Я пытаюсь использовать свойства переменных @State var и @Binding var в процессе инициализации моего представления, но я не совсем понимаю, является ли способ, которым я это делаю, обычным или «законным» способом, или я просто не могу сделайте это «законным» способом, потому что я не понимаю, как работают @State var и @Binding var.
struct EditPrView: View {
@State var name: String
@State var nameCount: Int = 0 // Some value just to initalise the variable
var body: some View {
VStack {
Text(name)
Text(String(nameCount))
}.onAppear(perform: { // Do the "actual" assigment here
nameCount = name.count
})
}
}
Это работает хорошо (я еще не сталкивался с проблемами), но кажется «хакерским», и я чувствую, что это можно сделать на этапе инициализации представления.
struct EditPrView: View {
@State var name: String
@State var nameCount: Int = name.count // This will throw an error
var body: some View {
VStack {
Text(name)
Text(String(nameCount))
}
}
}
Это, очевидно, не работает, и вы сразу же получаете ошибку времени компиляции.
class A {
var a: Int = 0
}
class B: A {
var b: Int = 5
}
struct EditPrView: View {
@State private var obj: A?
@State private var count: Int
init(obj: A?) {
self.obj = obj
if obj is B {
self.count = 2
} else {
self.count = 1
}
}
var body: some View {
VStack {
Text("foo-bar")
Text(String(count))
}
}
}
#Preview {
var b: A? = B()
return EditPrView(obj: b)
}
Это отлично работает, пока я использую только @State в представлении.
class A {
var a: Int = 0
}
class B: A {
var b: Int = 5
}
struct EditPrView: View {
@Binding private var obj: A?
@State private var count: Int
init(obj: Binding<A?>) {
self._obj = obj
self.count = 0
if self.obj is B {
self.count = 2
} else {
self.count = 1
}
}
var body: some View {
VStack {
Text("foo-bar")
Text(String(count))
}
}
}
#Preview {
@State var b: A? = B()
return EditPrView(obj: $b)
}
Но затем я пытаюсь добавить @Binding var вот так, и похоже, что он даже не доходит до оператора else, он просто устанавливает self.count в 0, а затем продолжает отображать представление, даже если экземпляр явно относится к типу B.
Поскольку мне нужен один способ инициализации представления, это заставило меня придерживаться .onAppear(perform: ) способа инициализации переменных, но, как спрашивается, действительно ли это способ сделать это?
Почему не срабатывает оператор if в последнем примере блока кода?





В вашем последнем блоке кода if действительно выполняется (вы можете проверить это с помощью отладчика или добавления операторов print), но вы пытаетесь обновить @State несколько раз (сначала до 0, а затем до 1 или 2) во время init, что не поддерживаемый шаблон.
Вместо этого, если вы назначите его один раз, он будет работать:
struct EditPrView: View {
@Binding private var obj: A?
@State private var count: Int
init(obj: Binding<A?>) {
self._obj = obj
self.count = obj.wrappedValue is B ? 2 : 1
}
var body: some View {
VStack {
Text("foo-bar")
Text(String(count))
}
}
}
Вы можете передать nameCount в родительском представлении, если не хотите использовать свой onAppear метод:
struct ParentView: View {
var body: some View {
EditPrView(
name: name,
nameCount: name.count
)
}
}
struct EditPrView: View {
@State var name: String
@State var nameCount: Int
var body: some View {
VStack {
Text(name)
Text(String(nameCount))
}
}
}
Обновлено: я бы также упомянул, что это не обязательно должно быть @Binding, если только вы не изменяете это значение nameCount изнутри EditPtrView, а затем используете это обновленное значение для чего-то еще в родительском представлении. То же самое и с @State: если вы не изменяете эти значения в представлении, это не обязательно должно быть @State.
Вот как будет выглядеть пример привязки, если вы пытаетесь это сделать:
struct ParentView: View {
@State var nameCount: Int = 0
var body: some View {
EditPrView(
name: name,
nameCount: $nameCount
)
.onAppear {
nameCount = name.count
}
}
}
struct EditPrView: View {
@State var name: String
@Binding var nameCount: Int
var body: some View {
VStack {
Text(name)
Text(String(nameCount))
}
}
}