вертикально выровнять текст в CATextLayer? - PullRequest
22 голосов
/ 22 января 2011

Я работаю над CATextLayer, который хочу использовать как на Mac, так и на iOS. Могу ли я контролировать вертикальное выравнивание текста в слое?

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

РЕДАКТИРОВАТЬ: Я нашел это , но я не могу заставить его работать.

Ответы [ 12 ]

25 голосов
/ 08 сентября 2015

Правильный ответ, как вы уже нашли, это здесь в Objective-C и работает для iOS . Он работает путем создания подкласса CATextLayer и переопределения функции drawInContext.

Однако, Я сделал некоторые улучшения в коде , как показано ниже, используя код Дэвида Хёрла в качестве основы. изменения происходят исключительно в , пересчитывая вертикальную позицию текста , представленного yDiff. Я проверил это с моим собственным кодом.

Вот код для пользователей Swift:

class LCTextLayer : CATextLayer {

    // REF: http://lists.apple.com/archives/quartz-dev/2008/Aug/msg00016.html
    // CREDIT: David Hoerl - https://github.com/dhoerl 
    // USAGE: To fix the vertical alignment issue that currently exists within the CATextLayer class. Change made to the yDiff calculation.

    override func draw(in context: CGContext) {
        let height = self.bounds.size.height
        let fontSize = self.fontSize
        let yDiff = (height-fontSize)/2 - fontSize/10

        context.saveGState()
        context.translateBy(x: 0, y: yDiff) // Use -yDiff when in non-flipped coordinates (like macOS's default)
        super.draw(in: context)
        context.restoreGState()
    }
}
11 голосов
/ 17 февраля 2015

Возможно, опоздать на ответ, но вы можете рассчитать размер текста и затем установить положение textLayer. Также вам нужно установить режим textLayer textAligment в "center"

CGRect labelRect = [text boundingRectWithSize:view.bounds.size options:NSStringDrawingUsesLineFragmentOrigin attributes:@{ NSFontAttributeName : [UIFont fontWithName:@"HelveticaNeue" size:17.0] } context:nil];
CATextLayer *textLayer = [CATextLayer layer];
[textLayer setString:text];
[textLayer setForegroundColor:[UIColor redColor].CGColor];
[textLayer setFrame:labelRect];
[textLayer setFont:CFBridgingRetain([UIFont fontWithName:@"HelveticaNeue" size:17.0].fontName)];
[textLayer setAlignmentMode:kCAAlignmentCenter];
[textLayer setFontSize:17.0];
textLayer.masksToBounds = YES;
textLayer.position = CGPointMake(CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds));
[view.layer addSublayer:textLayer];
7 голосов
/ 18 февраля 2017

Это поздний ответ, но у меня сейчас тот же вопрос, и я решил проблему с последующим исследованием.

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

Но мы все равно можем рассчитать среднюю точку по вертикали для разных случаев.

Согласно Apple Об обработке текста в iOS , нам нужно знать, как рисуется текст.

Например, я пытаюсь выровнять по вертикали строки будних дней: Sun, Mon, Tue, ....

В этом случае высота текста зависит от высота кепки , и для этих символов нет спуск .Поэтому, если нам нужно выровнять этот текст по центру, мы можем рассчитать смещение верхней части символа верха, например, положение верхней части символа «S».

В соответствии с рисунком ниже:

fig

Верхний пробел для заглавного символа "S" будет

font.ascender - font.capHeight

А нижний пробел для заглавного символа "S" будет

font.descender + font.leading

Так что нам нужно немного сдвинуть "S" с вершины на:

y = (font.ascender - font.capHeight + font.descender + font.leading + font.capHeight) / 2

Это равно:

y = (font.ascender + font.descender + font.leading) / 2

Тогда я могу сделатьвертикальный текст выравнивается по центру.

