我无法理解为什么 SwiftUI 修饰符.stroke()在.trim()Circle 上的行为有所不同,具体取决于您首先放置的内容。
在 Xcode Playground 中,以下代码:
struct ContentView: View {
var body: some View {
Circle()
.trim(from: 0, to: 0.5)
}
}
PlaygroundPage.current.setLiveView(
ContentView()
.padding(10)
.frame(width: 100, height: 100)
)
Run Code Online (Sandbox Code Playgroud)
结果(如预期)如下图所示:
如果我只想要半圆的轮廓,我添加.stroke修改器:
Circle()
.trim(from: 0, to: 0.5)
.stroke() // add this
Run Code Online (Sandbox Code Playgroud)
结果是:
到目前为止,一切都很好。但如果我向后做,它就不会按预期工作。也就是说,如果我首先构建一个圆的轮廓:
Circle()
.stroke()
Run Code Online (Sandbox Code Playgroud)
然后我尝试通过这样做来获得轮廓的下半部分:
Circle()
.stroke()
.trim(from: 0, to: 0.5)
Run Code Online (Sandbox Code Playgroud)
我得到一个(意外的)结果:
不仅如此:如果我更改toto的参数,我会得到以下结果:trim0.25
Circle()
.stroke()
.trim(from: 0, to: 0.25)
Run Code Online (Sandbox Code Playgroud)
我知道,一般来说,SwiftUI 修饰符的顺序很重要,但在这种情况下,我不明白我提供的代码片段的结果。
您调用的方法stroke的实现相当短。其实现本质上是这样的:
struct _StrokedShape<S: Shape>: Shape {\n var s: S\n var style: StrokeStyle\n\n func path(in rect: CGRect) -> Path {\n return s\n .path(in: rect)\n .strokedPath(style)\n // strokedPath is defined on Path.\n }\n}\n\nextension Shape {\n func stroke(lineWidth: CGFloat = 1) -> some Shape {\n return _StrokedShape(s: self, style: .init(lineWidth: lineWidth))\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n值得注意的是,抚摸路径总是会产生闭合路径。
\n该trim方法有一个类似的简短实现,本质上是这样的:
struct _TrimmedShape<S: Shape>: Shape {\n var s: S\n var from: CGFloat\n var to: CGFloat\n\n func path(in rect: CGRect) -> Path {\n return s\n .path(in: rect)\n .trimmedPath(from: from, to: to)\n // trimmedPath is defined on Path.\n }\n}\n\nextension Shape {\n func trim(from: CGFloat, to: CGFloat) -> some Shape {\n return _TrimmedShape(s: self, from: from, to: to)\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n值得注意的是,修剪路径通常会产生开放路径。
\n(我在调试器中逐步完成了这两种方法的实现,以验证它们是否是这样工作的。)
\n现在我们了解了 SwiftUI 如何实现这些修饰符。另一件需要理解的事情是 SwiftUI 如何绘制Shape:它调用Shape\ 的path(in:)方法,然后用前景色(或者,在较旧的操作系统上,前景色)填充路径。如果路径打开,则填充后会自动关闭。默认的前景样式是纯黑色或纯白色(取决于配色方案)。
有了这些知识,我们就可以理解您所看到的结果。
\n您的第一个测试是这段代码:
\n Circle()\n .trim(from: 0, to: 0.5)\nRun Code Online (Sandbox Code Playgroud)\n产生这个开放的半圆路径:
\n\n当填充时,它会闭合以形成闭合的半圆路径:
\n\n您的第二个测试是这段代码:
\nCircle()\n .trim(from: 0, to: 0.5)\n .stroke() // add this\nRun Code Online (Sandbox Code Playgroud)\n默认笔画lineWidth为 1,这使得生成的路径难以理解,因此我将改用以下代码:
Circle()\n .trim(from: 0, to: 0.5)\n .stroke(lineWidth: 5)\nRun Code Online (Sandbox Code Playgroud)\ntrim这会描画由上面的开放半圆 \xe2\x80\x94产生的路径,产生这个闭合的半环形路径:
填充该闭合半环形路径绘制一个半圆。
\n您的第三个测试是这段代码:
\nCircle()\n .stroke()\nRun Code Online (Sandbox Code Playgroud)\n同样,默认值lineWidth1 会使结果难以理解,因此我将改用以下代码:
Circle()\n .stroke(lineWidth: 5)\nRun Code Online (Sandbox Code Playgroud)\n这会绘制一个圆,生成一条包含两个闭合圆形子路径的路径路径:
\n\n从上图中无法看出,但子路径的运行方向相反,因此当使用非零缠绕规则填充时填充时,它看起来像一个圆圈。
\n您的第四个测试是这段代码:
\nCircle()\n .stroke()\n .trim(from: 0, to: 0.5)\nRun Code Online (Sandbox Code Playgroud)\n我将再次使用lineWidth5 来代替:
Circle()\n .stroke(lineWidth: 5)\n .trim(from: 0, to: 0.5)\nRun Code Online (Sandbox Code Playgroud)\n请记住,stroke生成的路径包含两个。如果这些圆具有完全相同的半径,则修剪掉路径的后半部分将恰好修剪掉其中一个圆。但其中一个圆圈比另一个圆圈稍大,并且较小的圆圈位于路径中较早的位置。因此它会修剪掉大圆的大部分,但不是全部。它生成一个包含闭合圆形子路径和微小开弧子路径的路径:
当填充时,它会关闭包含闭合圆形子路径和微小闭合圆弧段子路径的路径:
\n\n当我们把圆变lineWidth小时,圆的大小就会变得更接近。更多的大圆被修剪掉,剩余的弧/线段移近更小的圆。lineWidth当我们在 1 和 5 之间变化时会发生以下情况:
当 alineWidth为 1 时,圆弧段非常细,填充时不会产生明显的颜色,这就是为什么您在图像中根本看不到它。
您的第五次也是最后一次测试是以下代码:
\nCircle()\n .stroke()\n .trim(from: 0, to: 0.25)\nRun Code Online (Sandbox Code Playgroud)\n像往常一样,我将使用lineWidth5 来代替:
Circle()\n .stroke(lineWidth: 5)\n .trim(from: 0, to: 0.25)\nRun Code Online (Sandbox Code Playgroud)\n它产生这个开放的近半圆形路径:
\n\n由于我们要修剪掉描边路径的最后 3/4,因此我们修剪掉了整个大圆和几乎一半的小圆。填充后,它会关闭这条路径:
\n\n1lineWidth会使圆的大小更加接近,因此您可以修剪掉所有较大的圆和几乎恰好一半的较小圆。但如果放大图像,您会发现形状的顶部边缘确实有一个小角度,因为它不是完美的半圆:
| 归档时间: |
|
| 查看次数: |
99 次 |
| 最近记录: |