#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 нет. И я связал свой пример проекта. Я надеюсь, что это могло бы быть чище. Спасибо!!