Вывод:

  1. Если в вашем тексте нет символов, превышающих базовую линию, например, "p", "j", "g"и без символа над верхней частью высоты, например "f".Вы можете использовать приведенную выше формулу для выравнивания текста по вертикали.

    y = (font.ascender + font.descender + font.leading) / 2
    
  2. Если ваш текст содержит символ ниже базовой линии, например, "p", "j" и без символапревышать высоту колпачка, например, «f».Тогда вертикальная формула будет иметь вид:

    y = (font.ascender + font.descender) / 2
    
  3. Если ваш текст включает в себя не включает символ, нарисованный ниже базовой линии, например, "j", "p", и действительно включает символ, нарисованный вышелиния высоты крышки, например, "f".Тогда y будет:

    y = (font.descender + font.leading) / 2
    
  4. Если в вашем тексте будут встречаться все символы, тогда y равно:

    y = font.leading / 2
    
6 голосов
/ 18 мая 2017

Версия Swift 3 для обычных и атрибутивных строк.

class ECATextLayer: CATextLayer {
    override open func draw(in ctx: CGContext) {
        let yDiff: CGFloat
        let fontSize: CGFloat
        let height = self.bounds.height

        if let attributedString = self.string as? NSAttributedString {
            fontSize = attributedString.size().height
            yDiff = (height-fontSize)/2
        } else {
            fontSize = self.fontSize
            yDiff = (height-fontSize)/2 - fontSize/10
        }

        ctx.saveGState()
        ctx.translateBy(x: 0.0, y: yDiff)
        super.draw(in: ctx)
        ctx.restoreGState()
    }
}
4 голосов
/ 07 января 2017

спасибо @iamktothed, все работает. следующая версия swift 3:

class CXETextLayer : CATextLayer {

override init() {
    super.init()
}

override init(layer: Any) {
    super.init(layer: layer)
}

required init(coder aDecoder: NSCoder) {
    super.init(layer: aDecoder)
}

override func draw(in ctx: CGContext) {
    let height = self.bounds.size.height
    let fontSize = self.fontSize
    let yDiff = (height-fontSize)/2 - fontSize/10

    ctx.saveGState()
    ctx.translateBy(x: 0.0, y: yDiff)
    super.draw(in: ctx)
    ctx.restoreGState()
}
}
2 голосов
/ 01 октября 2016

код ГБК работает. ниже приведен код gbk, обновленный для бета-версии XCode 8. По состоянию на 1 октября 2016 года

Шаг 1. Подкласс CATextLayer. В приведенном ниже коде я назвал подкласс «MyCATextLayer». За пределами вашего класса контроллера представления скопируйте / вставьте приведенный ниже код.

class MyCATextLayer: CATextLayer {

// REF: http://lists.apple.com/archives/quartz-dev/2008/Aug/msg00016.html
// CREDIT: David Hoerl - https://github.com/dhoerl
// USAGE: To fix the vertical alignment issue that currently exists within the CATextLayer class. Change made to the yDiff calculation.

override init() {
    super.init()
}

required init(coder aDecoder: NSCoder) {
    super.init(layer: aDecoder)
}

override func draw(in ctx: CGContext) {
    let height = self.bounds.size.height
    let fontSize = self.fontSize
    let yDiff = (height-fontSize)/2 - fontSize/10

    ctx.saveGState()
    ctx.translateBy(x: 0.0, y: yDiff)
    super.draw(in: ctx)
    ctx.restoreGState()
   }
}

Шаг 2. В вашем классе контроллера представления в вашем файле .swift создайте CATextLabel. В примере кода я назвал подкласс «MyDopeCATextLayer».

let MyDopeCATextLayer: MyCATextLayer = MyCATextLayer()

Шаг 3. Установите новый CATextLayer с нужным текстом / цветом / границами / рамкой.

MyDopeCATextLayer.string = "Hello World"    // displayed text
MyDopeCATextLayer.foregroundColor = UIColor.purple.cgColor //color of text is purple
MyDopeCATextLayer.frame = CGRect(x: 0, y:0, width: self.frame.width, height: self.frame.height)  
MyDopeCATextLayer.font = UIFont(name: "HelveticaNeue-UltraLight", size: 5) //5 is ignored, set actual font size using  ".fontSize" (below)
MyDopeCATextLayer.fontSize = 24
MyDopeCATextLayer.alignmentMode = kCAAlignmentCenter //Horizontally centers text.  text is automatically centered vertically because it's set in subclass code
MyDopeCATextLayer.contentsScale = UIScreen.main.scale  //sets "resolution" to whatever the device is using (prevents fuzzyness/blurryness)

