web-dev-qa-db-fra.com

Comment appliquer le gras et l'italique à une plage NSMutableAttributedString?

J'ai essayé d'appliquer des combinaisons de NSFontAttributes à NSMutableAttributedString récemment et je ne trouve tout simplement pas d'explication détaillée sur la façon de le faire sans supprimer d'autres attributs.

J'ai cherché un tas, et j'ai trouvé ceci question concernant la façon de le faire avec HTML, puis ce question sur la façon de trouver = où le texte a été mis en gras ou en italique, mais rien sur la façon de le faire.

Actuellement, j'essaie de formater les piqûres comme suit:

Italique: [mutableAttributedString addAttribute: NSFontAttributeName value:[fontAttributes valueForKey:CXItalicsFontAttributeName] range:r];

Gras: [mutableAttributedString addAttribute:NSFontAttributeName value:[fontAttributes valueForKey:CXBoldFontAttributeName] range:r];

Où les constantes CXItalicsFontAttributeName et CXBoldAttributeName extraient les deux valeurs suivantes d'un dictionnaire avec respect:

UIFont *italicsFont = [UIFont fontWithName:@"Avenir-BookOblique" size:14.0f];
UIFont *boldFont = [UIFont fontWithName:@"Avenir-Heavy" size:14.0f];

Je sais que cela ne doit pas être la bonne façon de procéder au formatage, car NSAttributedString attributs standard n'inclut pas ItalicsFontAttribute ou BoldFontAttribute, mais je ne trouve pas la bonne façon de le faire. Quelqu'un peut-il m'aider?

14
Micrified

Si vous appliquez chaque trait (gras ou italique) individuellement, vous devez vous assurer que les plages en gras et en italique ne se chevauchent pas ou un trait écrasera l'autre.

La seule façon d'appliquer les caractères italiques gras et à une plage est d'utiliser une police qui est à la fois en gras et en italique, et d'appliquer les deux traits à la fois .

let str = "Normal Bold Italics BoldItalics"

let font = UIFont(name: "Avenir", size: 14.0)!
let italicsFont = UIFont(name: "Avenir-BookOblique", size: 14.0)!
let boldFont = UIFont(name: "Avenir-Heavy", size: 14.0)!
let boldItalicsFont = UIFont(name: "Avenir-HeavyOblique", size: 14.0)!

let attributedString = NSMutableAttributedString(string: str, attributes: [NSFontAttributeName : font])
attributedString.addAttribute(NSFontAttributeName, value: boldFont, range: NSMakeRange(7, 4))
attributedString.addAttribute(NSFontAttributeName, value: italicsFont, range: NSMakeRange(12, 7))
attributedString.addAttribute(NSFontAttributeName, value: boldItalicsFont, range: NSMakeRange(20, 11))

enter image description here

7
user4151918

Dans Swift et en utilisant une extension:

extension UIFont {

    func withTraits(_ traits: UIFontDescriptorSymbolicTraits) -> UIFont {

        // create a new font descriptor with the given traits
        guard let fd = fontDescriptor.withSymbolicTraits(traits) else {
            // the given traits couldn't be applied, return self
            return self
        }

        // return a new font with the created font descriptor
        return UIFont(descriptor: fd, size: pointSize)
    }

    func italics() -> UIFont {
        return withTraits(.traitItalic)
    }

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

    func boldItalics() -> UIFont {
        return withTraits([ .traitBold, .traitItalic ])
    }
}

Exemple:

if let font = UIFont(name: "Avenir", size: 30) {
    let s = NSAttributedString(string: "Hello World!", attributes: [ NSFontAttributeName: font.italic() ])
    let t = NSAttributedString(string: "Hello World!", attributes: [ NSFontAttributeName: font.boldItalic()) ])
}
20
nyg

Jusqu'à présent, la meilleure solution à mon problème a été d'utiliser la classe UIFontDescriptor pour me fournir le UIFont nécessaire pour chaque cas rencontré.

Par exemple, comme je veux utiliser Avenir-Book comme ma police principale avec une taille 14, Je peux créer un UIFontDescriptor comme suit:

UIFontDescriptor *fontDescriptor = [UIFontDescriptor fontDescriptorWithName:@"Avenir-Book" size:14.0f];

Ensuite, si je souhaite obtenir les caractères italiques, en gras ou une combinaison des deux, j'ai simplement à faire comme suit:

