Is it to automatically apply themes to SwiftUI controls as with Subclasses+Protocols+Appearance in UIKit?

131 Views Asked by At

After working with UIKit for quite a while I am now making my first steps in SwiftUI. In UIKit it is quite easy apply different themes to an app (e.g. as described here):

  1. Create different subclasses for controls which should have different styles. E.g. class HeadlineLabel: UILabel {} and class BodyLabel: UILabel {}
  2. Define a Theme protocol which controls the different styling options like different fonts + text sizes for the different label types.
  3. Create a ThemeManager which applies the styles of the current Theme to the controls using the appearance proxy. E.g. HeadlineLabel.appearance().textColor = theme.headlineColor

The big advantage of this approach is, that in the rest of the code one has not think about theming at all. All one has to do, is to use the correct control subclasses (HeadlineLabel, etc.) within the XIB or Storyboard files.

Is this also possible when working with SwiftUI?


Of course it is no problem to create and use a theme protocol when working with SwiftUI. However, since a SwiftUI view is a struct, one cannot create custom sub classes like struct HeadlineText: Text or struct BodyText: Text, simply because structs do not have inheritance...

So the only solution I found, is to apply the theming options to all controls manuall:

...
Text("Headline 1")
    .foregroundColor(ThemeManager.currentTheme.headlineColor)
Text("Body 1")
    .foregroundColor(ThemeManager.currentTheme.bodyColor)

Text("Headline 2")
    .foregroundColor(ThemeManager.currentTheme.headlineColor)
Text("Body 2")
    .foregroundColor(ThemeManager.currentTheme.bodyColor)

// Not possible
HeadlineText("Headline 3)
BodyText("Body 3")

Is this correct? Or is possible to apply styles to SwiftUI controls of a common type automatically?

1

There are 1 best solutions below

2
Timmy On BEST ANSWER

It's possible. You don't subclass Text, you subclass View to create a custom container:

struct HeadlineText: View {
    var text: String
    init(_ text: String) {
        self.text = text
    }
    var body: some View {
        Text(text)
        //customize
    }
}

HealineText("Hello")

Or you can create a custom ViewMofifier:

struct HeadlineText: ViewModifier {
    func body(content: Content) -> some View {
        content
        //customize
    }
}

extension View {//or extend Text
    func headlineText() -> some View {
        return self
            .modifier(HeadlineText)
    }
}

Text("")
    .headlineText()