2

I'm trying to apply Bold and Italic to selected text in UITextView using textKit in swift.

This is the code:

let isBold = false

if !isActive(modification: .swapBoldWithItalic) || isBold{
    storage.setAttributes(attributes, range: range)
} else {
    let currentFont = attributes![.font] as? UIFont
    
    let fontDescriptor = currentFont?.fontDescriptor
    
    var changedFontDescriptor: UIFontDescriptor?
    
    if fontDescriptor!.symbolicTraits.contains(.traitItalic) {
        changedFontDescriptor = fontDescriptor?.withSymbolicTraits(fontDescriptor!.symbolicTraits.union(.traitItalic))
        
    } else {
        changedFontDescriptor = fontDescriptor?.withSymbolicTraits(fontDescriptor!.symbolicTraits.union(.traitBold))
        
    }
    
    
    var updatedFont: UIFont? = nil
    if let changedFontDescriptor = changedFontDescriptor {
        updatedFont = UIFont(descriptor: changedFontDescriptor, size: (currentFont?.pointSize)!)
        
    }
    let dict = [
        NSAttributedString.Key.font: updatedFont,
        NSAttributedString.Key.foregroundColor: UIColor.red
    ]
    
    storage.setAttributes(dict, range: range)
}

What I'm trying to achieve is

  • change the selected text to bold or italic
  • change the selected text which is bold to italic or vice versa

What is happening now is when i select text and change it to bold it changes to bold, but when i try to change another text to Italic also it changes to bold and I'm still not able to swap from bold to italic.

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
akku
  • 25
  • 7

1 Answers1

3

I suspect you wanna toggle bold and italic by different buttons. I've made an extension which will be easy for you to use:

extension UIFont {
    func byTogglingSymbolicTraits(_ symbolicTraits: UIFontDescriptor.SymbolicTraits) -> UIFont {
        UIFont(
            descriptor: fontDescriptor.byTogglingSymbolicTraits(symbolicTraits),
            size: pointSize
        )
    }
}

extension UIFontDescriptor {
    func byTogglingSymbolicTraits(_ traints: UIFontDescriptor.SymbolicTraits) -> UIFontDescriptor {
        if symbolicTraits.contains(traints) {
            return withSymbolicTraits(symbolicTraits.subtracting(traints))!
        } else {
            return withSymbolicTraits(symbolicTraits.union(traints))!
        }
    }
}

Usage:

font = font.byTogglingSymbolicTraits(.traitBold)
// or
font = font.byTogglingSymbolicTraits(.traitItalic)
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • is there a way to check which one is selected in either 'Bold' or 'italic'? – akku Aug 13 '21 at 15:39
  • @akku `fontDescriptor.symbolicTraits.contains(.traitItalic)` – Phil Dukhov Aug 13 '21 at 15:40
  • 'if (fontDescriptor!.symbolicTraits.contains(.traitItalic)) { changedFontDescriptor = fontDescriptor!.withSymbolicTraits(fontDescriptor!.symbolicTraits.union(.traitItalic)) } else { changedFontDescriptor = fontDescriptor!.withSymbolicTraits(fontDescriptor!.symbolicTraits.union(.traitBold)) }' this isnt working? – akku Aug 13 '21 at 16:00
  • @akku in this if you're checking if there's italic then you're adding one more italic(which changes nothing), otherwise you're adding bold. Not sure what're you going for, but this doesn't sounds correct to me – Phil Dukhov Aug 13 '21 at 16:05
  • I want to swap between Bold and italic for selected text. But I'm not able to accomplish it. So i wanted to check which is slected either 'Bold', 'Italic', or underline – akku Aug 13 '21 at 16:07
  • @akku this is completely out of scope of this question, isn't it? it was about mixing bold and italic.. if you wanna swap between bold and italic you just need this: if fontDescriptor.symbolicTraits.contains(.traitItalic) { changedFontDescriptor = fontDescriptor!.withSymbolicTraits(.traitBold)! } else { changedFontDescriptor = fontDescriptor!.withSymbolicTraits(.traitItalic)! } – Phil Dukhov Aug 13 '21 at 16:20