NSString *normalFont = [[fontDescriptor fontAttributes]valueForKey:UIFontDescriptorNameAttribute];
NSString *italicsFont = [[[fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic]fontAttributes]valueForKey:UIFontDescriptorNameAttribute];
NSString *boldFont = [[[fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold]fontAttributes]valueForKey:UIFontDescriptorNameAttribute];
NSString *boldAndItalicsFont = [[[fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold | UIFontDescriptorTraitItalic]fontAttributes]valueForKey:UIFontDescriptorNameAttribute];

Et cela produit en effet les polices requises lors de l'impression:

NSLog(@"Normal Font: %@ Size: %@\n",normalFont,[[fontDescriptor fontAttributes]valueForKey:UIFontDescriptorSizeAttribute]);
NSLog(@"Italics Font: %@\n",italicsFont);
NSLog(@"Bold Font: %@\n",boldFont);
NSLog(@"Bold and Italics Font: %@\n",boldAndItalicsFont);

Sortie:

Normal Font: Avenir-Book Size: 14
Italics Font: Avenir-BookOblique
Bold Font: Avenir-Black
Bold and Italics Font: Avenir-BlackOblique

L'avantage ici est que je n'ai plus besoin de créer moi-même les types de polices individuels, et une police de la famille est suffisante pour le faire.

8
Micrified

Check-out NSAttributedString.Key.obliqueness. Si la valeur de cette clé est différente de 0, le texte en italique est de degré différent.

2
qsmy

Détails

  • Swift 5.1, Xcode 11.3.1

Solution

extension UIFont {
    class func systemFont(ofSize fontSize: CGFloat, symbolicTraits: UIFontDescriptor.SymbolicTraits) -> UIFont? {
        return UIFont.systemFont(ofSize: fontSize).including(symbolicTraits: symbolicTraits)
    }

    func including(symbolicTraits: UIFontDescriptor.SymbolicTraits) -> UIFont? {
        var _symbolicTraits = self.fontDescriptor.symbolicTraits
        _symbolicTraits.update(with: symbolicTraits)
        return withOnly(symbolicTraits: _symbolicTraits)
    }

    func excluding(symbolicTraits: UIFontDescriptor.SymbolicTraits) -> UIFont? {
        var _symbolicTraits = self.fontDescriptor.symbolicTraits
        _symbolicTraits.remove(symbolicTraits)
        return withOnly(symbolicTraits: _symbolicTraits)
    }

    func withOnly(symbolicTraits: UIFontDescriptor.SymbolicTraits) -> UIFont? {
        guard let fontDescriptor = fontDescriptor.withSymbolicTraits(symbolicTraits) else { return nil }
        return .init(descriptor: fontDescriptor, size: pointSize)
    }
}

Usage

font = UIFont.italicSystemFont(ofSize: 15).including(symbolicTraits: .traitBold)
font = UIFont.systemFont(ofSize: 15, symbolicTraits: [.traitBold, .traitItalic])
font = font.excluding(symbolicTraits: [.traitBold]
font = font.withOnly(symbolicTraits: [])

Échantillon complet

N'oubliez pas de coller le code de la solution ici .

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        addLabel(Origin: .init(x: 20, y: 20), font: .systemFont(ofSize: 15, symbolicTraits: [.traitBold, .traitItalic]))
        addLabel(Origin: .init(x: 20, y: 40), font: UIFont.italicSystemFont(ofSize: 15).including(symbolicTraits: .traitBold))
        guard let font = UIFont.systemFont(ofSize: 15, symbolicTraits: [.traitBold, .traitItalic]) else { return }
        addLabel(Origin: .init(x: 20, y: 60), font: font.excluding(symbolicTraits: [.traitBold]))
        addLabel(Origin: .init(x: 20, y: 80), font: font.withOnly(symbolicTraits: []))
    }

    private func addLabel(Origin: CGPoint, font: UIFont?) {
        guard let font = font else { return }
        let label = UILabel(frame: .init(Origin: Origin, size: .init(width: 200, height: 40)))
        label.attributedText = NSAttributedString(string: "Hello World!", attributes: [.font: font, .foregroundColor: UIColor.black ])
        view.addSubview(label)
    }
}

Résultat

enter image description here

0
Vasily Bodnarchuk