垂直对齐CATextLayer中的文本?
我正在开发一个我想在Mac和iOS上使用的CATextLayer。 我可以控制图层中文本的垂直对齐方式吗?
在这种特殊情况下,我希望将其垂直居中 – 但有关其他垂直对齐的信息也会引起人们的兴趣。
编辑:我发现了这个 ,但我无法使它工作。
正如您已经发现的那样,正确的答案在Objective-C中, 适用于iOS 。 它通过子类化CATextLayer
并覆盖drawInContext
函数来工作。
但是, 我已经使用David Hoerl的代码作为基础对代码进行了一些改进 ,如下所示。 这些变化仅仅是重新计算 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 init!() { super.init() } override init!(layer: AnyObject!) { super.init(layer: layer) } required init(coder aDecoder: NSCoder) { super.init(layer: aDecoder) } override func drawInContext(ctx: CGContext!) { let height = self.bounds.size.height let fontSize = self.fontSize let yDiff = (height-fontSize)/2 - fontSize/10 CGContextSaveGState(ctx) CGContextTranslateCTM(ctx, 0.0, yDiff) super.drawInContext(ctx) CGContextRestoreGState(ctx) } }
也许迟到的答案,但你可以计算文本的大小,然后设置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];
这是一个迟到的答案,但这些天我有同样的问题,并通过以下调查解决了这个问题。
垂直对齐取决于您需要绘制的文本以及您使用的字体,因此没有一种方法可以使所有情况都垂直。
但我们仍然可以计算不同情况下的垂直中点。
根据Apple 在iOS中的关于文本处理 ,我们需要知道如何绘制文本。
例如,我正在尝试为工作日字符串进行垂直对齐:Sun,Mon,Tue,….
对于这种情况,文本的高度取决于上限高度 ,并且这些字符没有下降 。 因此,如果我们需要使这些文本与中间对齐,我们可以计算顶部字符顶部的偏移量,例如字符“S”顶部的位置。
根据下图:
首都角色“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
然后我可以使文本垂直对齐中间。
结论:
-
如果您的文字不包含任何超过基线的字符,例如“p”,“j”,“g”,并且在帽子高度的顶部没有字符,例如“f”。 您可以使用上面的公式使文本对齐垂直。
y = (font.ascender + font.descender + font.leading) / 2
-
如果您的文字包含基线以下的字符,例如“p”,“j”,并且没有字符超过上限高度,例如“f”。 那么垂直公式将是:
y = (font.ascender + font.descender) / 2
-
如果您的文字包括不包括在基线下方绘制的字符,例如“j”,“p”,并且包括在帽高线以上绘制的字符,例如“f”。 然后你会是:
y = (font.descender + font.leading) / 2
-
如果文本中出现所有字符,则y等于:
y = font.leading / 2
谢谢@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() } }
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() } }
Swift 3的代码,基于代码@iamktothed
如果使用属性字符串来设置字体属性,则可以使用NSAttributedString中的函数size()来计算字符串的高度。 我认为这段代码也解决了@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() } } }
所以没有“直接”的方式来做到这一点,但你可以通过使用文本指标来完成同样的事情:
…例如,找到文本的大小,然后使用该信息将其放置在父图层中所需的位置。 希望这可以帮助。
您需要知道CATextLayer将放置文本基线的位置。 一旦你知道这一点,就会偏移图层中的坐标系,即根据字体的指标调整bounds.origin.y,即基线正常位置和你想要的位置之间的差异。
CATextLayer是一个黑盒子,找到基线所在的位置有点棘手 – 请参阅我在iOS的答案 – 我不知道Mac上的行为是什么。
gbk的代码有效。 下面是为XCode 8 beta 6更新的gbk代码。截至2016年10月1日
步骤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步。完成
我可以说,我的问题的答案是“不”。