В следующем коде SwiftUI у меня есть 3 основных компонента:
TestView
— содержит экземпляр TestClass
, отображает MyShape
и обновляет количество экземпляров TestClass
при касании.
TestClass
— простой класс для хранения count
, который публикуется при изменении.
MyShape
— простая фигура, положение которой (должно) меняться по мере изменения количества экземпляров TestClass
в TestView
.
Проблема заключается в том, что позиция 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 просто видит, что свойства идентичны, и не утруждает себя рендерингом новой версии MyShape
, хотя одно из внутренних свойств TestClass
изменилось.
Внутри 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
будет обновляться. Это, конечно, при условии, что ваша реальная модель более сложна и имеет более одного свойства, которое понадобится 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
, чтобы было легче нажимать на вид в начале.