Tableau comprenant plusieurs types de vues dans Swift / SwiftUI
Je souhaite créer une vue dans laquelle les gens peuvent choisir leur image d'arrière-plan préférée, notamment un rectangle avec une couleur de premier plan ou une image. Jusqu'à présent, je dois cela fonctionner en créant ceci
Struct:
struct BackgroundImage : Identifiable{
var background : AnyView
let id = UUID()
}
Je les ajoute à un tableau comme ça
VoirModèle:
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"))))
}
ce qui me permet de parcourir un tableau de BackgroundImages comme ceci
Vue:
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))
}
}
}
Cependant, je ne peux pas ajouter
.resizable()
.aspectRatio(contentMode: .fill)
pour les images car AnyView ne le permet pas.
Y a-t-il une meilleure façon d'y parvenir? Dois-je avoir deux tableaux séparés pour les formes / images à la place? Ou existe-t-il une autre structure de vue qui conviendrait mieux à cela?
Merci!
Réponses
Comme @ DávidPásztor l'a mentionné dans les commentaires, stocker des vues dans votre ViewModel est une mauvaise conception.
En réalité, il vous suffit de stocker une couleur et un nom d'image. Laissez le code du bâtiment de vue construire les vues réelles.
Voici une implémentation possible.
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)
}
}
}
}
}
Remarque: j'ai pensé à utiliser un enum
pour stocker la distinction entre imageName et couleur, mais il était plus simple d'utiliser simplement des options et de stocker celle dont j'avais besoin. Avec l'interface qui est exposée au code du bâtiment de vue, vous pouvez facilement modifier l'implémentation comme vous souhaitez stocker les informations.