Mark Meretzky
Mark Meretzky

Reputation: 99

if statement inside of ForEach in iOS SwiftUI

I have an "if" inside of a ForEach in iOS SwiftUI (Xcode version 11.6) and it works correctly.

struct ContentView: View {
    var body: some View {
        List {
            ForEach(0 ..< 10) {(i: Int) in
                if i % 2 == 0 {
                    Text(String(i))
                }
            }
        }
    }
}

Now I would like to do the same thing, but with an array of Strings instead of a half-open range of Ints.

struct ContentView: View {
    let states: [String] = [
        "Alabama",
        "Alaska",
        "Arizona",
        "Arkansas",
        "California"
    ];
    
    var body: some View {
        List {
            ForEach(states, id: \.self) {(state: String) in
                if state.hasPrefix("Al") {
                    Text(state)
                }
            }
        }
    }
}

The error message I get from this ForEach is

Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols

What has gone wrong? I'm puzzled because Apple's SwiftUI tutorial has an "if" inside of a ForEach in Section 3, Step 1 of https://developer.apple.com/tutorials/swiftui/handling-user-input
Thank you in advance.

Upvotes: 1

Views: 957

Answers (2)

cbjeukendrup
cbjeukendrup

Reputation: 3446

A possible method is using Group:

struct ContentView: View {
    let states: [String] = [
        "Alabama",
        "Alaska",
        "Arizona",
        "Arkansas",
        "California"
    ];
    
    var body: some View {
        List {
            ForEach(states, id: \.self) {(state: String) in
                Group {
                    if state.hasPrefix("Al") {
                        Text(state)
                    }
                }
            }
        }
    }
}

Some Views are a bit more flexible about what you put inside them than others are. For example, in an HStack or VStack, you can even put multiple (sub)Views, while in ForEach, you can't. Group has a lot of flexibility, but doesn't affect the layout, so often you can use it just to wrap complex views in it.

Upvotes: 1

pawello2222
pawello2222

Reputation: 54611

You may try the following:

struct ContentView: View {
    ...
    
    var body: some View {
        List {
            ForEach(states, id: \.self) { state in
                self.stateView(state: state)
            }
        }
    }
    
    @ViewBuilder
    func stateView(state: String) -> some View {
        if state.hasPrefix("Al") {
            Text(state)
        }
    }
}

This way your code may be more readable as well.

Upvotes: 3

Related Questions