我正在开发一个CATextLayer,希望它可以在Mac和iOS上使用。我能控制图层内文本的垂直对齐吗?
在这种情况下,我想要将文本垂直居中--但其他垂直对齐方式的信息也很有用。
编辑:我找到了这个,但是我无法使其工作。
我正在开发一个CATextLayer,希望它可以在Mac和iOS上使用。我能控制图层内文本的垂直对齐吗?
在这种情况下,我想要将文本垂直居中--但其他垂直对齐方式的信息也很有用。
编辑:我找到了这个,但是我无法使其工作。
正如您已经发现的那样,正确答案在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 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()
}
}
顶部空间用于大写字母"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
If your text does not include any character exceed the baseline, e.g. "p", "j", "g", and no character over the top of cap height, e.g. "f". The you can use the formula above to make the text align vertical.
y = (font.ascender + font.descender + font.leading) / 2
If your text include character below the baseline, e.g. "p", "j", and no character exceed the top of cap height, e.g. "f". Then the vertical formula would be:
y = (font.ascender + font.descender) / 2
If your text include does not include character drawn below the baseline, e.g. "j", "p", and does include character drawn above the cap height line, e.g. "f". Then y would be:
y = (font.descender + font.leading) / 2
If all characters would be occurred in your text, then y equals to:
y = font.leading / 2
y
的意思是什么? - alpineorigin.y
。 - Kjuly可能答案有些晚了,不过您可以计算文本大小,然后设置文本图层的位置。同时,您需要将文本图层的文本对齐模式设置为“中心”。
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];
正则表达式和属性字符串的 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()
}
}
ctx.translateBy(x: 0.0, y: -yDiff)
。 - Miro Markaravanes感谢@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()
}
}
更新此线程(适用于单行和多行CATextLayer),结合以上一些答案。
class VerticalAlignedTextLayer : CATextLayer {
func calculateMaxLines() -> Int {
let maxSize = CGSize(width: frame.size.width, height: CGFloat(Float.infinity))
let font = UIFont(descriptor: self.font!.fontDescriptor, size: self.fontSize)
let charSize = font.lineHeight
let text = (self.string ?? "") as! NSString
let textSize = text.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
let linesRoundedUp = Int(ceil(textSize.height/charSize))
return linesRoundedUp
}
override func draw(in context: CGContext) {
let height = self.bounds.size.height
let fontSize = self.fontSize
let lines = CGFloat(calculateMaxLines())
let yDiff = (height - lines * fontSize) / 2 - lines * 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()
}
}
没有什么能阻止您使用通用的CALayer(容器)创建带有CATextLayer子层的CALayer层次结构。
不需要为CATextLayer计算字体大小,只需计算CATextLayer在CALayer中的偏移量,使其垂直居中。如果将文本层的对齐模式设置为居中,并使文本层的宽度与封闭容器相同,则水平居中。
let container = CALayer()
let textLayer = CATextLayer()
// create the layer hierarchy
view.layer.addSublayer(container)
container.addSublayer(textLayer)
// Setup the frame for your container
...
// Calculate the offset of the text layer so that it is centred
let hOffset = (container.frame.size.height - textLayer.frame.size.height) * 0.5
textLayer.frame = CGRect(x:0.0, y: hOffset, width: ..., height: ...)
gbk的代码是可行的。下面是gbk的代码,更新为XCode 8 beta 6。截至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()
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)
第四步完成。
class CenterTextLayer: CATextLayer {
override func draw(in ctx: CGContext) {
#if os(iOS) || os(tvOS)
let multiplier = CGFloat(1)
#elseif os(OSX)
let multiplier = CGFloat(-1)
#endif
let yDiff = (bounds.size.height - ((string as? NSAttributedString)?.size().height ?? fontSize)) / 2 * multiplier
ctx.saveGState()
ctx.translateBy(x: 0.0, y: yDiff)
super.draw(in: ctx)
ctx.restoreGState()
}
}
致谢:
NSString.size(with: Attributes)
. I don't know if it's an improvement over (height-fontSize)/2 - fontSize/10
, but I like to think that it is. Although, in my experience, NSString.size(with: Attributes)
doesn't always return the most appropriate size.invertedYAxis
property. It was useful for my purposes of exporting this CATextLayer
subclass using AVVideoCompositionCoreAnimationTool
. AVFoundation operates in "normal" y axis, and that's why I had to add this property.NSString
. You can use Swift's String
class though, because it automatically casts to NSString
.It ignores CATextLayer.fontSize
property and completely relies on CATextLayer.font
property which MUST be a UIFont
instance.
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()
}
}
yDiff = (height-lines*fontSize)/2 - lines*fontSize/10
。 - Tai Le