Шаг 4. Готово

1 голос
/ 12 марта 2017

Код для Swift 3, основанный на коде @ iamktothed

Если вы используете приписанную строку для установки свойств шрифта, то вы можете использовать функцию size () из NSAttributedString для вычисления высоты строки. Я думаю, что этот код также решает проблемы, описанные @ Enix

class LCTextLayer: CATextLayer {

    override init() {
        super.init()
    }

    override init(layer: Any) {
        super.init(layer: layer)
    }

    required init(coder aDecoder: NSCoder) {
        super.init(layer: aDecoder)
    }

    override open func draw(in ctx: CGContext) {

        if let attributedString = self.string as? NSAttributedString {

            let height = self.bounds.size.height
            let stringSize = attributedString.size()
            let yDiff = (height - stringSize.height) / 2

            ctx.saveGState()
            ctx.translateBy(x: 0.0, y: yDiff)
            super.draw(in: ctx)
            ctx.restoreGState()
        }
    }
}
0 голосов
/ 25 марта 2019

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

final class CACenteredTextLayer: CATextLayer {
    override func draw(in ctx: CGContext) {
        guard let attributedString = string as? NSAttributedString else { return }

        let height = self.bounds.size.height
        let boundingRect: CGRect = attributedString.boundingRect(
            with: CGSize(width: bounds.width,
                         height: CGFloat.greatestFiniteMagnitude),
            options: NSStringDrawingOptions.usesLineFragmentOrigin,
            context: nil)
        let yDiff: CGFloat = (height - boundingRect.size.height) / 2

        ctx.saveGState()
        ctx.translateBy(x: 0.0, y: yDiff)
        super.draw(in: ctx)
        ctx.restoreGState()
    }
}
0 голосов
/ 16 февраля 2019

Я немного изменил этот ответ @ iamkothed .Различия:

  • расчет высоты текста основан на NSString.size(with: Attributes).Я не знаю, если это улучшение по сравнению с (height-fontSize)/2 - fontSize/10, но мне нравится думать, что это так.Хотя, по моему опыту, NSString.size(with: Attributes) не всегда возвращает наиболее подходящий размер.
  • добавленное invertedYAxis свойство.Для моих целей было полезно экспортировать этот подкласс CATextLayer, используя AVVideoCompositionCoreAnimationTool.AVFoundation работает по «нормальной» оси Y, и поэтому мне пришлось добавить это свойство.
  • Работает только с NSString.Вы можете использовать класс String Swift, поскольку он автоматически приводит к NSString.
  • Он игнорирует свойство CATextLayer.fontSize и полностью полагается на свойство CATextLayer.font, которое ДОЛЖНО быть экземпляром UIFont.

    class VerticallyCenteredTextLayer: CATextLayer {
        var invertedYAxis: Bool = true
    
        override func draw(in ctx: CGContext) {
            guard let text = string as? NSString, let font = self.font as? UIFont else {
                super.draw(in: ctx)
                return
            }
    
            let attributes = [NSAttributedString.Key.font: font]
            let textSize = text.size(withAttributes: attributes)
            var yDiff = (bounds.height - textSize.height) / 2
            if !invertedYAxis {
                yDiff = -yDiff
            }
            ctx.saveGState()
            ctx.translateBy(x: 0.0, y: yDiff)
            super.draw(in: ctx)
            ctx.restoreGState()
        }
    }
    
0 голосов
/ 20 октября 2012

Вы должны знать, куда CATextLayer поместит базовую линию вашего текста. Как только вы это узнаете, сместите систему координат в слое, то есть отрегулируйте bounds.origin.y на разницу между тем, где обычно находится базовая линия, и тем, где вы хотите ее видеть, учитывая метрики шрифта.

CATextLayer - это немного черный ящик, и найти базовую линию немного сложно - см. Мой ответ здесь для iOS - я понятия не имею, каково поведение на Mac.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...