При выборе обертки свойства вы можете воспользоваться следующей схемой
Данная схемая является переработанной схемой Криса Эйдхофа
@State
приводит к повторному вычислению свойства body
представления, а соответственно и к перерисовке интерфейса.
@State private var isEnable: Bool = falseДанное свойство подходит только для простых типов, вроде
Int
, Bool
, String
и т.д. При отслеживании более сложных значений (например, кастомных типов) может привести к непредсказуемому поведению.@State
-свойством родительского представления.
Представление, получившее @Binding
-свойство, может как считывать его значение, а значит реагировать на изменения, так и изменять его самостоятельно, передавая это изменение в представление-источник. Таким образом между @State
-свойством и @Binding
-свойством создается двухсторонняя связь, и изменение одного влечет за собой изменение другого.
Рассмотрим пример.
Существует View
, которое должно иметь свойство, связанное со @State
-свойством родителя. Для этого такое свойство помечается с помощью обертки @Binding
.
struct SecondView: View { @Binding var intValue: Int var body: some View { Button("Increment") { intValue += 1 } } }Теперь при создании экземпляра
SecondView
нам необходимо передать @State
-свойство.
struct StateView: View { @State private var intValue = 0 var body: some View { VStack { Text("intValue equals \(intValue)") SecondView(intValue: $intValue) } } }
Обратите внимание, что в данном случае параметрintValue
передается по ссылке, с помощью$
.
intValue
в любом из представлений повлечет за его обновление и в другом предсталвении, а значит и перестройку body
.ObservableObject
и имеет одно или несколько свойств-издателей, то при изменении любого из них происходит повторное вычисление свойства body
представления, а соответственно и перерисовка интерфейса.
// Тип со свойством-издателем class TestObject: ObservableObject { @Published var num: Int = 0 } // Представление struct StateObjectTestView: View { @StateObject var stateObject = TestObject() var body: some View { VStack { Text("State object: \(stateObject.num)") Button("Increase state object", action: { stateObject.num += 1 print("State object: \(stateObject.num)") }) } } }При нажатии на кнопку
Button
будет увеличиваться значение свойства num
объекта stateObject
. И так как данное свойство являестя издателем, тип подписан на ObservableObject
, а его экземпляр обернут @StateObject
, то вид будет перерисовываться.
Срок жизни объекта @StateObject
такой же, как и у связанного с ним представления. Это значит, что если мы перейдем через NavigationLink
к представлению, изменим значения объекта @StateObject
, вернемся к NavigationView
, потом вновь перейдем к представлению с данным @StateObject
-объектом, то все изменения будут сброшены. Это корректное поведение.@StateObject
-объект, и есть необходимость передавать его в дочернее представление при его открытии.
Открытие дочернего представления и передача объекта:
NavigationLink("To child", destination: ChildView(observedObject: stateObject))Использование переданного объекта в дочернем представлении:
struct ChildView: View { @ObservedObject var observedObject: TestObject var body: some View { VStack { Text("ObservedObject: \(observedObject.num)") Button("Increase observedObject", action: { observedObject.num += 1 print("ObservedObject \(observedObject.num)") }) } } }
@ObservedObject
, позволяет передать наблюдаемый объект из родительского представления в дочерние. Но в данном случае нет необходимости непосредственно передачи. Для использования @ObservedObject
необходимо:
environmentObject
и передать ему наблюдаемый объект.@main struct TestApp: App { @StateObject var environmentObject = TestObject() var body: some Scene { WindowGroup { ContentView().environmentObject(environmentObject) } } }
@ObservedObject
. В нем будет находиться экземпляр наблюдаемого объекта.@EnvironmentObject var environmentObject: TestObjectТеперь при каждом изменении
environmentObject
вид будет перерисовываться.
В одной иерархии представлений может быть только один environmentObject
-объект конкретного типа данных. То есть поиск наблюдаемого объекта происходит именно по типу данных.@Environment(\.colorScheme) var colorScheme: ColorScheme
if colorScheme == .dark { // Checks the wrapped value. DarkContent() } else { LightContent() }
@Environment(\.presentationMode) var presentationMode
presentationMode.wrappedValue.dismiss()
Позволяет отслеживать принимает ли представление пользовательский ввод (другими словами, на каком представлении сейчас находится фокус ввода). Есть два варианта использования обертки: со значением типа Bool
, или с перечислением.
Bool
@FocusState private var isUsernameFocused: Bool @State private var username = "Anonymous"
TextField("Enter your username", text: $username) .focused($isUsernameFocused)
enum FocusedField { case username, password } @FocusState private var focusedField: FocusedField?
TextField("Enter your username", text: $username) .focused($focusedField, equals: .username) SecureField("Enter your password", text: $password) .focused($focusedField, equals: .password)