vertically align text in a CATextLayer?

后端 未结 15 1330
时光说笑
时光说笑 2020-12-10 10:41

I am working on a CATextLayer that I want to use in both Mac and iOS. Can I control the vertical alignment of the text within the layer?

In this particular case, I

相关标签:
15条回答
  • 2020-12-10 11:13

    thank @iamktothed, it works. following is swift 3 version:

    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()
    }
    }
    
    0 讨论(0)
  • 2020-12-10 11:13

    The code for Swift 3, based on code @iamktothed

    If you use an attributed string for setting font properties, than you can use function size() from NSAttributedString to calculate height of string. I think this code also resolve the problems described by @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 讨论(0)
  • 2020-12-10 11:21

    Swift 3 version for regular and attributed strings.

    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()
        }
    }
    
    0 讨论(0)
  • 2020-12-10 11:21

    gbk's code works. below is gbk's code updated for XCode 8 beta 6. Current as of 1 Oct 2016

    Step 1. Subclass CATextLayer. In the code below I've named the subclass "MyCATextLayer" Outside your view controller class copy/paste the below code.

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

    Step 2. Within your view controller class in your ".swift" file, create your CATextLabel. In the code example I've named the subclass "MyDopeCATextLayer."

    let MyDopeCATextLayer: MyCATextLayer = MyCATextLayer()
    

    Step 3. Set your new CATextLayer with desired text/color/bounds/frame.

    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)
    

    Step 4. done

    0 讨论(0)
  • 2020-12-10 11:23

    You need to know where CATextLayer will put the baseline of your text. Once you know that, offset the coordinate system within the layer, i.e. adjust bounds.origin.y by the difference between where the baseline normally sits and where you want it to be, given the metrics of the font.

    CATextLayer is a bit of a black box and finding where the baseline will sit is a bit tricky - see my answer here for iOS - I've no idea what the behaviour is on Mac.

    0 讨论(0)
  • 2020-12-10 11:25

    The correct answer, as you've already found, is here in Objective-C and works for iOS. It works by subclassing the CATextLayer and overriding the drawInContext function.

    However, I've made some improvements to the code, as shown below, using David Hoerl's code as a basis. The changes come solely in recalculating the vertical position of the text represented by the yDiff. I've tested it with my own code.

    Here is the code for Swift users:

    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()
        }
    }
    
    0 讨论(0)
提交回复
热议问题