Ios Swift making font toggle bold, italic, boldItalic, normal without change other attributes
Asked Answered
C

6

18

I am surprised, that simply setting bold and italic for existing font is so complicated in Swift.

I just want to simplify things by having following methods on font class.

I want the below methods to be added to existing font which has font-family and font-size set. I need to preserve these and change only the following.

setBold : Shud preserve italic

setItalic : Shud preserve bold

setBoldItalic

setNormal : remove both bold and italic

removeBold : Shud preserve italic

removeitalic : Shud preserve bold

I tried the below, and it's feeling like a nightmare for me using fontDescriptorWithSymbolicTraits.

Is there a simpler way of doing these in a few lines of code?

extension UIFont
{
    var isBold: Bool
    {
        return fontDescriptor().symbolicTraits.contains(.TraitBold)
    }

    var isItalic: Bool
    {
        return fontDescriptor().symbolicTraits.contains(.TraitItalic)
    }

    func setBold() -> UIFont
    {
        var fontDescriptorVar: UIFontDescriptor
        if(isBold){
            return self
        }
        else
        {
            fontDescriptorVar = fontDescriptor().fontDescriptorWithSymbolicTraits(.TraitBold)
        }
        return UIFont(descriptor: fontDescriptorVar, size: 0)
    }

    func setItalic()-> UIFont
    {
        var fontDescriptorVar: UIFontDescriptor
        if(isItalic) {
            return self
        }
        else
        {
            fontDescriptorVar = fontDescriptor().fontDescriptorWithSymbolicTraits(.TraitItalic)
        }
        return UIFont(descriptor: fontDescriptorVar, size: 0)
    }

    func setBoldItalic()-> UIFont
    {
        let fontDescriptorVar = fontDescriptor().fontDescriptorWithSymbolicTraits(UIFontDescriptorSymbolicTraits(arrayLiteral: .TraitBold, .TraitItalic))
        return UIFont(descriptor: fontDescriptorVar, size: 0)
    }

    // Things I need are

    // To set back to normal

    func setNormal()-> UIFont
    {

    }

    // Remove only bold if it's both bold and Italic

    func removeBold()-> UIFont
    {

    }

    // Remove only italic if it's both bold and Italic

    func removeitalic()-> UIFont
    {

    }
}

I don't want to use this which asks me the size and font as input:

UIFont(name "namFontFamily", size: 16)

UIFont.systemFontOfSize(16, weight: UIFontWeightLight)

I searched everywhere and found no simple solution matching my needs.

Calvert answered 22/7, 2016 at 18:41 Comment(4)
I think it's really important to check the font family because not all of them have bold and italic forms, are you using default fonts or did you add a new one?Markmarkdown
I have my own list of font family which has bold and italic. Also Text Color is also there with the fontCalvert
most of the fonts have "Bold" or "Italic" suffix so if your font names matching this pattern maybe you will construct your fonts by their names.Gherardi
My methods shud be irrespective of font family, It shud be bolded like in css. Because it am trying to create a general one for all time useCalvert
C
19

You say you want to preserve other traits, so you may need to modify some methods in your code:

func setBold() -> UIFont
{
    if isBold {
        return self
    } else {
        var symTraits = fontDescriptor().symbolicTraits
        symTraits.insert([.TraitBold])
        let fontDescriptorVar = fontDescriptor().fontDescriptorWithSymbolicTraits(symTraits)
        return UIFont(descriptor: fontDescriptorVar, size: 0)
    }
}

setItalic() and setBoldItalic() as well.

So, removeBold() should be something like this:

func removeBold()-> UIFont
{
    if !isBold {
        return self
    } else {
        var symTraits = fontDescriptor().symbolicTraits
        symTraits.remove([.TraitBold])
        let fontDescriptorVar = fontDescriptor().fontDescriptorWithSymbolicTraits(symTraits)
        return UIFont(descriptor: fontDescriptorVar, size: 0)
    }
}

removeItalic() would be similar.

But I'm not sure about setNormal(). You want remove all traits, or want to just remove italic and bold? Maybe you can do it yourself as you like.

Christiano answered 22/7, 2016 at 21:8 Comment(2)
This is what i was looking for, Thanks a lot. You really saved my day :-)Calvert
@ReimondHill, Please explain not working. As far as I test my code with Xcode 10 and iPhone sim 12.0, my code translated for Swift 4.2 works as expected.Christiano
C
7

These functions should have come inbuilt in Swift, but I wish they add them in upcoming versions.

This is for all who wants a simple solution for setting bold and italic... etc. in Swift and don't want to spend the whole night like me.

