skip to Main Content

Goal: Stroke text in watchOS with SwiftUI

With a storyboard project, Create new storyboard project

WKInterfaceLabel offers support for NSAttributedString via setAttributedText which offers simple and easy support to stroke text.Storyboard

However, with a new project using SwiftUI,New Project SwiftUI WKInterfaceLabel isn’t available outside of the nonexistent storyboard:

Do not subclass or create instances of this class yourself. Instead, define outlets in your interface controller class and connect them to the corresponding objects in your storyboard file.

open class WKInterfaceLabel : WKInterfaceObject {

    
    open func setText(_ text: String?)

    open func setTextColor(_ color: UIColor?)

    
    open func setAttributedText(_ attributedText: NSAttributedString?)
}

‘init()’ is unavailable

So, how can we use NSAttributedString in watchOS with SwiftUI?


Side note:

Down the road, I expect the new AttributedString to reliably replace NSAttributedString, but as of watchOS 8 and iOS 15, AttributedString is broken and incomplete. This forces me to look for a solution to use NSAttributedString in watchOS with SwiftUI.

2

Answers


  1. In latest swiftUI you can use AttributedString instead of NSAttributedString.

    Details how to use it you can find in apple documentation: https://developer.apple.com/documentation/foundation/attributedstring


    But still you can use NSAttributedString in swiftUI app in case of wrapping original NSTextField(macos) / UITextField(IOS) controls

    Sample for macos wrapper (it’s must be easy to rewrite) :

    import SwiftUI
    import Cocoa
    
    @available(OSX 11.0, *)
    public struct AttributedText: NSViewRepresentable {
        private let text: NSAttributedString
        private let selectable: Bool
        
        public init(attributedString: NSAttributedString, selectable: Bool = true) {
            self.text = attributedString
            self.selectable = selectable
        }
        
        public func makeNSView(context: Context) -> NSTextField {
            let textField = NSTextField(labelWithAttributedString: text)
            textField.preferredMaxLayoutWidth = textField.frame.width
            textField.allowsEditingTextAttributes = true // Fix of clear of styles on click
            
            textField.isSelectable = selectable
            
            return textField
        }
        
        public func updateNSView(_ textField: NSTextField, context: Context) {
            textField.attributedStringValue = text
        }
    }
    

    But still in most cases better to use native way for SwiftUI — AttributedString.

    Login or Signup to reply.
  2. The easiest way is to draw an image:

      func renderImage(_ attributedString: NSAttributedString, for width: CGFloat) -> UIImage? {
        let boundingRect = attributedString.boundingRect(with: CGSize(width: width, height: .infinity), options: [.usesLineFragmentOrigin], context: nil)
        UIGraphicsBeginImageContextWithOptions(boundingRect.size, false, 0)
        attributedString.draw(in: CGRect(origin: .zero, size: boundingRect.size))
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        return image
    

    Then use it with Image()

            if let image = renderImage(longText, for: geometry.size.width) {
              Image(uiImage: image)
            }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search