簡體   English   中英

iOS - 如何在使用自定義 UIBezierPath 繪制的 CAShapeLayer 中設置 CATextLayer?

[英]iOS - How to set a CATextLayer inside a CAShapeLayer which is drawn with a custom UIBezierPath?

我想編寫一個自定義餅圖菜單。 在下面的代碼中,您將看到我如何創建一個包含兩個項目的餅圖菜單。 我的結構如下:我使用矩形 UIBezierPath 和 CAShapeLayer 作為上下文作為我的圓形背景。 在我的圓形背景中,我有一個孩子,里面的小圓圈(也是 UIBezierPath 和 CAShapeLayer)。 我的圓形背景層的其他子項是項目,它們也是使用自定義 UIBezierPath 的 CAShapeLayer(我繪制的項目取決於項目的數量(不同程度等))。 現在我想在每個項目層內添加一個 CATextLayer(“項目 1”、“項目 2”等)。 我的問題是,我不知道如何設置特定項目圖層的框架,以及如何以文本動態位於父項目圖層內的方式添加特定 CATextLayer。 就我而言, CATextLayer 取決於菜單背景層的框架。

電流輸出

func setMenuBackgroundLayer() {
        //Draw a circle background with UIBezierPath for the static pie menu
        let path = UIBezierPath(arcCenter: CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 2), radius: menuRadius, startAngle: CGFloat(0), endAngle: CGFloat(Double.pi * 2), clockwise: true)
    
        menuBackgroundLayer = CAShapeLayer()
        menuBackgroundLayer.path = path.cgPath
        menuBackgroundLayer.fillColor = menuBackgroundLayerColor.cgColor
        menuBackgroundLayer.frame = self.bounds
        menuBackgroundLayer.zPosition = 1
        
        self.layer.addSublayer(menuBackgroundLayer)
        
        //Draw the inner circle (back button)
        let pathInner = UIBezierPath(arcCenter: CGPoint(x: menuBackgroundLayer.frame.size.width / 2, y: menuBackgroundLayer.frame.size.height / 2), radius: innerCircleRadius, startAngle: CGFloat(0), endAngle: CGFloat(Double.pi * 2), clockwise: true)
        
        innerCircleLayer = CAShapeLayer()
        innerCircleLayer.path = pathInner.cgPath
        innerCircleLayer.fillColor = menuBackgroundLayerColor.cgColor
        innerCircleLayer.strokeColor = UIColor.black.cgColor
        innerCircleLayer.lineWidth = 1
        innerCircleLayer.frame = menuBackgroundLayer.frame
        menuBackgroundLayer.addSublayer(innerCircleLayer)
        //Set the inner circle above all other menu items
        innerCircleLayer.zPosition = 100
        
        //Add the arrow image inside the inner circle
        //addBackImage()
    }
func insertMenuItems() {
        //Compare which item has to get inserted and insert it
        if numberOfItems == 1 {
            let path = UIBezierPath(arcCenter: CGPoint(x: menuBackgroundLayer.frame.size.width / 2, y: menuBackgroundLayer.frame.size.height / 2), radius: menuRadius, startAngle: CGFloat(0), endAngle: CGFloat(Double.pi * 2), clockwise: true)
            
            item1Layer = CAShapeLayer()
            item1Layer.path = path.cgPath
            item1Layer.fillColor = menuBackgroundLayerColor.cgColor
            item1Layer.strokeColor = UIColor.black.cgColor
            item1Layer.lineWidth = 1
            item1Layer.frame = menuBackgroundLayer.bounds
            menuBackgroundLayer.addSublayer(item1Layer)
            item1Layer.zPosition = 2
            
            let textLayer = CATextLayer()
            textLayer.string = "ITEM 1"
            textLayer.foregroundColor = UIColor.white.cgColor
            textLayer.font = UIFont(name: "Avenir", size: 15.0)
            textLayer.fontSize = 15.0
            textLayer.alignmentMode = CATextLayerAlignmentMode.center
            textLayer.zPosition = 3
            textLayer.frame = item1Layer.bounds
            textLayer.position = CGPoint(x: item1Layer.position.x, y: item1Layer.position.y + 20.0)
            textLayer.contentsScale = UIScreen.main.scale
            item1Layer.addSublayer(textLayer)
        }
        else if numberOfItems == 2 {
            //Item 1
            let path1 = UIBezierPath()
            path1.move(to: CGPoint(x: menuBackgroundLayer.frame.size.width / 2, y: menuBackgroundLayer.frame.size.height / 2))
            path1.addArc(withCenter: CGPoint(x: menuBackgroundLayer.frame.size.width / 2, y: menuBackgroundLayer.frame.size.height / 2), radius: menuRadius, startAngle: rad2deg(180.0), endAngle: rad2deg(0.0), clockwise: true)
            path1.close()
            item1Layer = CAShapeLayer()
            item1Layer.path = path1.cgPath
            item1Layer.fillColor = menuBackgroundLayerColor.cgColor
            item1Layer.strokeColor = UIColor.black.cgColor
            item1Layer.lineWidth = 1
            item1Layer.frame = menuBackgroundLayer.bounds
            menuBackgroundLayer.addSublayer(item1Layer)
            item1Layer.zPosition = 2
            
            let textLayer1 = CATextLayer()
            textLayer1.string = "ITEM 1"
            textLayer1.foregroundColor = UIColor.white.cgColor
            textLayer1.font = UIFont(name: "Avenir", size: 15.0)
            textLayer1.fontSize = 15.0
            textLayer1.alignmentMode = CATextLayerAlignmentMode.center
            textLayer1.zPosition = 3
            textLayer1.frame = item1Layer.bounds
            textLayer1.position = CGPoint(x: item1Layer.position.x, y: item1Layer.position.y + 20.0)
            textLayer1.contentsScale = UIScreen.main.scale
            item1Layer.addSublayer(textLayer1)
            
            //Item 2
            let path2 = UIBezierPath()
            path2.move(to: CGPoint(x: menuBackgroundLayer.frame.size.width / 2, y: menuBackgroundLayer.frame.size.height / 2))
            path2.addArc(withCenter: CGPoint(x: menuBackgroundLayer.frame.size.width / 2, y: menuBackgroundLayer.frame.size.height / 2), radius: menuRadius, startAngle: rad2deg(0.0), endAngle: rad2deg(180.0), clockwise: true)
            path2.close()
            item2Layer = CAShapeLayer()
            item2Layer.path = path2.cgPath
            item2Layer.fillColor = menuBackgroundLayerColor.cgColor
            item2Layer.strokeColor = UIColor.black.cgColor
            item2Layer.lineWidth = 1
            item2Layer.frame = menuBackgroundLayer.bounds
            menuBackgroundLayer.addSublayer(item2Layer)
            item2Layer.zPosition = 2
            
            let textLayer2 = CATextLayer()
            textLayer2.string = "ITEM 2"
            textLayer2.foregroundColor = UIColor.white.cgColor
            textLayer2.font = UIFont(name: "Avenir", size: 15.0)
            textLayer2.fontSize = 15.0
            textLayer2.alignmentMode = CATextLayerAlignmentMode.center
            textLayer2.zPosition = 3
            textLayer2.frame = item2Layer.bounds
            textLayer2.position = CGPoint(x: item2Layer.position.x, y: item2Layer.position.y + 20.0)
            textLayer2.contentsScale = UIScreen.main.scale
            item2Layer.addSublayer(textLayer2)
        }
and so on...
}