This has following features:

isBold

isItalic

setBold: Should preserve italic

setItalic: Should preserve bold

setBoldItalic

setNormal: remove both bold and italic

desetBold: Should preserve italic

desetItalic: Should preserve bold

toggleBold

toggleItalic

extension UIFont
{
    var isBold: Bool
    {
        return fontDescriptor().symbolicTraits.contains(.TraitBold)
    }

    var isItalic: Bool
    {
        return fontDescriptor().symbolicTraits.contains(.TraitItalic)
    }

    func setBoldFnc() -> UIFont
    {
        if(isBold)
        {
            return self
        }
        else
        {
            var fontAtrAry = fontDescriptor().symbolicTraits
            fontAtrAry.insert([.TraitBold])
            let fontAtrDetails = fontDescriptor().fontDescriptorWithSymbolicTraits(fontAtrAry)
            return UIFont(descriptor: fontAtrDetails, size: 0)
        }
    }

    func setItalicFnc()-> UIFont
    {
        if(isItalic)
        {
            return self
        }
        else
        {
            var fontAtrAry = fontDescriptor().symbolicTraits
            fontAtrAry.insert([.TraitItalic])
            let fontAtrDetails = fontDescriptor().fontDescriptorWithSymbolicTraits(fontAtrAry)
            return UIFont(descriptor: fontAtrDetails, size: 0)
        }
    }

    func setBoldItalicFnc()-> UIFont
    {
        return setBoldFnc().setItalicFnc()
    }

    func detBoldFnc() -> UIFont
    {
        if(!isBold)
        {
            return self
        }
        else
        {
            var fontAtrAry = fontDescriptor().symbolicTraits
            fontAtrAry.remove([.TraitBold])
            let fontAtrDetails = fontDescriptor().fontDescriptorWithSymbolicTraits(fontAtrAry)
            return UIFont(descriptor: fontAtrDetails, size: 0)
        }
    }

    func detItalicFnc()-> UIFont
    {
        if(!isItalic)
        {
            return self
        }
        else
        {
            var fontAtrAry = fontDescriptor().symbolicTraits
            fontAtrAry.remove([.TraitItalic])
            let fontAtrDetails = fontDescriptor().fontDescriptorWithSymbolicTraits(fontAtrAry)
            return UIFont(descriptor: fontAtrDetails, size: 0)
        }
    }

    func SetNormalFnc()-> UIFont
    {
        return detbBoldFnc().detbItalicFnc()
    }

    func toggleBoldFnc()-> UIFont
    {
        if(isBold)
        {
            return detbBoldFnc()
        }
        else
        {
            return setBoldFnc()
        }
    }

    func toggleItalicFnc()-> UIFont
    {
        if(isItalic)
        {
            return detbItalicFnc()
        }
        else
        {
            return setItalicFnc()
        }
    }
}
Calvert answered 22/7, 2016 at 22:26 Comment(0)
B
5

Swift 3.1

extension UIFont{
var isBold: Bool
{
    return fontDescriptor.symbolicTraits.contains(.traitBold)
}

var isItalic: Bool
{
    return fontDescriptor.symbolicTraits.contains(.traitItalic)
}

func setBold() -> UIFont
{
    if(isBold)
    {
        return self
    }
    else
    {
        var fontAtrAry = fontDescriptor.symbolicTraits
        fontAtrAry.insert([.traitBold])
        let fontAtrDetails = fontDescriptor.withSymbolicTraits(fontAtrAry)
        return UIFont(descriptor: fontAtrDetails!, size: pointSize)
    }
}

func setItalic()-> UIFont
{
    if(isItalic)
    {
        return self
    }
    else
    {
        var fontAtrAry = fontDescriptor.symbolicTraits
        fontAtrAry.insert([.traitItalic])
        let fontAtrDetails = fontDescriptor.withSymbolicTraits(fontAtrAry)
        return UIFont(descriptor: fontAtrDetails!, size: pointSize)
    }
}
func desetBold() -> UIFont
{
    if(!isBold)
    {
        return self
    }
    else
    {
        var fontAtrAry = fontDescriptor.symbolicTraits
        fontAtrAry.remove([.traitBold])
        let fontAtrDetails = fontDescriptor.withSymbolicTraits(fontAtrAry)
        return UIFont(descriptor: fontAtrDetails!, size: pointSize)
    }
}

func desetItalic()-> UIFont
{
    if(!isItalic)
    {
        return self
    }
    else
    {
        var fontAtrAry = fontDescriptor.symbolicTraits
        fontAtrAry.remove([.traitItalic])
        let fontAtrDetails = fontDescriptor.withSymbolicTraits(fontAtrAry)
        return UIFont(descriptor: fontAtrDetails!, size: pointSize)
    }
}
}
Boatswain answered 4/7, 2017 at 5:13 Comment(0)
I
3

