SwiftUI:发布对自定义形状的更新
SwiftUI: Publish updates to custom shape
在下面的 SwiftUI 代码中,我有 3 个主要组件:
TestView
- 持有一个 TestClass
实例,显示 MyShape
,并在点击时更新 TestClass
实例的计数。
TestClass
- 一个简单的 class 来存储 count
,它在更改时发布。
MyShape
- 一个简单的形状,其位置(应该)随着 TestView
中 TestClass
实例的计数变化而变化。
问题是计数更新时MyShape
的位置没有改变。
我知道计数正在更新,因为如果我在文本视图中显示计数,它会在点击时发生变化,因此当计数发生变化时,形状不会随着计数变化而更新。为什么形状没有更新/为什么发布者没有达到形状?
struct TestView: View {
@ObservedObject var counter: TestClass = TestClass()
var body: some View {
ZStack {
MyShape(counter: counter)
.stroke(Color.red, lineWidth: 50)
.onTapGesture {
counter.count += 1
}
}
}
}
class TestClass: ObservableObject {
@Published var count = 0
}
struct MyShape: Shape {
var counter: TestClass
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: counter.count, y: counter.count))
return path
}
}
SwiftUI 仅在其属性更改时更新 View
或 Shape
。
现在,MyShape
在第一次渲染后永远不会更改其属性,因为 TestClass
是一个引用类型(即在这种情况下为 class
)——SwiftUI 只看到属性是相同的,即使 TestClass
的内部属性之一已更改,也不会费心呈现 MyShape
的新版本。
在 View
中,我们可以使用 @ObservedObject
属性 包装器,以便 View
知道在 ObservableObject
的 @Published
属性更改(就像您在 TestView
中所做的那样),但该包装器在 Shape
.
中不可用
此处最简单的解决方案是传递 count
而不是 counter
。 count
是一个 Int
(即值类型),这意味着每当它发生变化时,SwiftUI 就会知道更新 Shape
:
struct TestView: View {
@ObservedObject var counter: TestClass = TestClass()
var body: some View {
ZStack {
MyShape(count: counter.count)
.stroke(Color.red, lineWidth: 50)
.onTapGesture {
counter.count += 1
}
}
}
}
class TestClass: ObservableObject {
@Published var count = 10
}
struct MyShape: Shape {
var count: Int
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: count, y: count))
return path
}
}
另一个可能更适合 SwiftUI 原则的替代解决方案是将您的模型更改为 struct
。然后,可以将模型传递给 Shape
,由于它是值类型,因此 Shape
将更新。当然,这是假设您的 real-world 模型更复杂并且具有 Shape
需要的多个 属性 —— 否则,保持最初的解决方案,即只公开它的内容需求(即 Int
)可能是更好的需求。
struct TestView: View {
@State var counter: TestStruct = TestStruct()
var body: some View {
ZStack {
MyShape(counter: counter)
.stroke(Color.red, lineWidth: 50)
.onTapGesture {
counter.count += 1
}
}
}
}
struct TestStruct {
var count = 10
}
struct MyShape: Shape {
var counter: TestStruct
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: counter.count, y: counter.count))
return path
}
}
注意:我更新了初始的count
,这样一开始点击视图就更容易了
在下面的 SwiftUI 代码中,我有 3 个主要组件:
TestView
- 持有一个 TestClass
实例,显示 MyShape
,并在点击时更新 TestClass
实例的计数。
TestClass
- 一个简单的 class 来存储 count
,它在更改时发布。
MyShape
- 一个简单的形状,其位置(应该)随着 TestView
中 TestClass
实例的计数变化而变化。
问题是计数更新时MyShape
的位置没有改变。
我知道计数正在更新,因为如果我在文本视图中显示计数,它会在点击时发生变化,因此当计数发生变化时,形状不会随着计数变化而更新。为什么形状没有更新/为什么发布者没有达到形状?
struct TestView: View {
@ObservedObject var counter: TestClass = TestClass()
var body: some View {
ZStack {
MyShape(counter: counter)
.stroke(Color.red, lineWidth: 50)
.onTapGesture {
counter.count += 1
}
}
}
}
class TestClass: ObservableObject {
@Published var count = 0
}
struct MyShape: Shape {
var counter: TestClass
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: counter.count, y: counter.count))
return path
}
}
SwiftUI 仅在其属性更改时更新 View
或 Shape
。
现在,MyShape
在第一次渲染后永远不会更改其属性,因为 TestClass
是一个引用类型(即在这种情况下为 class
)——SwiftUI 只看到属性是相同的,即使 TestClass
的内部属性之一已更改,也不会费心呈现 MyShape
的新版本。
在 View
中,我们可以使用 @ObservedObject
属性 包装器,以便 View
知道在 ObservableObject
的 @Published
属性更改(就像您在 TestView
中所做的那样),但该包装器在 Shape
.
此处最简单的解决方案是传递 count
而不是 counter
。 count
是一个 Int
(即值类型),这意味着每当它发生变化时,SwiftUI 就会知道更新 Shape
:
struct TestView: View {
@ObservedObject var counter: TestClass = TestClass()
var body: some View {
ZStack {
MyShape(count: counter.count)
.stroke(Color.red, lineWidth: 50)
.onTapGesture {
counter.count += 1
}
}
}
}
class TestClass: ObservableObject {
@Published var count = 10
}
struct MyShape: Shape {
var count: Int
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: count, y: count))
return path
}
}
另一个可能更适合 SwiftUI 原则的替代解决方案是将您的模型更改为 struct
。然后,可以将模型传递给 Shape
,由于它是值类型,因此 Shape
将更新。当然,这是假设您的 real-world 模型更复杂并且具有 Shape
需要的多个 属性 —— 否则,保持最初的解决方案,即只公开它的内容需求(即 Int
)可能是更好的需求。
struct TestView: View {
@State var counter: TestStruct = TestStruct()
var body: some View {
ZStack {
MyShape(counter: counter)
.stroke(Color.red, lineWidth: 50)
.onTapGesture {
counter.count += 1
}
}
}
}
struct TestStruct {
var count = 10
}
struct MyShape: Shape {
var counter: TestStruct
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: counter.count, y: counter.count))
return path
}
}
注意:我更新了初始的count
,这样一开始点击视图就更容易了