Matriz incluindo vários tipos de visualização em Swift / SwiftUI
Quero criar uma visualização onde as pessoas possam escolher sua imagem de fundo preferida, que inclui um retângulo com uma cor de primeiro plano ou uma imagem. Até agora eu tenho que trabalhar criando este
Struct:
struct BackgroundImage : Identifiable{
var background : AnyView
let id = UUID()
}
Estou adicionando-os a uma matriz assim
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"))))
}
o que me permite percorrer uma série de BackgroundImages como
Visão:
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))
}
}
}
No entanto, não consigo adicionar
.resizable()
.aspectRatio(contentMode: .fill)
para as imagens, pois o AnyView não permite isso.
Existe uma maneira melhor de conseguir isso? Devo ter apenas duas matrizes separadas para formas / imagens em vez disso? Ou existe uma estrutura de visualização alternativa que seria mais adequada para isso?
Obrigado!
Respostas
Como @ DávidPásztor mencionou nos comentários, é um design ruim armazenar Views em seu ViewModel.
Na verdade, você só precisa armazenar uma cor e um nome de imagem. Deixe o código de construção de visualizações construir as visualizações reais.
Aqui está uma implementação possível.
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)
}
}
}
}
}
Nota: Pensei em usar um enum
para armazenar a distinção entre imageName e color, mas era mais simples usar apenas opcionais e armazenar o que eu precisava. Com a interface que está sendo exposta ao código de construção da visualização, você pode facilmente alterar a implementação da maneira que quiser para armazenar as informações.