web-dev-qa-db-fra.com

Texte de base - La hauteur de ligne NSAttributedString est correcte?

Je suis complètement dans le noir avec l'interligne de Core Text. J'utilise NSAttributedString et j'y spécifie les attributs suivants: - kCTFontAttributeName - kCTParagraphStyleAttributeName

CTFrameSetter est alors créé et dessiné dans un contexte.

Dans l'attribut de style de paragraphe, j'aimerais spécifier la hauteur des lignes. 

Lorsque j'utilise kCTParagraphStyleSpecifierLineHeightMultiple, chaque ligne est complétée par un remplissage en haut du texte, au lieu du texte affiché au milieu de cette hauteur.

Lorsque j'utilise kCTParagraphStyleSpecifierLineSpacing, un remplissage est ajouté au bas du texte. 

Aidez-moi à atteindre une hauteur de ligne spécifiée avec le texte (glyphes) au milieu de cette hauteur, au lieu du texte placé en bas ou en haut de la ligne.

N'est-ce pas possible sans aller dans le sens de la création explicite de CTLine et ainsi de suite?

46
Schoob

Je ne suis toujours pas sûr à 100% de mes déclarations suivantes, mais cela semble logique. S'il te plaît, corrige-moi là où je me trompe.

La hauteur de ligne (interligne) fait référence à la distance entre les lignes de base des lignes de type successives. La ligne de base peut ici être interprétée comme la ligne imaginaire sur laquelle repose le texte.

L'espacement est l'espace entre les lignes. L'espace apparaît après la ligne de texte.

J'ai fini par utiliser la solution suivante à mon problème:

// NOT SURE WHAT THE THEORY BEHIND THIS FACTOR IS. WAS FOUND VIA TRIAL AND ERROR.
    CGFloat factor = 14.5/30.5;
    CGFloat floatValues[4];
    floatValues[0] = self.lineHeight * factor/(factor + 1);
    floatValues[1] = self.lineHeight/(factor + 1);
    floatValues[2] = self.lineHeight;

Cette matrice est utilisée avec le paramètre de style de paragraphe pour NSAttributedString:

CTParagraphStyleSetting paragraphStyle[3];

paragraphStyle[0].spec = kCTParagraphStyleSpecifierLineSpacing;
paragraphStyle[0].valueSize = sizeof(CGFloat);
paragraphStyle[0].value = &floatValues[0];

paragraphStyle[1].spec = kCTParagraphStyleSpecifierMinimumLineHeight;
paragraphStyle[1].valueSize = sizeof(CGFloat);
paragraphStyle[1].value = &floatValues[1];

paragraphStyle[2].spec = kCTParagraphStyleSpecifierMaximumLineHeight;
paragraphStyle[2].valueSize = sizeof(CGFloat);
paragraphStyle[2].value = &floatValues[2];

CTParagraphStyleRef style = CTParagraphStyleCreate((const CTParagraphStyleSetting*) &paragraphStyle, 3);
[attributedString addAttribute:(NSString*)kCTParagraphStyleAttributeName value:(id)style range:NSMakeRange(0, [string length])];
CFRelease(style);

J'espère que ça aide quelqu'un. Je mettrai à jour cette réponse à mesure que je découvrirai des informations plus pertinentes.

11
Schoob

Vous pouvez l'utiliser si vous développez pour iOS> = 6.0

NSInteger strLength = [myString length];
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
[style setLineSpacing:24];
[attString addAttribute:NSParagraphStyleAttributeName
                  value:style
                  range:NSMakeRange(0, strLength)];
88
Tieme

Dans Swift 3 :

    let textFont = UIFont(name: "Helvetica Bold", size: 20)!
    let textColor = UIColor(white: 1, alpha: 1)      // White
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.paragraphSpacing = 20             // Paragraph Spacing
    paragraphStyle.lineSpacing = 40                  // Line Spacing

    let textFontAttributes = [
        NSFontAttributeName: textFont,
        NSForegroundColorAttributeName: textColor,
        NSParagraphStyleAttributeName: paragraphStyle
        ] as [String : Any]
7
Enrique

Vous pouvez définir/mettre à jour l'espacement et la hauteur de ligne multiples à partir du storyboard ainsi que par programme.

De Interface Builder:

enter image description here

Par programme:

Swift 4

extension UILabel {

    // Pass value for any one of both parameters and see result
    func setLineSpacing(lineSpacing: CGFloat = 0.0, lineHeightMultiple: CGFloat = 0.0) {

        guard let labelText = self.text else { return }

        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineSpacing = lineSpacing
        paragraphStyle.lineHeightMultiple = lineHeightMultiple

        let attributedString:NSMutableAttributedString
        if let labelattributedText = self.attributedText {
            attributedString = NSMutableAttributedString(attributedString: labelattributedText)
        } else {
            attributedString = NSMutableAttributedString(string: labelText)
        }

        // Line spacing attribute

// Swift 4.2++


attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))

// Swift 4.1--
attributedString.addAttribute(NSAttributedStringKey.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))

        self.attributedText = attributedString
    }
}