Another way of doing this is by checking the selection of bold & italic buttons

For Bold :

@IBAction func bold(_ sender : UIButton){
    sender.isSelected = !sender.isSelected
    if sender.isSelected{
        self.textView.font = self.textView.font?.bold()
    }else{
        self.textView.font = self.textView.font?.removeBold()
    }
}

For Italic :

@IBAction func italic(_ sender : UIButton){
    sender.isSelected = !sender.isSelected
    if sender.isSelected{
        self.textView.font = self.textView.font?.italic()
    }else{
        self.textView.font = self.textView.font?.removeItalic()
    }
}

Then the UIFont extension can hold different methods for adding and removing traits

Make sure that you persist previous traits while adding new ones in withTraits method

extension UIFont {

//Add Traits
func withTraits(traits:UIFontDescriptor.SymbolicTraits) -> UIFont {
    let symTraits = fontDescriptor.symbolicTraits
    let descriptor = fontDescriptor.withSymbolicTraits(UIFontDescriptor.SymbolicTraits(arrayLiteral: symTraits, traits))
    return UIFont(descriptor: descriptor!, size: 0) //size 0 means keep the size as it is
}

func bold() -> UIFont {
    return withTraits(traits: .traitBold)
}

func italic() -> UIFont {
    return withTraits(traits: .traitItalic)
}

//remove traits
func withoutTraits(traits:UIFontDescriptor.SymbolicTraits) -> UIFont {
    var symTraits = fontDescriptor.symbolicTraits
    symTraits.remove([traits])
    let fontDescriptorVar = fontDescriptor.withSymbolicTraits(symTraits)
    return UIFont(descriptor: fontDescriptorVar!, size: 0)
}

func removeBold() -> UIFont {
    return withoutTraits(traits: .traitBold)
}

func removeItalic() -> UIFont {
    return withoutTraits(traits: .traitItalic)
}

}
Iorio answered 2/9, 2020 at 12:39 Comment(0)
L
2

SWIFT 3.1

func changeTrait(trait: UIFontDescriptorSymbolicTraits) {
        let range = textView.selectedRange
        let currentAttributes = textView.textStorage.attributes(at: range.location, effectiveRange: nil)
        guard let currentFont = currentAttributes[NSFontAttributeName] as? UIFont else {
            return
        }

        let fontDescriptor = currentFont.fontDescriptor
        var changedFontDescriptor: UIFontDescriptor!


        if fontDescriptor.symbolicTraits.contains(trait) {
            let existingTraitsWithNewTrait = UIFontDescriptorSymbolicTraits(rawValue: fontDescriptor.symbolicTraits.rawValue & ~trait.rawValue)
            changedFontDescriptor = fontDescriptor.withSymbolicTraits(existingTraitsWithNewTrait)
        } else {
            changedFontDescriptor = fontDescriptor.withSymbolicTraits(UIFontDescriptorSymbolicTraits(rawValue: fontDescriptor.symbolicTraits.rawValue | trait.rawValue))
        }

        let updatedFont = UIFont(descriptor: changedFontDescriptor , size: 0)

        let newAttributes = [NSFontAttributeName: updatedFont]
        textView.textStorage.beginEditing()
        textView.textStorage.setAttributes(newAttributes, range: range)
        textView.textStorage.endEditing()
 }

INVOKE:

  1. FOR BOLD: changeTrait(trait: .traitBold)
  2. FOR ITALIC: changeTrait(trait: .traitItalic)
Loseff answered 7/6, 2017 at 4:55 Comment(0)
I
0

I don't know if I can help you, but I think you can do this way:

extension UIFont {

    public static func CreateWithStyle(name: String, size: CGFloat, styles: [UIFontDescriptor.SymbolicTraits]) -> UIFont {

        let fontDescriptor = UIFontDescriptor(name: name, size: size)
        var fontAtrAry = fontDescriptor.symbolicTraits

        if styles.count > 0 {

            for style in styles {
                fontAtrAry.update(with: style)
            }
        }

        return UIFont(descriptor: fontDescriptor.withSymbolicTraits(fontAtrAry)!, size: size)
    }
}

I don't create a remove system because I don't think we would need it. When we are creating a Font, we establish the styles we want and that's it. Hope I helped.

Iata answered 18/4, 2019 at 11:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.