Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have a dynamic List of Views using SwiftUI

Tags:

ios

swift

swiftui

I can do a static List like

List {    View1()    View2() } 

But how do i make a dynamic list of elements from an array? I tried the following but got error: Closure containing control flow statement cannot be used with function builder 'ViewBuilder'

    let elements: [Any] = [View1.self, View2.self]      List {        ForEach(0..<elements.count) { index in           if let _ = elements[index] as? View1 {              View1()           } else {              View2()           }     } } 

Is there any work around for this? What I am trying to accomplish is a List contaning dynamic set of elements that are not statically entered.

like image 334
Just a coder Avatar asked Jun 18 '19 09:06

Just a coder


People also ask

How do I create a list in SwiftUI?

Probably the simplest way to build a list is to create a new SwiftUI view and wrap the Hello World text in a List: struct StaticListView: View { var body: some View { List { Text("Hello, world!") } } } To add more items to the list, we can just add another line: List { Text("Hello, world!") Text("Hello, SwiftUI!") }

How do I make a list editable in SwiftUI?

If you have configured a SwiftUI list view to support deletion or editing of its items, you can allow the user to toggle editing mode for your list view by adding an EditButton somewhere. When that is run, you'll find you can tap the edit button to enable or disable editing mode for the items in the list.

Does SwiftUI have Viewdidload?

SwiftUI gives us equivalents to UIKit's viewDidAppear() and viewDidDisappear() in the form of onAppear() and onDisappear() . You can attach any code to these two events that you want, and SwiftUI will execute them when they occur.


2 Answers

Looks like the answer was related to wrapping my view inside of AnyView

struct ContentView : View {     var myTypes: [Any] = [View1.self, View2.self]     var body: some View {         List {             ForEach(0..<myTypes.count) { index in                 self.buildView(types: self.myTypes, index: index)             }         }     }          func buildView(types: [Any], index: Int) -> AnyView {         switch types[index].self {            case is View1.Type: return AnyView( View1() )            case is View2.Type: return AnyView( View2() )            default: return AnyView(EmptyView())         }     } } 

With this, i can now get view-data from a server and compose them. Also, they are only instanced when needed.

like image 166
Just a coder Avatar answered Sep 19 '22 10:09

Just a coder


if/let flow control statement cannot be used in a @ViewBuilder block.

Flow control statements inside those special blocks are translated to structs.

e.g.

if (someBool) {     View1() } else {     View2() } 

is translated to a ConditionalValue<View1, View2>.

Not all flow control statements are available inside those blocks, i.e. switch, but this may change in the future.

More about this in the function builder evolution proposal.


In your specific example you can rewrite the code as follows:

struct ContentView : View {      let elements: [Any] = [View1.self, View2.self]      var body: some View {         List {             ForEach(0..<elements.count) { index in                 if self.elements[index] is View1 {                     View1()                 } else {                     View2()                 }             }         }     } } 
like image 44
Matteo Pacini Avatar answered Sep 17 '22 10:09

Matteo Pacini