How do I make an attributed string using Swift?

前端 未结 28 2174
耶瑟儿~
耶瑟儿~ 2020-11-22 10:11

I am trying to make a simple Coffee Calculator. I need to display the amount of coffee in grams. The \"g\" symbol for grams needs to be attached to my UILabel that I am usin

28条回答
  •  误落风尘
    2020-11-22 10:33

    Details

    • Swift 5.2, Xcode 11.4 (11E146)

    Solution

    protocol AttributedStringComponent {
        var text: String { get }
        func getAttributes() -> [NSAttributedString.Key: Any]?
    }
    
    // MARK: String extensions
    
    extension String: AttributedStringComponent {
        var text: String { self }
        func getAttributes() -> [NSAttributedString.Key: Any]? { return nil }
    }
    
    extension String {
        func toAttributed(with attributes: [NSAttributedString.Key: Any]?) -> NSAttributedString {
            .init(string: self, attributes: attributes)
        }
    }
    
    // MARK: NSAttributedString extensions
    
    extension NSAttributedString: AttributedStringComponent {
        var text: String { string }
    
        func getAttributes() -> [Key: Any]? {
            if string.isEmpty { return nil }
            var range = NSRange(location: 0, length: string.count)
            return attributes(at: 0, effectiveRange: &range)
        }
    }
    
    extension NSAttributedString {
    
        convenience init?(from attributedStringComponents: [AttributedStringComponent],
                          defaultAttributes: [NSAttributedString.Key: Any],
                          joinedSeparator: String = " ") {
            switch attributedStringComponents.count {
            case 0: return nil
            default:
                var joinedString = ""
                typealias SttributedStringComponentDescriptor = ([NSAttributedString.Key: Any], NSRange)
                let sttributedStringComponents = attributedStringComponents.enumerated().flatMap { (index, component) -> [SttributedStringComponentDescriptor] in
                    var components = [SttributedStringComponentDescriptor]()
                    if index != 0 {
                        components.append((defaultAttributes,
                                           NSRange(location: joinedString.count, length: joinedSeparator.count)))
                        joinedString += joinedSeparator
                    }
                    components.append((component.getAttributes() ?? defaultAttributes,
                                       NSRange(location: joinedString.count, length: component.text.count)))
                    joinedString += component.text
                    return components
                }
    
                let attributedString = NSMutableAttributedString(string: joinedString)
                sttributedStringComponents.forEach { attributedString.addAttributes($0, range: $1) }
                self.init(attributedString: attributedString)
            }
        }
    }
    

    Usage

    let defaultAttributes = [
        .font: UIFont.systemFont(ofSize: 16, weight: .regular),
        .foregroundColor: UIColor.blue
    ] as [NSAttributedString.Key : Any]
    
    let marketingAttributes = [
        .font: UIFont.systemFont(ofSize: 20.0, weight: .bold),
        .foregroundColor: UIColor.black
    ] as [NSAttributedString.Key : Any]
    
    let attributedStringComponents = [
        "pay for",
        NSAttributedString(string: "one",
                           attributes: marketingAttributes),
        "and get",
        "three!\n".toAttributed(with: marketingAttributes),
        "Only today!".toAttributed(with: [
            .font: UIFont.systemFont(ofSize: 16.0, weight: .bold),
            .foregroundColor: UIColor.red
        ])
    ] as [AttributedStringComponent]
    let attributedText = NSAttributedString(from: attributedStringComponents, defaultAttributes: defaultAttributes)
    

    Full Example

    do not forget to paste the solution code here

    import UIKit
    
    class ViewController: UIViewController {
    
        private weak var label: UILabel!
        override func viewDidLoad() {
            super.viewDidLoad()
            let label = UILabel(frame: .init(x: 40, y: 40, width: 300, height: 80))
            label.numberOfLines = 2
            view.addSubview(label)
            self.label = label
    
            let defaultAttributes = [
                .font: UIFont.systemFont(ofSize: 16, weight: .regular),
                .foregroundColor: UIColor.blue
            ] as [NSAttributedString.Key : Any]
    
            let marketingAttributes = [
                .font: UIFont.systemFont(ofSize: 20.0, weight: .bold),
                .foregroundColor: UIColor.black
            ] as [NSAttributedString.Key : Any]
    
            let attributedStringComponents = [
                "pay for",
                NSAttributedString(string: "one",
                                   attributes: marketingAttributes),
                "and get",
                "three!\n".toAttributed(with: marketingAttributes),
                "Only today!".toAttributed(with: [
                    .font: UIFont.systemFont(ofSize: 16.0, weight: .bold),
                    .foregroundColor: UIColor.red
                ])
            ] as [AttributedStringComponent]
            label.attributedText = NSAttributedString(from: attributedStringComponents, defaultAttributes: defaultAttributes)
            label.textAlignment = .center
        }
    }
    

    Result

提交回复
热议问题