Основной текст - NSAttributedString высота строки сделано правильно? - PullRequest
53 голосов
/ 15 ноября 2011

Я в полном неведении с межстрочным интервалом в Core Text. Я использую NSAttributedString, и я указываю следующие атрибуты на нем: - kCTFontAttributeName - kCTParagraphStyleAttributeName

Отсюда CTFrameSetter создается и обращается к контексту.

В атрибуте стиля абзаца я бы хотел указать высоту линий.

Когда я использую kCTParagraphStyleSpecifierLineHeightMultiple, каждая строка получает заполнение вверху текста вместо текста, отображаемого в середине этой высоты.

Когда я использую kCTParagraphStyleSpecifierLineSpacing, к нижней части текста добавляется отступ.

Пожалуйста, помогите мне достичь заданной высоты строки с текстом (глифами) в середине этой высоты, вместо текста, расположенного либо внизу, либо вверху строки.

Разве это невозможно без перехода по пути явного создания CTLine и т. Д.?

Ответы [ 8 ]

97 голосов
/ 02 мая 2013

Объектив-C

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

Swift 5

let strLength = myString.length()
var style = NSMutableParagraphStyle()
style.lineSpacing = 24
attString.addAttribute(.paragraphStyle, value: style, range: NSRange(location: 0, length: strLength))
12 голосов
/ 17 ноября 2011

Я все еще не уверен на 100% в своих следующих высказываниях, но, похоже, это имеет смысл. Пожалуйста, поправьте меня, где я не прав.

Высота линии (ведущая) относится к расстоянию между базовыми линиями последовательных линий типа. Базовую линию здесь можно интерпретировать как воображаемую линию, на которой находится текст.

Интервал - это расстояние между строками. Пробел появляется после строки текста.

Я использовал следующее решение моей проблемы:

// 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;

Эта матрица используется с параметром стиля абзаца для 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);

Надеюсь, это кому-нибудь поможет. Я буду обновлять этот ответ по мере того, как узнаю более важную информацию.

8 голосов
/ 25 апреля 2017

In 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 голосов
/ 11 октября 2017

Вы можете установить / обновить межстрочный интервал и высоту строки, кратные раскадровке и программно.

Из Interface Builder:

enter image description here

Программный:

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

Теперь вызовите функцию расширения

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

Или используя экземпляр метки (просто скопируйте и выполните этот код, чтобы увидеть результат)

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
1 голос
/ 18 июля 2018

Существует два свойства NSParagraphStyle, которые изменяют высоту между последовательными текстовыми базовыми линиями в том же абзаце : lineSpacing и lineHeightMultiple. @Schoob прав, что lineHeightMultiple над 1.0 добавляет дополнительное пространство над текстом, в то время как lineSpacing над 0.0 добавляет пространство под текстом. Эта диаграмма показывает, как связаны различные измерения.

Чтобы текст оставался по центру, цель состоит в том, чтобы указать одно в терминах другого таким образом, чтобы любое добавление, добавляемое одним атрибутом (верх / низ), было сбалансировано путем определения отступа другого атрибута (внизу / вверху), чтобы соответствовать. Другими словами, любое добавленное дополнительное пространство распределяется равномерно, в противном случае сохраняется существующее позиционирование текста.

Приятно то, что таким образом вы можете выбрать, какой атрибут вы хотите указать, а затем просто определить другой:

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

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

Отсюда другие ответы показывают, как эти два атрибута могут быть установлены в NSAttributedString, но это должно ответить, как эти два атрибута могут быть связаны с «центрированием» текста.

0 голосов
/ 25 июня 2019

Я сделал расширение для этого, см. Ниже.С помощью расширения вы можете просто установить высоту строки следующим образом:

let label = UILabel()
label.lineHeight = 19 

Это расширение:

// Put this in a file called UILabel+Lineheight.swift, or whatever else you want to call it

import UIKit

extension UILabel {

    var lineHeight: CGFloat {
        set {
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.minimumLineHeight = newValue
            paragraphStyle.maximumLineHeight = newValue
            _setAttribute(key: NSAttributedString.Key.paragraphStyle, value: paragraphStyle)
        }
        get {
            let paragraphStyle = _getAttribute(key: NSAttributedString.Key.paragraphStyle) as? NSParagraphStyle
            return paragraphStyle?.minimumLineHeight ?? 0
        }
    }

    func _getAttribute(key: NSAttributedString.Key) -> Any? {
        return attributedText?.attribute(key, at: 0, effectiveRange: .none)
    }

    func _setAttribute(key: NSAttributedString.Key, value: Any) {
        let attributedString: NSMutableAttributedString!
        if let currentAttrString = attributedText {
            attributedString = NSMutableAttributedString(attributedString: currentAttrString)
        } else {
            attributedString = NSMutableAttributedString(string: text ?? "")
            text = nil
        } 
        attributedString.addAttribute(key,
                                      value: value,
                                      range: NSRange(location: 0, length: attributedString.length))
        attributedText = attributedString
    }
}

Примечания:

  • Я нет как высота линии, кратная.Мой проектный документ содержит высоту, например, 20, а не кратную.
  • lineSpacing, как и в некоторых других ответах, является чем-то совершенно другим.Не то, что вы хотите.
  • Причина, по которой есть дополнительный метод _set / _getAttribute, заключается в том, что я использую тот же метод для установки межбуквенного интервала.Может также использоваться для любых других значений NSAttributedString, но кажется, что я хорош только с межбуквенным интервалом (кернинг в Swift / UIKit) и высотой строки.
0 голосов
/ 27 апреля 2019

Еще один способ трякнуть с позицией строки NSAttributedString - играть baselineOffset атрибут:

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


Результат:

"Понятно

Я думаю, что это будет и уведомление, и
место, чтобы увидеть прошлые объявления

Как односторонний чат. "

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

0 голосов
/ 12 февраля 2016

Это сработало для меня в 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
    }
...