NSAttributedString, change the font overall BUT keep all other attributes?

前端 未结 6 888
长情又很酷
长情又很酷 2020-11-28 08:24

Say I have an NSMutableAttributedString .

The string has a varied mix of formatting throughout:

Here is an example:

6条回答
  •  被撕碎了的回忆
    2020-11-28 09:11

    Important -

    rmaddy has invented an entirely new technique for this annoying problem in iOS.

    The answer by manmal is the final perfected version.

    Purely for the historical record here is roughly how you'd go about doing it the old days...


    // carefully convert to "our" font - "re-doing" any other formatting.
    // change each section BY HAND.  total PITA.
    
    func fixFontsInAttributedStringForUseInApp() {
    
        cachedAttributedString?.beginEditing()
    
        let rangeAll = NSRange(location: 0, length: cachedAttributedString!.length)
    
        var boldRanges: [NSRange] = []
        var italicRanges: [NSRange] = []
    
        var boldANDItalicRanges: [NSRange] = [] // WTF right ?!
    
        cachedAttributedString?.enumerateAttribute(
                NSFontAttributeName,
                in: rangeAll,
                options: .longestEffectiveRangeNotRequired)
                    { value, range, stop in
    
                    if let font = value as? UIFont {
    
                        let bb: Bool = font.fontDescriptor.symbolicTraits.contains(.traitBold)
                        let ii: Bool = font.fontDescriptor.symbolicTraits.contains(.traitItalic)
    
                        // you have to carefully handle the "both" case.........
    
                        if bb && ii {
    
                            boldANDItalicRanges.append(range)
                        }
    
                        if bb && !ii {
    
                            boldRanges.append(range)
                        }
    
                        if ii && !bb {
    
                            italicRanges.append(range)
                        }
                    }
                }
    
        cachedAttributedString!.setAttributes([NSFontAttributeName: font_f], range: rangeAll)
    
        for r in boldANDItalicRanges {
            cachedAttributedString!.addAttribute(NSFontAttributeName, value: font_fBOTH, range: r)
        }
    
        for r in boldRanges {
            cachedAttributedString!.addAttribute(NSFontAttributeName, value: font_fb, range: r)
        }
    
        for r in italicRanges {
            cachedAttributedString!.addAttribute(NSFontAttributeName, value: font_fi, range: r)
        }
    
        cachedAttributedString?.endEditing()
    }
    

    .


    Footnote. Just for clarity on a related point. This sort of thing inevitably starts as a HTML string. Here's a note on how to convert a string that is html to an NSattributedString .... you will end up with nice attribute ranges (italic, bold etc) BUT the fonts will be fonts you don't want.

    fileprivate extension String {
        func htmlAttributedString() -> NSAttributedString? {
            guard let data = self.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
            guard let html = try? NSMutableAttributedString(
                data: data,
                options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
                documentAttributes: nil) else { return nil }
            return html
        }
    }  
    

    .

    Even that part of the job is non-trivial, it takes some time to process. In practice you have to background it to avoid flicker.

提交回复
热议问题