Переключение переменных @State с помощью .OnTapGesture в SwiftUI - PullRequest
0 голосов
/ 15 апреля 2020

Может кто-нибудь сказать мне, почему этот лог c не работает? Я пытаюсь создать экземпляр представления и сохранить его в переменной. Затем я использую эту переменную, чтобы вернуть представление в var body. Моя цель состоит в том, чтобы переключать переменную isActive объекта просмотра на кране, чтобы отображалось изображение галочки.

Я могу сделать это, когда помещаю onTapGesture в объект пользовательского представления, но не могу получить изменение состояния, когда переключаю переменную из родительского представления. Я надеюсь, что это имеет смысл.

struct SensorFamilyView: View {

    @State var analogView = FamilyItemView(title: "Analog", isActive: false)

    var body: some View {

        VStack(alignment: .leading, spacing: 0) {

            analogView                                   // Show view instance

                .onTapGesture {                          // I want this tap gesture to work
                    self.analogView.isActive.toggle()
            }

        }
    }
}

struct FamilyItemView: View {                            // Custom View

    @State var title: String
    @State var isActive = false

    var body: some View {


        HStack {

            if ( isActive )                              // isActive toggles a checkmark image
            {   
                Image(systemName: "checkmark.circle")
            }
            else
            {
                Image(systemName: "circle")
            }

            Text("\(title)")
        }

        .padding()
        .onTapGesture {                            // This Tap works, but not what I want
            //self.isActive.toggle()
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 16 апреля 2020

@ State

Чтобы понять это, нам нужно сначала коснуться @State. Что это?

SwiftUI управляет хранением любого свойства, которое вы объявляете как состояние. Когда значение состояния изменяется, представление делает его недействительным и пересчитывает тело.
...
Экземпляр состояния не является самим значением ; это средство чтения и записи значения. Чтобы получить доступ к базовому значению состояния, используйте имя его переменной, которое возвращает значение свойства wrappedValue.
Ref: https://developer.apple.com/documentation/swiftui/state

Но почему нам нужно было @State? Хорошо ... Структуры являются типом значения, и его переменные по умолчанию не изменяются, поэтому, чтобы обойти это, было предоставлено @State propertyWrapper, которое в основном оборачивает значение и сохраняет и сохраняет его для нас в некотором персистенте * хранилище в рамках SwiftUI.
* Подробнее об этом см. В WWD C: https://developer.apple.com/videos/play/wwdc2019/226/

Когда @State свойство изменяется в теле структуры View, в которой оно было объявлено, движок SwiftUI автоматически выполняет рендеринг тела. Но если он видоизменяется извне, SwiftUI не реагирует на это.

Итак, что теперь?
Вот где @Binding можно использовать для создания двусторонней привязки.


@ Binding

Используйте привязку для создания двустороннего соединения между свойством, которое хранит данные, и представлением, которое отображает и изменяет данные. Привязка связывает свойство с источником правды, хранящимся в другом месте, вместо непосредственного хранения данных. Например, кнопка, переключающаяся между воспроизведением и паузой, может создать привязку к свойству своего родительского представления с помощью оболочки свойств @Binding.
Ref: https://developer.apple.com/documentation/swiftui/binding

Решение:

struct SensorFamilyView: View {
    @State var isActive: Bool = false

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            FamilyItemView(title: "Title", isActive: $isActive)
                .onTapGesture {
                    self.isActive.toggle()
            }
        }
    }
}

struct FamilyItemView: View {
    @State var title: String
    @Binding var isActive: Bool

    var body: some View {
        HStack {
            if (isActive) {
                Image(systemName: "checkmark.circle")
            } else {
                Image(systemName: "circle")
            }
            Text("\(title)")
        }
    }
}
  • SensorFamilyView имеет свойство состояния isActive
  • FamilyItemView имеет свойство привязки isActive

Между ними есть двухсторонняя привязка, поэтому, когда один меняется, другой также меняется. Кроме того, это все в рамках Combine (на котором в значительной степени основан SwiftUI), и поэтому запускается правильная последовательность событий, которая приводит к отображению тела.

0 голосов
/ 16 апреля 2020

Почему это не сработает?

Вы не можете хранить экземпляр FamilyItemView. Почему? Потому что это struct, а не class. Когда вы переключаете свойство isActive, представление воссоздается (поскольку оно использует @State).

Как это можно исправить?

Использовать @Binding. Создание привязки означает, что FamilyItemView будет обновляться при изменении свойства SensorFamilyView isActive. Его можно использовать следующим образом:

struct SensorFamilyView: View {

    @State private var isActive = false

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            FamilyItemView(title: "Analog", isActive: $isActive)
                .onTapGesture {
                    self.isActive.toggle()
                }
        }
    }
}

struct FamilyItemView: View {

    @State var title: String
    @Binding var isActive: Bool

    var body: some View {
        HStack {
            if isActive {
                Image(systemName: "checkmark.circle")
            } else {
                Image(systemName: "circle")
            }

            Text("\(title)")
        }.padding()
    }
}

Примечание: Что касается кода прямо сейчас, title не обязательно должен быть @State.

Дополнительная очистка кода

struct FamilyItemView: View {

    let title: String
    @Binding var isActive: Bool

    var body: some View {
        HStack {
            Image(systemName: isActive ? "checkmark.circle" : "circle")
            Text(title)
        }.padding()
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...