1

Trying to following this discussion, I implemented the suggestion of Yurii Kotov:

struct ContentView: View {

@State private var index = 0

@ViewBuilder
 var body: some View {
   if index == 0 {
       MainView()
   } else {
       LoginView()
   }
}

It works fine. But if I try to use a switch statement instead:

    switch  index {
        case 0: MainView()
        case 1: LoginView()
        default:
            print("# error in switch")
        }

nothing happens. There is no mistake alert, but also no result at all. Could somebody help?

Roman
  • 759
  • 2
  • 9
  • 23
  • 2
    Your switch statement does not do the same thing as the if. If you write an equivalent switch statement, then it should work. – Sweeper Jan 22 '20 at 07:21
  • OK, Sweeper, but what is wrong with it? – Roman Jan 22 '20 at 07:23
  • Think about the case when `index` is 2. What will the if do? What will the switch do? – Sweeper Jan 22 '20 at 07:23
  • It s an interesting discussion, but I have defined index as 0, so it should be not interesting for switch what happens by 1000. It should match the 0 case. IMHO. – Roman Jan 22 '20 at 07:26
  • Whenever a `@State` changes, your view updates, which means that everything in `body` is evaluated again. – Sweeper Jan 22 '20 at 07:29
  • My mistake, I didn't think your code would compile in the first place... – Sweeper Jan 22 '20 at 07:42
  • As said, I got the positive result, using the if-statement. But what to do, if you have more cases and more views in play? – Roman Jan 22 '20 at 07:46
  • After reading the other answers in your linked post. It appears that the `ViewBuilder` function builder turns if statements into `_ConditionalContent`s, which is strictly a "either this or that" thing. You would have to nest if statements to achieve more cases. But if your true intention is to have more cases, you better use `AnyView`. See Mike Glukhov's answer. – Sweeper Jan 22 '20 at 07:58
  • Thank a lot for your time. I will check other options. – Roman Jan 22 '20 at 08:52

2 Answers2

8

I had an issue with the default case where I wanted a "break" type situation for the switch to be exhaustive. SwiftUI requires some type of view, I found that

EmptyView() solved the issue.

also noted here EmptyView Discussion that you "have to return something"

struct FigureListMenuItems: View {
    var showContextType: ShowContextEnum
    @Binding var isFavoriteSeries: Bool?

    var body: some View {
    
        Menu {
            switch showContextType {
            case .series:
                Button(action: { toggleFavoriteSeries() }) {
                    isFavoriteSeries! ?
                    Label("Favorite Series?", systemImage: "star.fill")
                        .foregroundColor(.yellow)
                    :
                    Label("Favorite Series?", systemImage: "star")
                        .foregroundColor(.yellow)
                }
            default: // <-- can use EmptyView() in this default too if you want
                Button(action: {}) {
                    Text("No menu items")
                }
            }
        } label: {
            switch showContextType {
            case .series:
                isFavoriteSeries! ?
                Image(systemName: "star.fill")
                    .foregroundColor(.yellow)
                :
                Image(systemName: "star")
                    .foregroundColor(.yellow)
            default:
                EmptyView()
            }
            Label("Menu", systemImage: "ellipsis.circle")
        }
    }

    private func toggleFavoriteSeries() {
        isFavoriteSeries?.toggle()
    }
}

Enum for switch

enum ShowContextEnum: Int {
    case series = 1
    case whatIsNew = 2
    case allFigures = 3
}
Jacksonsox
  • 1,114
  • 15
  • 25
1

As @Sweeper said: your if...else and switch...case statements are not equal. The main idea is: body is just a computed variable of a View protocol and it should return something of its' type. Sure, you can do it with switch...case statements. In your code snippet mistake is in default statement: body cannot return() -> Void, only some View. So your code should looks like this:

struct ViewWithSwitchStatements: View {

    @State var option = 0

    var body: some View {

        switch option {
        case 1:
            return AnyView(Text("Option 1"))
        case 2:
            return AnyView(Text("Option 2"))
        default:
            return AnyView(Text("Wrong option!"))
        }

    }
}

However you can't put it into VStack or something else, like if...else statements.

Hrabovskyi Oleksandr
  • 3,070
  • 2
  • 17
  • 36
  • 1
    Also, be careful and avoid returning Opaque types from ViewBuilders (don't wrap in AnyView) as it hides all the type information and compiler cannot optimise so well. – kdeo Dec 22 '21 at 20:39