Skip to main content
deleted 222 characters in body
Source Link
Fattie
  • 9.8k
  • 76
  • 455
  • 769

rmaddy has invented an entirely new and amazing technique for this "old chestnut"annoying problem in iOS.

We have not had time to really test it out but it seems infinitely better than the "old way" explained in thisThe answer by manmal is the final perfected version. Cheers!


 

Here's an outline ofPurely for the historical record here is roughly how you'd go about doing it the basic solution.old days...it's a complete PITA

 
// somewhat depressingly, we have to (very carefully) convert it to "our" font // the only way is change- each"re-doing" sectionany BYother HANDformatting. // thus, forchange everyeach section you must care for bold/italicBY sectionsHAND. // the fixFontsInAttributedStringForUseInApp() call does all or most oftotal thatPITA. 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() } 

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

rmaddy has invented an entirely new and amazing technique for this "old chestnut" problem.

We have not had time to really test it out but it seems infinitely better than the "old way" explained in this answer. Cheers!


 

Here's an outline of the basic solution....it's a complete PITA

// somewhat depressingly, we have to (very carefully) convert it to "our" font // the only way is change each section BY HAND. // thus, for every section you must care for bold/italic sections. // the fixFontsInAttributedStringForUseInApp() call does all or most of that 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() } 

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

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() } 

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.

deleted 101 characters in body
Source Link
Fattie
  • 9.8k
  • 76
  • 455
  • 769

I don't like to answer my own question, but here's an outline of the basic solution.Important -

It is actually incredibly difficult to dormaddy has invented an entirely new and amazing technique for this, you have to in fact massage every "attribute part" by hand "old chestnut" problem. As of late 2017, there is

#... no easy wayWe have not had time to doreally test it :/

There is no drop-in copy and paste solution to all eventualities here,out but this basic clean code will resolve much ofit seems infinitely better than the issue"old way" explained in most casesthis answer. Cheers!

 

Hope it helps someone!Here's an outline of the basic solution....it's a complete PITA

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

I don't like to answer my own question, but here's an outline of the basic solution.

It is actually incredibly difficult to do this, you have to in fact massage every "attribute part" by hand. As of late 2017, there is

#... no easy way to do it :/

There is no drop-in copy and paste solution to all eventualities here, but this basic clean code will resolve much of the issue in most cases.

Hope it helps someone!

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

Important -

rmaddy has invented an entirely new and amazing technique for this "old chestnut" problem.

We have not had time to really test it out but it seems infinitely better than the "old way" explained in this answer. Cheers!

 

Here's an outline of the basic solution....it's a complete PITA

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

added 961 characters in body
Source Link
Fattie
  • 9.8k
  • 76
  • 455
  • 769

.


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 } } 

.

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

.


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 } } 

.

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

Source Link
Fattie
  • 9.8k
  • 76
  • 455
  • 769
Loading