В этой статье я объясню основные принципы комбинированной среды SwiftUI, используя простое приложение, которое меняет свое состояние в нескольких случаях.

Использование @State

В SwiftUI оболочка свойства @State используется для объявления состояния представления. Когда состояние представления изменяется, платформа автоматически повторно отображает представление, чтобы отразить обновленное состояние. Это означает, что с помощью @State вы можете создавать динамические интерактивные пользовательские интерфейсы, которые реагируют на пользовательский ввод и другие события.

Вот пример того, как @State можно использовать в представлении SwiftUI:

struct ContentView: View {
    @State private var name = "John"

    var body: some View {
        VStack {
            Text("Hello, \(name)!")
            TextField("Enter your name", text: $name)
        }
    }
}

Использование @Binding

Обертка свойства @Binding используется для создания двусторонней привязки между состоянием представления и состоянием его родительского представления. Когда дочернее представление обновляет свое состояние с помощью свойства @Binding, состояние родительского представления также обновляется, и наоборот. Это позволяет передавать данные из одного представления в другое таким образом, чтобы представления автоматически синхронизировались.

Вот пример того, как @Binding можно использовать в иерархии представлений SwiftUI:

struct ParentView: View {
    @State private var name = "John"

    var body: some View {
        VStack {
            Text("Hello, \(name)!")
            ChildView(name: $name)
        }
    }
}

struct ChildView: View {
    @Binding var name: String

    var body: some View {
        TextField("Enter your name", text: $name)
    }
}

В этом примере родительское представление имеет свойство состояния name, объявленное с помощью оболочки свойства @State. Дочернее представление ChildView принимает состояние родителя name в качестве свойства @Binding. Когда пользователь вводит текстовое поле в дочернем представлении, свойство name дочернего представления обновляется, что автоматически обновляет состояние name родительского представления и повторно отображает оба представления. Это позволяет потоку данных между представлениями быть динамическим и синхронизированным.

Использование @StateObject

В SwiftUI оболочка свойства @StateObject используется для объявления состояния представления, которым управляет класс, соответствующий протоколу ObservableObject. Свойство @StateObject автоматически синхронизируется с представлением, и представление будет автоматически обновляться при каждом изменении @StateObject.

Протокол ObservableObject позволяет вам определить класс, который может публиковать изменения своих свойств, которые затем могут наблюдаться представлением SwiftUI. Это упрощает управление сложным состоянием, которое необходимо разделить между несколькими представлениями.

Вот пример того, как @StateObject можно использовать в представлении SwiftUI:

class User: ObservableObject {
    @Published var name = "John"
}

struct ContentView: View {
    @StateObject var user = User()

    var body: some View {
        VStack {
            Text("Hello, \(user.name)!")
            TextField("Enter your name", text: $user.name)
        }
    }
}

В этом примере класс User соответствует протоколу ObservableObject и имеет свойство @Published, name, которое представляет собой данные, которые должны быть разделены между несколькими представлениями. Затем ContentView использует оболочку свойства @StateObject для объявления свойства состояния user, которое является экземпляром класса User. Всякий раз, когда пользователь вводит текстовое поле, свойство name объекта состояния user обновляется, что автоматически запускает повторную визуализацию представления.

Использование @EnvironmentObject

В SwiftUI оболочка свойства @EnvironmentObject используется для доступа к общему состоянию из среды. Это способ передачи данных вниз по иерархии представлений без необходимости явно передавать их через несколько представлений. @EnvironmentObject — это экземпляр класса, который соответствует протоколу ObservableObject и объявлен в среде иерархии представлений с помощью метода .environmentObject().

Вот пример того, как @EnvironmentObject можно использовать в иерархии представлений SwiftUI:

class UserSettings: ObservableObject {
    @Published var fontSize: CGFloat = 17
}

struct ContentView: View {
    @EnvironmentObject var userSettings: UserSettings

    var body: some View {
        VStack {
            Text("Hello, World!")
                .font(.system(size: userSettings.fontSize))
            SettingsView()
        }
    }
}

struct SettingsView: View {
    @EnvironmentObject var userSettings: UserSettings

    var body: some View {
        VStack {
            Text("Settings")
            Slider(value: $userSettings.fontSize, in: 10...20)
        }
    }
}

В этом примере класс UserSettings соответствует протоколу ObservableObject и имеет свойство @Published, fontSize, которое представляет настройку пользователя. И ContentView, и SettingsView объявляют свойство @EnvironmentObject userSettings, которое ссылается на один и тот же экземпляр класса UserSettings. Это означает, что оба представления имеют доступ к одному и тому же общему состоянию, и всякий раз, когда изменяется свойство fontSize объекта userSettings, оба представления будут автоматически обновляться, чтобы отразить это изменение.

Вы устанавливаете объект среды для иерархии представлений, используя метод .environmentObject() в корневом представлении, например:

let userSettings = UserSettings()

let contentView = ContentView()
    .environmentObject(userSettings)

Я надеюсь, что эта статья поможет, как всегда, весь код связан с моей страницей на github.