In SwiftUI recurring Stacks simplifying

40 Views Asked by At

For MacOS I use the following code many times to create forms. I really like to create a modifier or extension to make it more simplified.

HStack{
    VStack (alignment: .leading, spacing: 5) {
         Text("Account Number").font(.headline)
         TextField("", text: $selectedBankAccount.accountNumber, prompt: Text("AccountNumber"))
    }
}
.frame(width: 450 )

I would love to see something like:

struct FormStackModifier: ViewModifier {
    var width : CGFloat
    func body(content: Content) -> some View {
        HStack {
            VStack(alignment: .leading, spacing: 5) {
                content
            }
        }
        .frame(width: width)
    }
}

// and use it like
ModifiedContent( 
   content: VStack { Text("Account Name").font(.headline)
                            TextField("", text: $selectedBankAccount.accountName, prompt: Text("AccountName"))},
   modifier: FormStackModifier(width: 450)
)

But I need stil the extra VStack in content.

Is there a to solve this and a way to make this neat?

1

There are 1 best solutions below

0
Sweeper On BEST ANSWER

Group is a view that does nothing but groups views together, allowing you to use multiple views as a single view. Unlike VStack, it doesn't enforce a particular layout on the views or do anything like that.

You can replace the VStack with Group.

ModifiedContent( 
   content: 
       Group { 
           Text("Account Name").font(.headline)
           TextField("", text: $selectedBankAccount.accountName, prompt: Text("AccountName"))
       },
   modifier: FormStackModifier(width: 450)
)

Using ModifiedContent like this is rather inconvenient. I recommend adding an extension on View that applies the FormStackModifier.

extension View {
    func formStack(width: CGFloat) -> some View {
        modifier(FormStackModifier(width: width))
    }
}

// usage:

Group { 
    Text("Account Name").font(.headline)
    TextField("", text: $selectedBankAccount.accountName, prompt: Text("AccountName"))
}
.formStack(width: 450)

Alternatively, instead of a ViewModifier, you can make your own View called FormStack:

struct FormStack<Content: View>: View {
    let width : CGFloat
    let content: Content
    
    init(width: CGFloat, @ViewBuilder content: () -> Content) {
        self.width = width
        self.content = content()
    }
    
    var body: some View {
        HStack {
            VStack(alignment: .leading, spacing: 5) {
                content
            }
        }
        .frame(width: width)
    }
}

// usage:

FormStack(width: 450) { 
    Text("Account Name").font(.headline)
    TextField("", text: $selectedBankAccount.accountName, prompt: Text("AccountName"))
}