Array mit mehreren Ansichtstypen in Swift / SwiftUI
Ich möchte eine Ansicht erstellen, in der Benutzer ihr bevorzugtes Hintergrundbild auswählen können. Dazu gehört ein Rechteck mit einer Vordergrundfarbe oder ein Bild. Bisher muss das funktionieren, indem ich es erstelle
Struktur:
struct BackgroundImage : Identifiable{
var background : AnyView
let id = UUID()
}
Ich füge sie einem Array wie diesem hinzu
ViewModel:
class VM : ObservableObject{
@Published var imageViews : Array<BackgroundImage> = Array<BackgroundImage>()
init(){
imageViews.append(BackgroundImage(background: AnyView(Rectangle().foregroundColor(Color.green))))
imageViews.append(BackgroundImage(background: AnyView(Rectangle().foregroundColor(Color.yellow))))
imageViews.append(BackgroundImage(background: AnyView(Image("Testimage"))))
}
Dadurch kann ich ein Array von BackgroundImages wie dieses durchlaufen
Aussicht:
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())]) {
ForEach(VM.imageViews, id: \.self.id) { view in
ZStack{
view.background
//.resizable()
//.aspectRatio(contentMode: .fill)
.frame(width: g.size.width/2.25, height: g.size.height/8)
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
}
}
}
Ich kann jedoch nicht hinzufügen
.resizable()
.aspectRatio(contentMode: .fill)
für die Bilder, da AnyView dies nicht zulässt.
Gibt es einen besseren Weg, dies zu erreichen? Sollte ich stattdessen nur zwei separate Arrays für Shapes / Images haben? Oder gibt es eine alternative View-Struktur, die dafür besser geeignet wäre?
Vielen Dank!
Antworten
Wie @ DávidPásztor in den Kommentaren erwähnt hat, ist es ein schlechtes Design, Ansichten in Ihrem ViewModel zu speichern.
Wirklich müssen Sie nur eine Farbe und einen Bildnamen speichern. Lassen Sie den View Building Code die tatsächlichen Ansichten erstellen.
Hier ist eine mögliche Implementierung.
struct BackgroundItem: Identifiable {
private let uicolor: UIColor?
private let imageName: String?
let id = UUID()
var isImage: Bool { imageName != nil }
var color: UIColor { uicolor ?? .white }
var name: String { imageName ?? "" }
init(name: String? = nil, color: UIColor? = nil) {
imageName = name
uicolor = color
}
}
class VM : ObservableObject{
@Published var imageItems: [BackgroundItem] = []
init() {
imageItems = [.init(color: .green),
.init(color: .blue),
.init(name: "TestImage")]
}
}
struct ContentView: View {
@ObservedObject var vm = VM()
var body: some View {
GeometryReader { g in
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())]) {
ForEach(vm.imageItems) { item in
ZStack{
if item.isImage {
Image(item.name)
.resizable()
.aspectRatio(contentMode: .fill)
} else {
Color(item.color)
}
}
.frame(width: g.size.width/2.25, height: g.size.height/8)
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)
}
}
}
}
}
Hinweis: Ich habe überlegt, ein enumzu verwenden, um die Unterscheidung zwischen imageName und Farbe zu speichern, aber es war am einfachsten, nur Optionen zu verwenden und das zu speichern, das ich benötigte. Mit der Schnittstelle, die für den Code zum Erstellen von Ansichten verfügbar ist, können Sie die Implementierung problemlos so ändern, wie Sie die Informationen speichern möchten.