SwiftUI:将更新发布到自定义形状



在下面的SwiftUI代码中,我有三个主要组件:

TestView-保存TestClass实例,显示MyShape,并随时更新TestClass实例的计数。

TestClass-一个用于存储count的简单类,该类在更改时发布。

MyShape-一个简单的形状,其位置(应该(随着TestViewTestClass实例的计数的变化而变化。

问题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仅在ViewShape的属性更改时更新。

目前,MyShape在第一次渲染后从未更改其属性,因为TestClass是一个引用类型(即本例中的class(——SwiftUI只看到属性是相同的,即使TestClass上的一个内部属性发生了更改,也不必渲染新版本的MyShape

View中,我们可以使用@ObservedObject属性包装器,以便View知道在ObservableObject@Published属性更改时进行更新(就像在TestView中所做的那样(,但该包装器在Shape中不可用。

这里最简单的解决方案是通过count而不是countercountInt(即值类型(,这意味着无论何时更改,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将更新。当然,这是假设您的真实世界模型更复杂,并且具有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,以便在开始时更容易点击视图

最新更新