Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extracting view to function or struct?

Tags:

swift

swiftui

I am new to learning Swift and SwiftUI. I have a struct with a View which contains a VStack. Within the VStack, I'm calling four functions which return a View.

var body: some View {
    VStack {
        
        self.renderLogo()
        self.renderUserNameTextField()
        self.renderPasswordSecureField()
        self.renderLoginButton()
        
    }
}

func renderLoginButton() -> some View {
    return Button(action: {print("Button clicked")}){
        Text("LOGIN").font(.headline).foregroundColor(.white).padding().frame(width: 220, height: 60).background(Color(red: 0, green: 70/255, blue: 128/255)).cornerRadius(15.0)
    }.padding()
}[...]

I've just read that it's more common that views are extracted to a struct like that:

struct UsernameTextField : View {
@Binding var username: String
var body: some View {
return TextField("Username", text: $username)
            .padding()
            .background(lightGreyColor)
            .cornerRadius(5.0)
            .padding(.bottom, 20)
    }
}

Why is that? What advantages are there using structs instead of a normal function?

like image 890
PeterPan Avatar asked Sep 15 '25 21:09

PeterPan


2 Answers

This raises an interesting point. You don't want to make large views by having standard controls with lots of modifiers, but you want some kind of reuse, either by using a function to return the view or a custom view.

When I face these problems in SwiftUI I look to see what I am extracting. In your case it looks like the controls are standard, but they have different styling applied to them. The clue is in the function names that all have render.

In this case, rather than using a function or a custom View, I would write a custom modifier that applies a set of common styles to a control. A couple of examples from your code:

First, create ViewModifiers

struct InputTextFieldModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(Color.gray)
            .cornerRadius(5.0)
            .padding(.bottom, 20)
    }
}


struct ButtonTextModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .font(.headline)
            .foregroundColor(.white)
            .padding()
            .frame(width: 220, height: 60)
            .background(Color(red: 0, green: 70/255, blue: 128/255))
            .cornerRadius(15.0)
    }
}

These are quite simple to write. You just apply the modifiers you want to the content parameter.

And to make them easier to use, you can write extension to View

extension View {
    func inputTextFieldStyle() -> some View {
        modifier(InputTextFieldModifier())
    }

    func buttonTextStyle() -> some View {
        modifier(ButtonTextModifier())
    }
}

And this makes the call site look something like this:

var body: some View {
    VStack {
        
        ...
        TextField(...)
            .inputTextFieldStyle()
        ...
        Button(action: {print("Button clicked")}){
            Text("LOGIN")
                .buttonTextStyle()
        }.padding()
        
    }
}

If you want to be able to configure the modifier, that is easy as well. Say you want to specify the background colour of your common text fields, you can re-write the modifier to take this as a parameter:

struct InputTextFieldModifier: ViewModifier {
    let backgroundColor: Color

    func body(content: Content) -> some View {
        content
            .padding()
            .background(backgroundColor)
            .cornerRadius(5.0)
            .padding(.bottom, 20)
    }
}

And update your convenience function to take this as a parameter:

extension View {
    func inputTextFieldStyle(backgroundColor: Color) -> some View {
        modifier(InputTextFieldModifier(backgroundColor: backgroundColor))
    }
}

And at the call site:

TextField("Username", text: $username)
    .inputTextFieldStyle(backgroundColor: Color.gray)

And custom modifiers are reusable.

like image 75
Abizern Avatar answered Sep 18 '25 21:09

Abizern


I would separate a view in a different struct if that view will have at least a @Binding with the main view. Otherwise, I would stick to functions. But...

It's up to you if you want to do it in separate structs or in a function but take into account that if you want to reuse a view, is easier if you have it declared as a separate entity.

Another purpose of this is to avoid having enormous views in SwiftUI, separating the responsibilities into smaller views makes the code easier to read.

like image 27
Sergio Avatar answered Sep 18 '25 20:09

Sergio