Appelez maintenant la fonction d'extension

let label = UILabel()
let stringValue = "How to\ncontrol\nthe\nline spacing\nin UILabel"

// Pass value for any one argument - lineSpacing or lineHeightMultiple
label.setLineSpacing(lineSpacing: 2.0) .  // try values 1.0 to 5.0

// or try lineHeightMultiple
//label.setLineSpacing(lineHeightMultiple = 2.0) // try values 0.5 to 2.0

Ou en utilisant une instance de label (il suffit de copier et d'exécuter ce code pour voir le résultat)

let label = UILabel()
let stringValue = "How to\ncontrol\nthe\nline spacing\nin UILabel"
let attrString = NSMutableAttributedString(string: stringValue)
var style = NSMutableParagraphStyle()
style.lineSpacing = 24 // change line spacing between paragraph like 36 or 48
style.minimumLineHeight = 20 // change line spacing between each line like 30 or 40

// Swift 4.2++
// Line spacing attribute
attrString.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: NSRange(location: 0, length: stringValue.characters.count))

// Character spacing attribute
attrString.addAttribute(NSAttributedString.Key.kern, value: 2, range: NSMakeRange(0, attrString.length))


// Swift 4.1--
// Line spacing attribute
attrString.addAttribute(NSAttributedStringKey.paragraphStyle, value: style, range: NSRange(location: 0, length: stringValue.characters.count))

// Character spacing attribute
attrString.addAttribute(NSAttributedStringKey.kern, value: 2, range: NSMakeRange(0, attrString.length))

label.attributedText = attrString

Swift 3

let label = UILabel()
let stringValue = "How to\ncontrol\nthe\nline spacing\nin UILabel"
let attrString = NSMutableAttributedString(string: stringValue)
var style = NSMutableParagraphStyle()
style.lineSpacing = 24 // change line spacing between paragraph like 36 or 48
style.minimumLineHeight = 20 // change line spacing between each line like 30 or 40
attrString.addAttribute(NSParagraphStyleAttributeName, value: style, range: NSRange(location: 0, length: stringValue.characters.count))
label.attributedText = attrString
5
Krunal

Il existe deux propriétés de NSParagraphStyle qui modifient la hauteur entre les lignes de base de texte successives dans le même paragraphe: lineSpacing et lineHeightMultiple. @Schoob a raison de dire qu'une lineHeightMultiple ci-dessus 1.0 ajoute un espace supplémentaire au-dessus du texte, tandis qu'une lineSpacing ci-dessus 0.0 ajoute un espace sous le texte. Ce diagramme montre la relation entre les différentes dimensions.

Pour que le texte reste centré, l’objectif est donc de spécifier l’un en fonction de l’autre, de sorte que tout «remplissage» ajouté par un attribut (haut/bas) soit contrebalancé en déterminant le remplissage de l’autre attribut (bas/en haut) pour correspondre. En d'autres termes, tout espace supplémentaire ajouté est distribué de manière uniforme tout en préservant le positionnement existant du texte.

La bonne chose est que de cette façon, vous pouvez choisir l’attribut que vous voulez spécifier et ensuite déterminer l’autre:

extension UIFont
{
    func lineSpacingToMatch(lineHeightMultiple: CGFloat) -> CGFloat {
        return self.lineHeight * (lineHeightMultiple - 1)
    }

    func lineHeightMultipleToMatch(lineSpacing: CGFloat) -> CGFloat {
        return 1 + lineSpacing / self.lineHeight
    }
}

A partir de là, d'autres réponses montrent comment ces deux attributs peuvent être définis dans une variable NSAttributedString, mais cela devrait indiquer comment les deux peuvent être associés au "centre" du texte.

1
wardw

Une autre façon de twerking avec une position de ligne NSAttributedString est de jouer avec baselineOffset attribut:

let contentText = NSMutableAttributedString(
string: "I see\nI'd think it`d be both a notification and a\nplace to see past announcements\nLike a one way chat.")

contentText.addAttribute(.baselineOffset, value: 10, range: NSRange(location: 0, length: 5))
contentText.addAttribute(.baselineOffset, value: -10, range: NSRange(location: 85, length: 20))


Résultat:

"Je vois

Je pense que ce serait à la fois une notification et une
endroit pour voir les annonces passées

Comme un chat à sens unique. "

https://stackoverflow.com/a/55876401/4683601

0
Ely Dantas

Cela a fonctionné pour moi dans Xcode 7.2. iOS 9.2.1. (Swift 2.1.):

  dispatch_async(dispatch_get_main_queue()) { () -> Void in
        let paragraphStyleWithSpacing           = NSMutableParagraphStyle()
        paragraphStyleWithSpacing.lineSpacing   = 2.0 //CGFloat
        let textWithLineSpacing                 = NSAttributedString(string: str, attributes: [NSParagraphStyleAttributeName : paragraphStyleWithSpacing])
        self.MY_TEXT_VIEW_NAME.attributedText   = textWithLineSpacing
    }
0
MB_iOSDeveloper