如果创建的形状没有特定大小,它将自动扩展以占据所有可用空间。例如,这将创建一个填充我们视图的圆,并为其提供40点蓝色边框:
代码语言:swift复制struct ContentView: View {
var body: some View {
Circle()
.stroke(Color.blue, lineWidth: 40)
}
}
仔细观察边框的左右边缘——您注意到边框是怎么被切掉的吗?
您在这里看到的是SwiftUI在形状周围绘制边框的方式的副作用。如果您递给某人一个圆的铅笔轮廓,并要求他们用粗笔在该圆上画线,他们将绘制出该圆的精确线——大约一半的笔在该线的内部,一半在该线的外部。这就是SwiftUI为我们所做的,但是当形状到达屏幕边缘时,则意味着边框的外部最终超出了屏幕边缘。
现在尝试改用这个圆:
代码语言:swift复制Circle()
.strokeBorder(Color.blue, lineWidth: 40)
这将stroke()
更改为strokeBorder()
,现在我们得到了更好的结果:我们的所有边框都是可见的,因为Swift在圆的内部绘制而不是将圆作为绘制的中心。
之前我们建立了一个如下的弧形:
代码语言:swift复制struct Arc: Shape {
var startAngle: Angle
var endAngle: Angle
var clockwise: Bool
func path(in rect: CGRect) -> Path {
let rotationAdjustment = Angle.degrees(90)
let modifiedStart = startAngle - rotationAdjustment
let modifiedEnd = endAngle - rotationAdjustment
var path = Path()
path.addArc(center: CGPoint(x: rect.midX, y: rect.midY), radius: rect.width / 2, startAngle: modifiedStart, endAngle: modifiedEnd, clockwise: !clockwise)
return path
}
}
就像Circle
一样,它会自动占用所有可用空间。但是,这种代码不起作用:
Arc(startAngle: .degrees(-90), endAngle: .degrees(90), clockwise: true)
.strokeBorder(Color.blue, lineWidth: 40)
如果您打开Xcode的错误消息,则会看到它显示“Value of type 'Arc' has no member 'strokeBorder'''——也就是说,arc
中不存在strokeBorder()
修饰符。
SwiftUI的Circle
和我们的Arc
之间有一个微小但重要的区别:两者均符合Shape
协议,但Circle
也符合名为InsettableShape
的第二种协议。该形状可以嵌入(向内减小)一定距离以产生另一个形状。它产生的插图形状可以是任何其他类型的插图形状,但实际上,它应该是一个有相同形状的较小的矩形。
为了使Arc
符合InsettableShape
,我们需要为其添加一个额外的方法:inset(by :)
。这将获得插入量(笔画的线宽的一半),并应返回一种新的可插入形状——在我们的实例中,这意味着我们应该创建一个插入弧型。问题是我们不知道圆弧的实际大小,因为尚未调用path(in :)
。
事实证明,解决方案非常简单:如果我们为Arc
形状提供一个默认为0的新insetAmount
属性,则只要调用inset(by :)
就可以添加该属性。添加到inset
允许我们在需要时多次调用inset(by :)
,例如,如果我们想手动调用一次,则使用strokeBorder()
。
首先,将此新属性添加到Arc
:
var insetAmount: CGFloat = 0
现在给它这个inset(by :)
方法:
func inset(by amount: CGFloat) -> some InsettableShape {
var arc = self
arc.insetAmount = amount
return arc
}
传入的数量参数应应用于所有边缘,这在圆弧的情况下意味着我们应使用它减小绘制半径。因此,将path(in :)
内部的addArc()
调用更改为:
path.addArc(center: CGPoint(x: rect.midX, y: rect.midY), radius: rect.width / 2 - insetAmount, startAngle: modifiedStart, endAngle: modifiedEnd, clockwise: !clockwise)
通过该更改,我们现在可以使Arc
符合InsettableShape
,如下所示:
struct Arc: InsettableShape {
注意:InsettableShape
实际上是基于Shape
构建的,因此无需在其中添加两者。
译自 Adding strokeBorder() support with InsettableShape