I have a view to show different styles according to parameters passed in.
There are more than 50 different styles, each style have a conflict layout. I want to show different styles according to the style passed in.
I have to write 50+ cases as below:
struct StyleDefaultView: View {
let style: Int
var body: some View {
VStack() {
Text("111")
}
}
}
struct Style1View: View {
let style: Int
var body: some View {
HStack() {
VStack() {
Text("111")
}
Text("222")
}
}
}
struct Style2View: View {
let style: Int
var body: some View {
VStack() {
Text("111")
HStack() {
Text("111")
Text("111")
}
}
Text("222")
}
}
struct Style3View: View {
let style: Int
var body: some View {
HStack() {
Text("111")
VStack() {
Text("111")
Text("111")
}
}
Text("222")
}
}
struct Style4View: View {
let style: Int
var body: some View {
HStack() {
Text("222")
VStack() {
Text("111")
Text("111")
}
Text("111")
}
}
}
// ... other 50+ style
struct ContentView: View {
let style: Int
var body: some View {
VStack() {
switch style {
case 1:
Style1View(style: style)
case 2:
Style2View(style: style)
case 3:
Style3View(style: style)
case 4:
Style4View(style: style)
// case .. other 50+ style
default:
StyleDefaultView(style: style)
}
}
}
}
Is there a way to simplify these swift case codes? Similar to the reflection in Java as code bellow
struct ContentView: View {
let style: Int
let viewMap = [
0 : "StyleDefaultView",
1 : "Style1View",
2 : "Style2View",
3 : "Style3View",
// ... other 50 views
]
var body: some View {
let viewName: String = viewMap[style] ?? "StyleDefaultView"
// initialization a view by a string
viewName(style : style)
}
}
EDIT:
this is just an example to get you thinking about looping over your different style views:
struct StyleDefaultView: View {
var body: some View {
VStack() {
Text("111")
}
}
}
struct Style1View: View {
var body: some View {
HStack() {
VStack() {
Text("111")
}
Text("222")
}
}
}
struct Style2View: View {
var body: some View {
VStack() {
Text("111")
HStack() {
Text("111")
Text("111")
}
}
Text("222")
}
}
struct Style3View: View {
var body: some View {
Text("1112")
Text("1112")
Text("1112")
}
}
struct Style4View: View {
var body: some View {
Text("1112")
Text("1")
}
}
class StyleViewModel {
let styles: [AnyView] = [
AnyView(Style1View()),
AnyView(Style2View()),
AnyView(Style3View()),
AnyView(Style4View())]
func selectedStyleView(_ ndx: Int) -> AnyView {
if ndx < styles.count {
return styles[ndx]
} else {
return AnyView(StyleDefaultView())
}
}
}
struct ContentView: View {
let styles = StyleViewModel()
let style: Int = 2
var body: some View {
styles.selectedStyleView(style)
}
}
I think a much better way is to use a protocol which each of these conform to. You then only need to store the types, meaning it's really light-weight since you aren't instantiating a bunch of views that you may not need to use.
Firstly, the ContentView so you can see mainly what's happening. It's an interactive example with a Slider so you can change the style to test it out.
struct ContentView: View {
private static let styleTypes: [StyledView.Type] = [
StyleDefaultView.self,
Style1View.self,
Style2View.self,
Style3View.self,
Style4View.self
]
@State private var currentStyle = 0
var body: some View {
VStack {
Text("Style: \(currentStyle)")
Slider(
value: Binding<Double>(
get: { Double(currentStyle) },
set: { currentStyle = Int($0) }
),
in: 0 ... 4,
step: 1
)
Spacer()
Self.styleTypes[currentStyle].init().buildView()
}
}
}
Result (with rest of code too):
Protocol:
protocol StyledView {
init()
func buildView() -> AnyView
}
extension StyledView where Self: View {
func buildView() -> AnyView {
AnyView(self)
}
}
Now all you need to do is conform each view to StyledView, no need for extra code in each of those views. Example for first view (but do this for all of them):
struct StyleDefaultView: View, StyledView {
var body: some View {
VStack() {
Text("111")
}
}
}
Side note: the VStack doesn't need those () after since you have a trailing closure.