Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternative to switch statement in SwiftUI ViewBuilder block?

Tags:

swift

swiftui

⚠️ 23 June 2020 Edit: From Xcode 12, both switch and if let statements will be supported in the ViewBuilder!

I’ve been trying to replicate an app of mine using SwiftUI. It has a RootViewController which, depending on an enum value, shows a different child view controller. As in SwiftUI we use views instead of view controllers, my code looks like this:

struct RootView : View {    @State var containedView: ContainedView = .home     var body: some View {       // custom header goes here       switch containedView {          case .home: HomeView()          case .categories: CategoriesView()          ...       }    } } 

Unfortunately, I get a warning:

Closure containing control flow statement cannot be used with function builder ViewBuilder.

So, are there any alternatives to switch so I can replicate this behaviour?

like image 649
Nikolay Marinov Avatar asked Jun 24 '19 12:06

Nikolay Marinov


People also ask

How do I use a switch in SwiftUI?

You can create a toggle or switch by simply typing Toggle() . To configure toggle, we have to pass the parameter. The parameter name is isOn of type Binding<Bool> , which defines the state of the toggle (i.e., whether it's on or off). Inside the toggle body, we can define the text that'll appear beside the toggle view.

What does @ViewBuilder do in SwiftUI?

@ViewBuilder is a kind of result builder that's specifically designed to help create child views. Result builders create functions that build a result from a sequence of elements. SwiftUI uses this in its own native views, controls and components. It also uses this in the body to compose your views.


2 Answers

⚠️ 23 June 2020 Edit: From Xcode 12, both switch and if let statements will be supported in the ViewBuilder!

Thanks for the answers, guys. I’ve found a solution on Apple’s Dev Forums. It’s answered by Kiel Gillard. The solution is to extract the switch in a function as Lu_, Linus and Mo suggested, but we have to wrap the views in AnyView for it to work – like this:

struct RootView: View {   @State var containedViewType: ContainedViewType = .home    var body: some View {      VStack {        // custom header goes here        containedView()      }   }    func containedView() -> AnyView {      switch containedViewType {      case .home: return AnyView(HomeView())      case .categories: return AnyView(CategoriesView())      ...    } } 
like image 76
Nikolay Marinov Avatar answered Oct 24 '22 17:10

Nikolay Marinov


Update: SwiftUI 2 now includes support for switch statements in function builders, https://github.com/apple/swift/pull/30174


Adding to Nikolai's answer, which got the switch compiling but not working with transitions, here's a version of his example that does support transitions.

struct RootView: View {   @State var containedViewType: ContainedViewType = .home    var body: some View {      VStack {        // custom header goes here        containedView()      }   }    func containedView() -> some View {      switch containedViewType {      case .home: return AnyView(HomeView()).id("HomeView")      case .categories: return AnyView(CategoriesView()).id("CategoriesView")      ...    } } 

Note the id(...) that has been added to each AnyView. This allows SwiftUI to identify the view within it's view hierarchy allowing it to apply the transition animations correctly.

like image 29
opsb Avatar answered Oct 24 '22 17:10

opsb