我如何在 iOS Swift4 中创建这个圆形?
How I can create this circular shape in iOS Swift4?
我必须在 UIView 中创建如下图所示的形状。但我不知道如何绘制这个形状。我正在尝试使用 UIBezierPath 路径和 CAShapeLayer 绘制此形状,圆线绘制成功,但我无法绘制圆点和圆形填充颜色。任何人都可以建议我如何使用任何库或 UIBezierPath 实现这种类型形状。
这是我尝试绘制这个圆形的代码。
class ViewController: UIViewController {
var firstButton = UIButton()
var mylabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
self.creatingLayerWithInformation()
}
func creatingLayerWithInformation(){
let safeAreaHeight = self.view.safeAreaInsets.top
let navBarHeight = self.navigationController?.navigationBar.frame.height
self.addLayer(isClockWise: true, radius: self.view.frame.width * 0.72, xPoint: 0, yPoint: navBarHeight!, layerColor: UIColor.green, fillcolor: .clear)
self.addLayer(isClockWise: true, radius: self.view.frame.width * 0.72, xPoint: self.view.frame.width, yPoint: self.view.frame.height - 150, layerColor: UIColor.blue, fillcolor: .clear)
let aa = self.view.frame.width * 0.72
self.addLayer(isClockWise: true, radius: 10, xPoint: 0+aa, yPoint: navBarHeight!+5, layerColor: UIColor.blue, fillcolor: .clear)
self.addLayer(isClockWise: true, radius: 10, xPoint: 0+15, yPoint: navBarHeight!+aa, layerColor: UIColor.blue, fillcolor: .clear)
}
func addLayer(isClockWise: Bool, radius: CGFloat, xPoint: CGFloat, yPoint: CGFloat, layerColor: UIColor, fillcolor: UIColor) {
let pi = CGFloat(Float.pi)
let start:CGFloat = 0.0
let end :CGFloat = 20
// circlecurve
let path: UIBezierPath = UIBezierPath();
path.addArc(
withCenter: CGPoint(x:xPoint, y:yPoint),
radius: (radius),
startAngle: start,
endAngle: end,
clockwise: isClockWise
)
let layer = CAShapeLayer()
layer.lineWidth = 3
layer.fillColor = fillcolor.cgColor
layer.strokeColor = layerColor.cgColor
layer.path = path.cgPath
self.view.layer.addSublayer(layer)
}}
但我得到以下结果。
请建议我如何实现这种形状。如果我做错了什么,请纠正我。如果有任何图书馆存在,那么也请提出建议。请给我一些解决方案。
提前感谢大家。
以最简单的形式思考问题。想象一条直线,中间叠加了一个小圆圈。
让我们把思考分为三个层次:
需要显示的背景
保持直线的图层
持有小圆圈的图层
计算小圆图层相对直线图层的位置
将小圆圈图层放置在该位置。好的,但是直线显示出来了。
回到直线图层。给它一个掩码。在小圆层的准确位置用透明圆构建该蒙版。
现在遮罩 "punches a hole" 通过直线 — 正好在圆覆盖它的地方。这样我们就好像透过圆圈看到了背景,因为直线正好在那个地方不见了。
在现实生活中会有多个圆层,蒙版会有多个透明圆圈,直线会是曲线,但是一旦 hole-punching 解决了,这就是小问题了。
有很多方法可以实现这种效果,但一个简单的解决方案是不要将大圆圈绘制为单个圆弧,而是将其绘制为一系列在较小圆圈的边缘开始和停止的圆弧。为此,您需要知道内圈的偏移量。做一点三角学,你可以计算为:
let angleOffset = asin(innerRadius / 2 / mainRadius) * 2
因此:
let path = UIBezierPath()
path.move(to: point(from: arcCenter, radius: mainRadius, angle: startAngle))
let anglePerChoice = (endAngle - startAngle) / CGFloat(choices.count)
let angleOffset = asin(innerRadius / 2 / mainRadius) * 2
var from = startAngle
for index in 0 ..< choices.count {
var to = from + anglePerChoice / 2 - angleOffset
path.addArc(withCenter: arcCenter, radius: mainRadius, startAngle: from, endAngle: to, clockwise: true)
to = from + anglePerChoice
from += anglePerChoice / 2 + angleOffset
path.move(to: point(from: arcCenter, radius: mainRadius, angle: from))
path.addArc(withCenter: arcCenter, radius: mainRadius, startAngle: from, endAngle: to, clockwise: true)
from = to
}
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = strokeColor.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = lineWidth
layer.addSublayer(shapeLayer)
其中:
func point(from point: CGPoint, radius: CGFloat, angle: CGFloat) -> CGPoint {
return CGPoint(x: point.x + radius * cos(angle),
y: point.y + radius * sin(angle))
}
产生:
因此,当您添加内圈时:
虽然上面很简单,但也有局限性。具体来说,如果大弧的 lineWidth
与小圆的相比真的很宽,则单独的大弧中的中断将不会与小圆的边缘很好地对齐。例如。假设小圆圈的半径为 22 点,但大圆弧的行程相对较宽,例如36分。
如果你有这种情况(不是你的情况,而是为了未来的读者),另一种方法,如 matt 建议的那样,是将大弧绘制为单个笔划,然后使用小圆圈的路径。
所以,假设您有:
单个CAShapeLayer
,称为mainArcLayer
,用于大弧;和
所有小圆圈的 UIBezierPath
数组,称为 smallCirclePaths
。
然后您可以在 UIView
子类的 layoutSubviews
中创建掩码(或 UIViewController
子类中的 viewDidLayoutSubviews
),如下所示:
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath(rect: bounds)
smallCirclePaths.forEach { path.append([=13=]) }
let mask = CAShapeLayer()
mask.fillColor = UIColor.white.cgColor
mask.strokeColor = UIColor.clear.cgColor
mask.lineWidth = 0
mask.path = path.cgPath
mask.fillRule = .evenOdd
mainArcLayer.mask = mask
}
产生:
这是针对此问题的稍微更通用的解决方案。
我必须在 UIView 中创建如下图所示的形状。但我不知道如何绘制这个形状。我正在尝试使用 UIBezierPath 路径和 CAShapeLayer 绘制此形状,圆线绘制成功,但我无法绘制圆点和圆形填充颜色。任何人都可以建议我如何使用任何库或 UIBezierPath 实现这种类型形状。
这是我尝试绘制这个圆形的代码。
class ViewController: UIViewController {
var firstButton = UIButton()
var mylabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
self.creatingLayerWithInformation()
}
func creatingLayerWithInformation(){
let safeAreaHeight = self.view.safeAreaInsets.top
let navBarHeight = self.navigationController?.navigationBar.frame.height
self.addLayer(isClockWise: true, radius: self.view.frame.width * 0.72, xPoint: 0, yPoint: navBarHeight!, layerColor: UIColor.green, fillcolor: .clear)
self.addLayer(isClockWise: true, radius: self.view.frame.width * 0.72, xPoint: self.view.frame.width, yPoint: self.view.frame.height - 150, layerColor: UIColor.blue, fillcolor: .clear)
let aa = self.view.frame.width * 0.72
self.addLayer(isClockWise: true, radius: 10, xPoint: 0+aa, yPoint: navBarHeight!+5, layerColor: UIColor.blue, fillcolor: .clear)
self.addLayer(isClockWise: true, radius: 10, xPoint: 0+15, yPoint: navBarHeight!+aa, layerColor: UIColor.blue, fillcolor: .clear)
}
func addLayer(isClockWise: Bool, radius: CGFloat, xPoint: CGFloat, yPoint: CGFloat, layerColor: UIColor, fillcolor: UIColor) {
let pi = CGFloat(Float.pi)
let start:CGFloat = 0.0
let end :CGFloat = 20
// circlecurve
let path: UIBezierPath = UIBezierPath();
path.addArc(
withCenter: CGPoint(x:xPoint, y:yPoint),
radius: (radius),
startAngle: start,
endAngle: end,
clockwise: isClockWise
)
let layer = CAShapeLayer()
layer.lineWidth = 3
layer.fillColor = fillcolor.cgColor
layer.strokeColor = layerColor.cgColor
layer.path = path.cgPath
self.view.layer.addSublayer(layer)
}}
但我得到以下结果。
请建议我如何实现这种形状。如果我做错了什么,请纠正我。如果有任何图书馆存在,那么也请提出建议。请给我一些解决方案。
提前感谢大家。
以最简单的形式思考问题。想象一条直线,中间叠加了一个小圆圈。
让我们把思考分为三个层次:
需要显示的背景
保持直线的图层
持有小圆圈的图层
计算小圆图层相对直线图层的位置
将小圆圈图层放置在该位置。好的,但是直线显示出来了。
回到直线图层。给它一个掩码。在小圆层的准确位置用透明圆构建该蒙版。
现在遮罩 "punches a hole" 通过直线 — 正好在圆覆盖它的地方。这样我们就好像透过圆圈看到了背景,因为直线正好在那个地方不见了。
在现实生活中会有多个圆层,蒙版会有多个透明圆圈,直线会是曲线,但是一旦 hole-punching 解决了,这就是小问题了。
有很多方法可以实现这种效果,但一个简单的解决方案是不要将大圆圈绘制为单个圆弧,而是将其绘制为一系列在较小圆圈的边缘开始和停止的圆弧。为此,您需要知道内圈的偏移量。做一点三角学,你可以计算为:
let angleOffset = asin(innerRadius / 2 / mainRadius) * 2
因此:
let path = UIBezierPath()
path.move(to: point(from: arcCenter, radius: mainRadius, angle: startAngle))
let anglePerChoice = (endAngle - startAngle) / CGFloat(choices.count)
let angleOffset = asin(innerRadius / 2 / mainRadius) * 2
var from = startAngle
for index in 0 ..< choices.count {
var to = from + anglePerChoice / 2 - angleOffset
path.addArc(withCenter: arcCenter, radius: mainRadius, startAngle: from, endAngle: to, clockwise: true)
to = from + anglePerChoice
from += anglePerChoice / 2 + angleOffset
path.move(to: point(from: arcCenter, radius: mainRadius, angle: from))
path.addArc(withCenter: arcCenter, radius: mainRadius, startAngle: from, endAngle: to, clockwise: true)
from = to
}
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = strokeColor.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = lineWidth
layer.addSublayer(shapeLayer)
其中:
func point(from point: CGPoint, radius: CGFloat, angle: CGFloat) -> CGPoint {
return CGPoint(x: point.x + radius * cos(angle),
y: point.y + radius * sin(angle))
}
产生:
因此,当您添加内圈时:
虽然上面很简单,但也有局限性。具体来说,如果大弧的 lineWidth
与小圆的相比真的很宽,则单独的大弧中的中断将不会与小圆的边缘很好地对齐。例如。假设小圆圈的半径为 22 点,但大圆弧的行程相对较宽,例如36分。
如果你有这种情况(不是你的情况,而是为了未来的读者),另一种方法,如 matt 建议的那样,是将大弧绘制为单个笔划,然后使用小圆圈的路径。
所以,假设您有:
单个
CAShapeLayer
,称为mainArcLayer
,用于大弧;和所有小圆圈的
UIBezierPath
数组,称为smallCirclePaths
。
然后您可以在 UIView
子类的 layoutSubviews
中创建掩码(或 UIViewController
子类中的 viewDidLayoutSubviews
),如下所示:
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath(rect: bounds)
smallCirclePaths.forEach { path.append([=13=]) }
let mask = CAShapeLayer()
mask.fillColor = UIColor.white.cgColor
mask.strokeColor = UIColor.clear.cgColor
mask.lineWidth = 0
mask.path = path.cgPath
mask.fillRule = .evenOdd
mainArcLayer.mask = mask
}
产生:
这是针对此问题的稍微更通用的解决方案。