В родительском представлении swiftui как я могу узнать, когда изменяется состояние во внутреннем методе viewmodel void views viewmodel?

#swift #binding #swiftui #combine

#swift #привязка #swiftui #объединить

Вопрос:

Данная структура AuthenticationView swiftui содержит UserLoginView. UserLoginView имеет класс UserLoginViewModel для обработки действий кнопки входа.

В AuthenticationView я хотел бы знать, когда изменяется значение переменной во внутреннем UserLoginView (если значение tutorialScreenIsShowable равно true в AuthenticationView, навигационная ссылка переходит к следующему слайду).

 struct AuthenticationView: View {
    @State private var tutorialScreenIsShowable = false
    var body: some View {
        NavigationView {
            ZStack {
                NavigationLink(destination: TutorialView(), isActive: $tutorialScreenIsShowable) {
                    EmptyView()
                }
                UserLoginView(tutorialScreenIsShowable: self.$tutorialScreenIsShowable)
            }
        }
    }
}
  

,

 struct UserLoginView: View {
    @Binding var tutorialScreenIsShowable: Bool
    @ObservedObject var userLoginViewModel = UserLoginViewModel()
    var body: some View {
        Button(action: {
            self.userLoginViewModel.loginButtonAction() // it not works but I need this way
//            self.tutorialScreenIsShowable = self.userLoginViewModel.loginButtonActionWithReturn() // it works, but its not good in this case
        }) {
            Text("Log in")
        }
    }
}
  

,

 class UserLoginViewModel: NSObject, ObservableObject {
    @Published var tutorialScreenIsShowable = false
    
    func loginButtonAction() {
        self.tutorialScreenIsShowable = true
    }
    
    func loginButtonActionWithReturn() -> Bool {
        return true
    }
}
  

Насколько я знаю, если функция будет иметь возвращаемое значение, которое может работать, но в этом случае выполняется метод делегирования, где я могу обрабатывать переменную.

Я бы хотел избежать @EnvironmentObject, насколько это возможно, я надеюсь, что есть другое решение.

Не могли бы вы помочь мне с этим?

Редактировать: я упростил пример. И загрузил пример проекта здесь:https://www.dropbox.com/s/paosp6iom9oks9p/InnerViewPropBind.zip

Ответ №1:

Причина этого поведения заключается в NavigationView многократном воссоздании собственного содержимого во время обновления.

Вот возможное решение — некоторые изменения дизайна и владельца.

Протестировано с Xcode 11.4 / iOS 13.4

 struct AuthenticationView: View {
    // keep model here to avoid recreation inside navigation view
    @ObservedObject var userLoginViewModel = UserLoginViewModel()

    var body: some View {
        NavigationView {
            ZStack {
                // use binding directly in view model
                NavigationLink(destination: Text("TutorialView"), isActive: $userLoginViewModel.tutorialScreenIsShowable) {
                    EmptyView()
                }

                UserLoginView(userLoginViewModel: userLoginViewModel)
            }
        }
    }
}

struct UserLoginView: View {
    @ObservedObject var userLoginViewModel: UserLoginViewModel
    var body: some View {
        Button(action: {
            self.userLoginViewModel.loginButtonAction()
        }) {
            Text("Log in")
        }
    }
}

class UserLoginViewModel: NSObject, ObservableObject {
    @Published var tutorialScreenIsShowable = false

    func loginButtonAction() {
        self.tutorialScreenIsShowable = true
    }

    func loginButtonActionWithReturn() -> Bool {
        return true
    }
}
  

Комментарии:

1. Спасибо за ваше предложение, но, к сожалению, оно не работает. ): Навигационная ссылка не переходит к следующему слайду.

2. Ну, навигация — это другая проблема. Вы не можете перемещаться, находясь в модальном представлении, сначала вам нужно закрыть модальное.

3. Навигация работает, если loginWithGoogleButtonAction будет иметь возвращаемое значение (self.tutorialScreenIsShowable = self.userLoginViewModel.loginWithGoogleButtonAction() ). Так что это может быть не проблемой. В противном случае я также пытался контролировать с помощью свойства видимость представления, но это тоже не изменилось.

4. didSignInFor Получен обратный вызов? В основной очереди? Если да для первого, но нет для второго, следует добавить перенаправление в основную очередь отправки. Я не могу протестировать вашу зависимую от API часть.

5. Пожалуйста, проверьте еще раз пример, я очистил код, теперь API нет. И я связал свой пример проекта. Я надеюсь, что это могло бы быть чище. Спасибо!!