所以,這是一個粗略的原型,它可以完成您需要的東西,但不是很精確。 在此處輸入圖片說明

如果要旋轉文本,可以使用CATransform來實現。 你可以在這里玩代碼: https : //github.com/gatamar/stackoverflow_answers/tree/master/so64348954

或者我可以讓它更精確,如果這幾乎是你需要的。

餅圖菜單代碼:

import Foundation
import UIKit

class HackLinesView: UIView {
    init(frame: CGRect, partsCount parts: Int) {
        super.init(frame: frame)
        backgroundColor = .clear
        
        let side = frame.width/2
        // add lines
        for part in 0..<parts {
            let angle = CGFloat(part)/CGFloat(parts) * 2 * .pi
            let lineLayer = CAShapeLayer()
            lineLayer.backgroundColor = UIColor.black.cgColor
            let path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 1, height: side))
            lineLayer.path = path.cgPath
            lineLayer.transform = CATransform3DMakeRotation(angle, 0, 0, 1)
            layer.addSublayer(lineLayer)
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
    
class PieMenuView: UIView {
    init(frame: CGRect, partsCount parts: Int) {
        assert( abs(frame.width-frame.height) < 0.001)
        super.init(frame: frame)
        setupLayers(parts)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupLayers(_ parts: Int) {
        let side = bounds.width
        let outerRadius = side * 0.5
        let innerRadius = side * 0.2
        
        // add outer circle
        let outerCircleLayer = CAShapeLayer()
        outerCircleLayer.frame = bounds
        outerCircleLayer.cornerRadius = outerRadius
        outerCircleLayer.backgroundColor = UIColor.orange.cgColor
        layer.addSublayer(outerCircleLayer)
        
        // add inner circle
        let innerCircleLayer = CAShapeLayer()
        innerCircleLayer.frame = CGRect(x: side/2-innerRadius, y: side/2-innerRadius, width: innerRadius*2, height: innerRadius*2)
        innerCircleLayer.cornerRadius = innerRadius
        innerCircleLayer.backgroundColor = UIColor.yellow.cgColor
        layer.addSublayer(innerCircleLayer)
        
        let linesView = HackLinesView(frame: CGRect(x: side/2, y: side/2, width: side, height: side), partsCount: parts)
        addSubview(linesView)
        
        // add text
        for part in 0..<parts {
            let angle = CGFloat(part)/CGFloat(parts) * 2 * .pi
            
            let textLayer = CATextLayer()
            textLayer.string = String(format: "%d", part)
            textLayer.foregroundColor = UIColor.blue.cgColor
            
            // calc the center for text layer
            let x1 = side/2
            let y1 = side/2
            let x2 = x1 + cos(angle)*outerRadius
            let y2 = y1 + sin(angle)*outerRadius
            
            let textCenterX = (x1 + x2)/2, textCenterY = (y1 + y2)/2
            let textLayerSide: CGFloat = 50
            
            textLayer.frame = CGRect(x: textCenterX-textLayerSide/2, y: textCenterY-textLayerSide/2, width: textLayerSide, height: textLayerSide)
            
            layer.addSublayer(textLayer)
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM