59

I have a UIViewRepresentable with a UITextView and I want to set the font on the UITextView using the current Font from the SwiftUI environment. I just need a simple init like:

UIFont(_ swiftUIFont: Font)

But I can't find any such thing. And the Font type doesn't seem to have any information I can use to try to implement one. Anyone know of a way to convert between the two font representations?

4
  • unfortunately, not yet (at least, I didn't find any way), fill the radar ... Commented Feb 4, 2020 at 10:27
  • Keep in mind: SwiftUI.Font is a View, where UITextView.font is a UIFont Commented Feb 7, 2020 at 17:39
  • 12
    @ninestones SwiftUI.Font is just a plain Struct conforms to Equatable and Hashable, not a View: developer.apple.com/documentation/swiftui/font Commented Jun 27, 2020 at 6:23
  • 2
    github.com/LeoNatan/LNSwiftUIUtils I've created a small library, which includes a Font -> UIFont conversion method. It supports all modifiers but the leading() one. Commented Oct 22, 2023 at 17:49

4 Answers 4

19

A bit of a hack but works (doing the other direction is left as an exercise to the reader).

extension UIFont {
    class func preferredFont(from font: Font) -> UIFont {
        let uiFont: UIFont
        
        switch font {
        case .largeTitle:
            uiFont = UIFont.preferredFont(forTextStyle: .largeTitle)
        case .title:
            uiFont = UIFont.preferredFont(forTextStyle: .title1)
        case .title2:
            uiFont = UIFont.preferredFont(forTextStyle: .title2)
        case .title3:
            uiFont = UIFont.preferredFont(forTextStyle: .title3)
        case .headline:
            uiFont = UIFont.preferredFont(forTextStyle: .headline)
        case .subheadline:
            uiFont = UIFont.preferredFont(forTextStyle: .subheadline)
        case .callout:
            uiFont = UIFont.preferredFont(forTextStyle: .callout)
        case .caption:
            uiFont = UIFont.preferredFont(forTextStyle: .caption1)
        case .caption2:
            uiFont = UIFont.preferredFont(forTextStyle: .caption2)
        case .footnote:
            uiFont = UIFont.preferredFont(forTextStyle: .footnote)
        case .body:
            fallthrough
        default:
            uiFont = UIFont.preferredFont(forTextStyle: .body)
        }
        
        return uiFont
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

This does not support custom font designs (rounded, etc), custom sizes/weights or custom fonts. I suggest people create feedbacks for Apple to support retrieving a UIFont or NSFont instance from Font directly.
@Luke_Howard perhaps change the method name to preferredFont(from font:Font) to reflect that this only returns default system font.
I don't think that this answers the OP's question. There's the assumption of using the default font. What if the OP loaded a custom font?
16

Here is a reformatting of Luke Howard's solution to reduce the amount of text

extension UIFont {
  class func preferredFont(from font: Font) -> UIFont {
      let style: UIFont.TextStyle
      switch font {
        case .largeTitle:  style = .largeTitle
        case .title:       style = .title1
        case .title2:      style = .title2
        case .title3:      style = .title3
        case .headline:    style = .headline
        case .subheadline: style = .subheadline
        case .callout:     style = .callout
        case .caption:     style = .caption1
        case .caption2:    style = .caption2
        case .footnote:    style = .footnote
        case .body: fallthrough
        default:           style = .body
     }
     return  UIFont.preferredFont(forTextStyle: style)
   }
}

From Swift 5.9, you can use the switch statement to return a value and have an even less cluttered look:

extension UIFont {
  class func preferredFont(from font: Font) -> UIFont {
      let style: UIFont.TextStyle =
      switch font {
        case .largeTitle:   .largeTitle
        case .title:        .title1
        case .title2:       .title2
        case .title3:       .title3
        case .headline:     .headline
        case .subheadline:  .subheadline
        case .callout:      .callout
        case .caption:      .caption1
        case .caption2:     .caption2
        case .footnote:     .footnote
        default: /*.body */ .body 
      }
      return  UIFont.preferredFont(forTextStyle: style)
    }
 }

3 Comments

Nice piece of re-factoring.
Note that this works equally well for NSFont.
I have added a version that works with the new syntax feature in Swift 5.9, of allowing switch to return a value
5

It's a good question and although as mentioned in the other answers, you can't convert SwiftUI.Font directly to UIFont, but you can easily work around it by using Foundation's AttributedString. Here's how I did it for UILabel:

func updateUIView(_ label: UILabel, context: Context) {
    var container = AttributeContainer()
    container.font = context.environment.font

    let attributedString = AttributedString(text, attributes: container)
    label.attributedText = NSAttributedString(attributedString)
}

4 Comments

(A) I'm not following how this addresses the question of finding a UIFont that corresponds to a given SwiftUI.Font. (B) It sounds like you provided a workaround, but I'm not seeing the connection yet. Could you flesh out your explanation please?
Hey @DavidJ. you're right. I tried to give an answer to the question, which was about using it in a UIViewRepresentable with UITextView. You could perhaps (ab)use the AttributeContainer to convert between the two by using the swiftUI and uiKit atributes on that container: developer.apple.com/documentation/foundation/attributescopes
This doesn't work. The font in attributes of NSAttributedString remainsFont.
@MarK perhaps you could first convert it to NSAttributedString and read it from there? It should only support UIFont.
3

No you can't, you have to use the params you passed to the Font to create UIFont

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.