Custom Container View with ForEach(subviews:content:)

Hi, I’m trying to implement a custom horiztonal Picker using the newly introduced API on ForEach like the following:

struct ScopeBar<SelectionValue: Hashable, Content: View>: View {

    // MARK: Initializers

    @Binding
    private var selection: SelectionValue
    @ViewBuilder
    private var content: () -> Content

    public init(selection: Binding<SelectionValue>, @ViewBuilder content: @escaping () -> Content) {
        _selection = selection
        self.content = content
    }

    // MARK: Content

    var body: some View {
        ScrollView(.horizontal) {
            LazyHStack {
                ForEach(subviews: content()) { subview in
                    Button {
//                        selection = subview.id as! SelectionValue
                    } label: {
                        subview
                    }
                    .tag(subview.id)
                }
            }
        }
    }
}

The following implementation is what I’m trying to achieve for the custom container usage:

 struct MyOtherView: View {

    enum SomeScopes: String, CaseIterable, Hashable {
        case first
        case second
        case third
        case fourth
        case fifth
    }

    @State
    private var selection: SomeScopes = .first

    var body: some View {
        ScopeBar(selection: $selection) {
            ForEach(SomeScopes.allCases, id: \.rawValue) { scope in
                Text(scope.rawValue)
            }
        }
    }
}

I’m having some trouble figuring out two things:

  • How can I make my SelectionValue equal to the Subview.ID so that my selection behaves internally like a Picker would?
  • Say I wanted to add an Image(…) in addition to the Text(scope.rawValue), how would I do it in order for the Button { … } label: { … } to use both views, and not each separately…?

Have you tried creating a custom PickerStyle instead? this way you're able to customize the appearance and interaction without having to re-implement its functionality ?

You should review Creating custom container views sample code and Demystify SwiftUI containers WWDC session to learn more about container views. The learning from those resources should answer your questions and get you going.

Thank you for your reply! From what I read in the PickerStyle documentation, there is no public API to create a custom one… 🤔

Anyway, revisiting the mentioned sessions allowed me to derive the ID property from the containerValues rather than the Subview.ID, like so:

if let selection = subview.containerValues.tag(for: SelectionValue.self) {
…
}

All I have to do then is tag my custom view in place like so:

ForEach(SomeEnum.allCases, id: \.self) { enumCase in
    Text(enumCase.localizedString)
        .tag(enumCase)
}

I’m unsure wether this is the best way to go, but at least it technically solves my initial problem and works quite well.

Custom Container View with ForEach(subviews:content:)
 
 
Q