14

So far I am able to write custom modifiers for Views; however, when trying to keep my code DRY, I am trying to add a custom modifier for TextFields. The view modifiers work great with something like so:

struct sampleModifier : ViewModifier {
    var height: CGFloat? = 100
    func body(content: Content) -> some View {
        content
            .frame(height: height)
            .background(Color.white)
            .border(Color.gray, width: 0.5)
            .shadow(color: Color.black, radius: 15, x: 0, y: 10)
    }
}

But when I try to use modifiers like font and so, it shows a lot of errors. I do understand that they might need to be more specific and instead conform to the TextFieldStyleModifier, but I do not get how to make it work. I have tried to do it this way without success:

struct TitleModifier : TextFieldStyleModifier {
    func body(content: TextFieldStyle) -> some View {
        content
            .font(.custom("Open Sans", size: 18))
            .color(Color.green)
            
    }
}

Which obviously fails and shows the following error: Error

If I click on the Fix suggestion, it adds this to my code

TextFieldStyleModifier<<#Style: TextFieldStyle#>>

Which I do not know how to use.

Any suggestions?

1

7 Answers 7

24

Right now it is possible to add .font() and .foregroundColor() modifiers to the content inside ViewModifier. However, if you want to add some custom modifiers that can be applied only to a specific view, for example .strikethrough() modifier for Text View, you can add these modifiers into the extension.

struct TitleModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .font(.custom("Open Sans", size: 18))
            .foregroundColor(.green)
    }
}

extension Text {
    func customText() -> some View {
        self.strikethrough().bold().italic().lineLimit(4)
            .modifier(TitleModifier())
    }
}

Usage Text("Hello").customText().

Sign up to request clarification or add additional context in comments.

4 Comments

I have tried this but getting an error: Type '() -> some View' cannot conform to 'View'
Asif Syeed, Can you please write more details, code sample?
Hey! Later I have implemented by creating an extension of View. Here is the snippet: codeshare.io/K8JEVE
Asif Syeed thank you for the code snippet. If I change extension of View back to Text, your sample doesn't show an error. You are correctly applying the modifier directly to Text and returning some View, so it works. Maybe there was some other modifier before custom one, or you tried to return View not some View, or ViewBuilder was required. Anyway I am glad you've found a suitable solution for your case, if you still have some questions, let me know :)
7

TextFields are also views, so you create the modifier in the same fashion:

struct TitleModifier : ViewModifier {
    func body(content: Content) -> some View {
        content
            .font(.custom("Open Sans", size: 18))
            .foregroundColor(Color.green)

    }
}

Also note that there is no modifier .color(). It is .foregroundColor().

When you want to apply it to a FormField, you just do:

TextField("", text: $field1).modifier(TitleModifier())

6 Comments

There is one problem for this ViewModifier. It's not only for Text. What if I only want to apply this modifier to Text?
Hi @Bagusflyer, sorry I do not get your point. If you don't want to apply the modifier to non-text views, simply don't apply it. What am I missing?
@Bagusflyer, did you find a solution? I mean how to create custom modifier for Text type
@kontiki think about the case where you want to apply a modifier that isn't implemented on all Views. What if, for example, shadow didn't work on all views, but did on TextViews. The compiler would not accept the above solution.
@kontiki Concatenation will not work: Text("1").modifier(TitleModifier()) + Text("2") + Text("3")
|
5

You can create a custom TextFieldStyle, this will apply only to TextField, not to other views on the view container

struct CustomTextFieldStyle: TextFieldStyle {
    func _body(configuration: TextField<Self._Label>) -> some View {
        configuration
            .font(.custom("Open Sans", size: 18))
            .foregroundColor(Color.green)
    }
}

Usage:

Group {
    Text("not applied here")
    TextField("applied here", text: $presenter.name)
}
.textFieldStyle(CustomTextFieldStyle())

Comments

5

As I know, use a Text extension is much better. For Text, some modifiers are very special, the return body type is not View, but Text. You can find many here.

 extension Text {
    func font(size: CGFloat) -> Text { // don't return `some View`
        self.font(Font.custom("Open Sans", size: size))
    }
}

This means we defined the type of Content and Body for ViewModifier, not sure there is a better way.

Comments

1

In Xcode 11 beta 4, the color(_:) modifier is deprecated. So use the foregroundColor(_:) method instead.

Comments

0

Working with custom fonts:

  • install font in your mac and show font details in font book, use the postscript name all time

postscript name

  • add fonts to app

adding custom fonts

adding custom fonts

add enum, in a new file:

import SwiftUI

enum CustomFonts: String {
        case bold = "Family-Bold", medium = "Family-Medium"
}

add extension, in a new file:

import SwiftUI

extension Font {
    static func custom(_ font: CustomFonts, size: CGFloat) -> SwiftUI.Font {
        SwiftUI.Font.custom(font.rawValue, size: size)
    }
}

use:

Text("Hello World")
  .font(.custom(.bold, size: 24))

Comments

0
// MARK: - Text Styles, iOS 15+:

import SwiftUI

struct TextStyle {
    var fontFamily: String = "Roboto Flex"
    let fontSize: CGFloat
    let fontWeight: Font.Weight
    let lineHeight: CGFloat
    var lineSpacing: CGFloat { lineHeight - fontSize }
}

// MARK: - Create our styles:

extension TextStyle {
    static let heading1 = TextStyle(
        fontSize: 36,
        fontWeight: .bold,
        lineHeight: 44
    )
    static let heading2 = TextStyle(
        fontSize: 30,
        fontWeight: .medium,
        lineHeight: 32
    )
}

// MARK: - Create ViewModifier:

fileprivate struct TextStyleModifier: ViewModifier {
    let textStyle: TextStyle

    func body(content: Content) -> some View {
        content
            .font(
                Font.custom(
                    textStyle.fontFamily,
                    size: textStyle.fontSize
                )
                .weight(textStyle.fontWeight)
            )
            .lineSpacing(textStyle.lineSpacing)
    }
}

// MARK: - Create View extension:

extension View {
    func setStyle(_ textStyle: TextStyle) -> some View {
        modifier(TextStyleModifier(textStyle: textStyle))
    }
}

// MARK: - Usage:

struct TestFont: View {
    var body: some View {
        VStack(alignment: .leading) {
            Group {
                Text("The quick brown fox jumps over the lazy dog and cat")
                    .setStyle(.heading1)
                Text("The quick brown fox jumps over the lazy dog and cat")
                    .setStyle(.heading2)
            }
            .padding(10)
        }
    }
}

// MARK: - Preview:

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        TestFont()
    }
}

Result:

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.