我试图制作两个不同直径的圆形并填充较小的圆形。
let path = UIBezierPath(ovalIn: CGRect(x: position.x - diameter / 2, y: position.y - diameter / 2, width: diameter, height: diameter))
let path2 = UIBezierPath(ovalIn: CGRect(x: position.x - diameter / 2 + 2, y: position.y - diameter / 2 + 2, width: diameter - 2, height: diameter - 2))
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = color.cgColor
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.lineWidth = lineWidth
let shapeLayer2 = CAShapeLayer()
shapeLayer.path = path2.cgPath
shapeLayer.strokeColor = color.cgColor
shapeLayer.fillColor = isFilled ? color.cgColor : UIColor.white.cgColor
shapeLayer.lineWidth = lineWidth
view.layer.addSublayer(shapeLayer)
view.layer.addSublayer(shapeLayer2)
答案 0 :(得分:2)
您不需要为此的图层。只需使用UIBezierPath
s:
override func draw(_ rect: CGRect) {
// this is the line width of the outer circle
let outerLineWidth = CGFloat(3) // adjust this however you like
// if you didn't create a separate view for these concentric circles,
// which I strongly recommend you do, replace "bounds" with the desired frame
let outerCircle = UIBezierPath(ovalIn: bounds.insetBy(dx: outerLineWidth / 2, dy: outerLineWidth / 2))
UIColor.white.setStroke()
outerCircle.lineWidth = outerLineWidth
outerCircle.stroke()
// this is the distance between the outer and inner circle
let inset = CGFloat(5); // you can adjust this too
let innerCircle = UIBezierPath(ovalIn: bounds.insetBy(dx: inset, dy: inset))
UIColor.white.setFill()
innerCircle.fill()
}
如您所见,您无需手动计算帧,只需使用insetBy(dx:dy:)
方法即可。
答案 1 :(得分:2)
这是使用图层的确切方法。
使用图层而不是绘制图层确实有很多优点。
(通过“绘制”,我的意思是在draw#rect
中绘制它。)
从动画开始比较容易;而且更可重用;而且您不必担心“如果视图移动/重塑我应该重画什么”的困难。一切都是自动的。
因此,在制作图层时,这是四个主要工作:
在iOS中,当您制作图层时,您(A)制作(几乎可以肯定是 lazy var )和(B)在布局时 >,当然,您设置框架的大小。
这是在iOS中使用图层的两个基本步骤。
请注意,您不要在布局时制作它们,并且不要在开机时设置边框。您只能在启动时制作它们,并且只能在布局时设置框架的大小。
我在情节提要中制作了一个UIView,“ Thingy”,并使用约束将其定位为100x100。
我将背景色设为蓝色,以便您可以看到发生了什么。 (您可能希望将其弄清楚。)
它在应用程序中:
我们将需要环的厚度和间隙:
class Thingy: UIView {
var thicknessOfOuterRing: CGFloat = 20.0 {
didSet{ setNeedsLayout() }
}
var thicknessOfTheGap: CGFloat = 20.0 {
didSet{ setNeedsLayout() }
}
要理解的一个关键点是,更改这些值时,您将不得不调整所有内容的大小。对吧?
这意味着您可以按照通常的方式将“ didSet .. setNeedsLayout”添加到这些属性中。
(类似地,如果您想即时更改颜色,则也可以对颜色进行更改。想要“更改”的任何内容,都需要“ didSet .. setNeedsLayout “模式。)
我们将始终需要两个简单的计算,即半厚度和它们的组合脂肪。只需使用计算变量即可,从而大大简化您的代码。
var halfT: CGFloat { return thicknessOfOuterRing / 2.0 }
var both: CGFloat { return thicknessOfOuterRing + thicknessOfTheGap }
好!有两层。环将是一个形状层,但中间的斑点可以只是一个层:
private lazy var outerBand: CAShapeLayer = {
let l = CAShapeLayer()
layer.addSublayer(l)
return l
}()
private lazy var centralBlob: CALayer = {
let l = CALayer()
layer.addSublayer(l)
return l
}()
现在,您必须在布局时设置其框架:
override func layoutSubviews() {
super.layoutSubviews()
outerBand.frame = bounds
centralBlob.frame = bounds
}
(A)在布局时制作图层(几乎可以肯定是带有惰性变量)和(B),当然,您可以设置框架的大小。
从不改变的图层质量是什么?
在惰性变量中设置 那些“不变的”质量:
private lazy var outerBand: CAShapeLayer = {
let l = CAShapeLayer()
l.fillColor = UIColor.clear.cgColor
l.strokeColor = UIColor.black.cgColor
layer.addSublayer(l)
return l
}()
private lazy var centralBlob: CALayer = {
let l = CALayer()
l.backgroundColor = UIColor.black.cgColor
layer.addSublayer(l)
return l
}()
再一次,从不改变的所有内容,将其推送到惰性变量中。
下一步...
关于中央斑点。在iOS中使用正方形CALayer
制作硬币形状非常容易,您只需执行以下操作:
someCALayer.cornerRadius = someCALayer.bounds.width / 2.0
请注意,您可能会和某些程序员一样,实际上使用形状图层,然后去制作圆形形状的麻烦等等。但是只使用普通图层并简单地设置cornerRadius即可。
其次是关于内部斑点。请注意,它只是小于整个单位。换句话说,内部斑点的框架必须缩小一定程度。
要实现此目的,请在iOS中使用关键的inset(by: UIEdgeInsets
调用。经常使用!
inset(by: UIEdgeInsets
。这是一个重要的小技巧。我真的鼓励您使用inset(by: UIEdgeInsets( ...
来写出“顶部,左侧,底部,右侧”
另一种变化insetBy#dx#dy
容易引起混乱;尚不清楚您是要加倍数量还是要加倍数量。
我个人建议使用“长格式” inset(by: UIEdgeInsets( ...
,因为这样 绝对毫无疑问 。
所以我们现在完全完成了内点:
override func layoutSubviews() {
super.layoutSubviews()
outerBand.frame = bounds
outerBand.lineWidth = thicknessOfOuterRing
centralBlob.frame =
bounds.inset(by: UIEdgeInsets(top: both, left: both, bottom: both, right: both))
centralBlob.cornerRadius = centralBlob.bounds.width / 2.0
}
最后,这是您问题的核心:
外圈是使用形状层而非普通层制成的。
形状层由路径定义。
这是在iOS中使用形状图层和路径的关键所在的“一个令人惊讶的技巧”:
根据我的经验,这使程序员从其他平台迁移到iOS时感到非常困惑,但这就是失败的原因。
所以让我们创建一个计算变量,为我们创建路径。
请注意,您一定会使用方便的inset(by: UIEdgeInsets( ...
通话。
别忘了在调用此方法时,您应该已经正确设置所有涉及的帧。
最后Apple友好地给了我们一个方便的UIBezierPath#ovalIn
电话,这样您就不必傻乎乎地用弧线和Pi了!
就这么简单...
var circlePathForCurrentLayout: UIBezierPath {
var b = bounds
b = b.inset(by: UIEdgeInsets(top: halfT, left: halfT, bottom: halfT, right: halfT))
let p = UIBezierPath(ovalIn: b)
return p
}
然后,正如它用粗体字母表示的那样,在iOS 中,您意外地在layoutSubviews 中设置了形状层的实际路径。
override func layoutSubviews() {
super.layoutSubviews()
outerBand.frame = bounds
outerBand.lineWidth = thicknessOfOuterRing
outerBand.path = circlePathForCurrentLayout.cgPath
centralBlob.frame =
bounds.inset(by: UIEdgeInsets(top: both, left: both, bottom: both, right: both))
centralBlob.cornerRadius = centralBlob.bounds.width / 2.0
}
回顾:
这就是整个过程:
// Thingy.swift
// Created for SO on 11/3/19.
import UIKit
class Thingy: UIView {
var thicknessOfOuterRing: CGFloat = 20.0 {
didSet{ setNeedsLayout() }
}
var thicknessOfTheGap: CGFloat = 20.0 {
didSet{ setNeedsLayout() }
}
var halfT: CGFloat { return thicknessOfOuterRing / 2.0 }
var both: CGFloat { return thicknessOfOuterRing + thicknessOfTheGap }
var circlePathForCurrentLayout: UIBezierPath {
var b = bounds
b = b.inset(by:
UIEdgeInsets(top: halfT, left: halfT, bottom: halfT, right: halfT))
let p = UIBezierPath(ovalIn: b)
return p
}
private lazy var outerBand: CAShapeLayer = {
let l = CAShapeLayer()
l.fillColor = UIColor.clear.cgColor
l.strokeColor = UIColor.black.cgColor
layer.addSublayer(l)
return l
}()
private lazy var centralBlob: CALayer = {
let l = CALayer()
l.backgroundColor = UIColor.black.cgColor
layer.addSublayer(l)
return l
}()
override func layoutSubviews() {
super.layoutSubviews()
outerBand.frame = bounds
outerBand.lineWidth = thicknessOfOuterRing
outerBand.path = circlePathForCurrentLayout.cgPath
centralBlob.frame = bounds.inset(by:
UIEdgeInsets(top: both, left: both, bottom: both, right: both))
centralBlob.cornerRadius = centralBlob.bounds.width / 2.0
}
}
下一步:
如果您要挑战在中间放置图片的挑战:link
如果想,请遮掩阴影:link
正在寻找渐变? link
部分弧:link