Trying SwiftUI again after a few years ... I have some difficulties mapping my UI idea to SwiftUI, maybe thinking too much in an OOP way?
I want a list of different data models to be rendered by their corresponding views and displayed in a vertical grid on my main view.
I tried having data fed into models, that would then be mapped to construct component views depending on their type, and finally delivered to a main view, to display in a 2 columns list ...
After some trials and errors, I settled for models that generate their custom view which is then used by my main view ...
I am not entirely satisfied with my current architecture and would like to know if there is a more colloquial and better way to achieve the same thing with the latest version of SwiftUI.
Here is a simplified representation of my UI:
- the base model "abstract class"
import SwiftUI
class BaseModel: Identifiable, Equatable {
static func == (lhs: BaseModel, rhs: BaseModel) -> Bool {
lhs.id == rhs.id }
var id : String
var isSelected: Bool = false
init(_ id: String = "") {
self.id = id
}
func toggleSelection() {
self.isSelected = !self.isSelected
}
func view() -> any View {
return EmptyView()
}
}
- one type of model that displays text
import SwiftUI
class TextModel: BaseModel {
let text: String
override init(_ text: String) {
self.text = text
super.init(self.text.description)
}
override func view() -> any View {
return Button(action: self.toggleSelection) {
Text(self.text)
.frame( maxWidth: .infinity,maxHeight: .infinity)
.padding()
.foregroundStyle(self.isSelected ? .white : .blue)
.background(self.isSelected ? .blue : .white)
.frame(width:.infinity)
.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10).stroke(Color(.blue), lineWidth: 2)
)
}
}
}
- another type of model that displays images
import SwiftUI
class ImageModel: BaseModel {
let resource: String
override init(_ resource: String) {
self.resource = resource
super.init(self.resource.description)
}
override func view() -> any View {
return Button(action: self.toggleSelection) {
Image(self.resource)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(maxWidth: .infinity, alignment: .center)
.clipped()
.padding()
.foregroundStyle(self.isSelected ? .white : .blue)
.background(self.isSelected ? .blue : .white)
.frame(width:.infinity)
.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10).stroke(Color(.blue), lineWidth: 2)
)
}
}
}
- the main view
import SwiftUI
struct MainView: View {
var models:[BaseModel] = [
TextModel("ABCDEFG"),
ImageModel("my_image")
]
var body: some View {
VStack {
LazyVGrid(columns: [
GridItem(.flexible()), GridItem(.flexible())
]) {
ForEach(self.models) {model in
AnyView(model.view()
.frame(maxWidth: .infinity)).frame(maxWidth: .infinity)
}
}
}
}.padding()
}
}