iOS - 如何在使用自定义 UIBezierPath 绘制的 CAShapeLayer 中设置 CATextLayer?
Posted
技术标签:
【中文标题】iOS - 如何在使用自定义 UIBezierPath 绘制的 CAShapeLayer 中设置 CATextLayer?【英文标题】:iOS - How to set a CATextLayer inside a CAShapeLayer which is drawn with a custom UIBezierPath? 【发布时间】:2020-10-14 07:56:58 【问题描述】:我想编写一个自定义饼形菜单。在下面的代码中,您会看到我如何创建一个包含两个项目的饼状菜单。我的结构如下:我使用矩形 UIBezierPath 和 CAShapeLayer 作为上下文作为我的圆形背景。在我的圆形背景中,我有一个孩子,内部的小圆圈(也是带有 CAShapeLayer 的 UIBezierPath)。我的圆形背景层的其他孩子是项目,它们也是一个使用自定义 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...
【问题讨论】:
您能否举例说明您的饼状菜单包含 3 个项目(包括 Item1、Item2、Item3 以及它们之间的所有行)的外观? ***.com/questions/64333630/… @Olha 第一张图片展示了我的想法。所以我想为每个饼片添加一个 CATextLayer 在特定的饼片内 您是否正在尝试做这样的事情? ***.com/a/62269780/6257435 【参考方案1】:所以,这里有一个粗略的原型,可以满足您的需要,但不是很精确。
如果你想旋转文字,这可以通过CATransform
来实现。
你可以在这里玩代码:https://github.com/gatamar/***_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)
【讨论】:
我做的有点不同(我计算了每个文本项在整个框架内的位置)但非常感谢您的回答:D以上是关于iOS - 如何在使用自定义 UIBezierPath 绘制的 CAShapeLayer 中设置 CATextLayer?的主要内容,如果未能解决你的问题,请参考以下文章