Invincible_Pain
Invincible_Pain

Reputation: 571

In SwiftUI, how can we reuse a set of modifiers or make them into a kind of component to not repeatedly call them?

In SwiftUI we may have many pieces of code like this:

Text("Hello World")
  .padding(EdgeInsets(top: 3, leading: 3, bottom: 3, trailing: 3))
  .background(Color.blue)
  .cornerRadius(5)

However, as you or any other very experienced and professional developers may aware, we want to stay DRY and absolutely don't want to re-write the modifiers:

.padding(EdgeInsets(top: 3, leading: 3, bottom: 3, trailing: 3))
.background(Color.blue)
.cornerRadius(5)

for every Text(...) or Button or any other SwiftUI component.

It would be better to wrap the:

.padding(EdgeInsets(top: 3, leading: 3, bottom: 3, trailing: 3))
.background(Color.blue)
.cornerRadius(5)

in some kind of method or reusable component. I know how to do it in UIKit, but the question is how do we do that in SwiftUI's declarative way to build GUIs?

Upvotes: 17

Views: 3903

Answers (4)

leo
leo

Reputation: 115

you can also use Group:

Before:

GoogleAuthUI()
    .padding(.horizontal)
    .disabled(isLoading)
    .opacity(isLoading ? 0.5 : 1)

AppleAuthUI()
    .padding(.horizontal)
    .disabled(isLoading)
    .opacity(isLoading ? 0.5 : 1)

After:

Group {
    GoogleAuthUI()
    AppleAuthUI()
}
.padding(.horizontal)
.disabled(isLoading)
.opacity(isLoading ? 0.5 : 1)

Upvotes: 0

Asperi
Asperi

Reputation: 257719

Here is how it is usually done with custom ViewModifier, plus depicts example of how it could be configured via parameters:

struct MyTextModifier: ViewModifier {
    let corner: CGFloat
    func body(content: Content) -> some View {
        content
            .padding(EdgeInsets(top: 3, leading: 3, bottom: 3, trailing: 3))
            .background(Color.blue)
            .cornerRadius(corner)
    }
}

extension View {
    func configured(with radius: CGFloat = 5) -> some View {
        self.modifier(MyTextModifier(corner: radius))
    }
}

struct MyTextModifier_Previews: PreviewProvider {
    static var previews: some View {
        Text("Hello World")
            .configured()
    }
}

Tested with Xcode 11.2, iOS 13.2

Upvotes: 19

dot3
dot3

Reputation: 1216

I would create every component as a view, instead of an extension. That way you can always preview the component and have a central repository of 'reusable' components:

struct customText : View {

 let text: String

 var body: some View {

  Text(string)
   .padding(EdgeInsets(top: 3, leading: 3, bottom: 3, trailing: 3))
   .background(Color.blue)
   .cornerRadius(5)
  }
}

Append a preview to the above and you can always view exactly what your text will look like in the preview canvas, making future edits easier.

And to use:

customText(text: "Hello World")

You can still add modifiers to its use. You can then have a single source of views which can be used throughout your app (different text types, buttons, etc).

Upvotes: 5

LuLuGaGa
LuLuGaGa

Reputation: 14388

The easiest way to group modifiers for reuse is to declare them in an extension on View:

extension View {

    func customStyle() -> some View {
        self.padding(3)
            .background(Color.blue)
            .cornerRadius(5)
    }
}

then you can just simply use it like this:

Text("Hello World").customStyle()

This will only work when all of the modifiers can be applied to View. When you want to group modifiers specific to Image you would have to do it in an extension on Image etc.

Upvotes: 13

Related Questions