79328029

Date: 2025-01-04 01:14:27
Score: 0.5
Natty:
Report link

A solution that uses built-in @ViewBuilder and doesn't convert views to AnyView.

The advantage compared to creating your own @resultBuilder is that you don't have to redefine other methods such as buildExpression, buildIf, etc.

The disadvantage is that it only works if you want to apply the same transformation to all subviews. In case of a divider, for example, you can't only add dividers between subviews, this solution will add an extra divider before the first subview. I couldn't find a way to retrieve the first element of a value pack..

Also note that this only works if we have more than one subview. If you try

BoxWithDividerView {
    Text("Hello")
}

you will get a compilation error.

import SwiftUI

struct BoxWithDividerView<each SubView: View>: View  {
    private let subviews: (repeat each SubView)
    init (@ViewBuilder content: @escaping () -> TupleView<(repeat each SubView)>) {
        subviews = content().value
    }
    
    var body: some View {
        VStack {
            // using TupleView directly instead of ForEach etc.
            TupleView(
                // TupleView takes a tuple instead of an array,
                // which works nicely here with "repeat";
                ( repeat
                    // need another TupleView inside to wrap two views;
                    // if you're only applying modifies to the subviews,
                    // and are not adding extra views, you don't need this
                    TupleView(
                        // add all our views and their modifications here
                        (Divider(), each subviews)
                    )
                )
            )
        }
    }
}

struct ViewThatUsesBox: View {
    let show_airplane: Bool
    
    var body: some View {
        BoxWithDividerView {
            Text("Hello")
            Image(systemName: "house")
            Text("Some more text")
            // example demonstrating that we're able to use "if" conditions
            // inside our builder
            if show_airplane {
                Image(systemName: "airplane")
            }
        }
    }
}

#Preview {
    ViewThatUsesBox(show_airplane: true)
}
Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @ViewBuilder
  • User mentioned (0): @resultBuilder
  • Low reputation (1):
Posted by: Alex