【问题标题】:Updating SwiftUI View based on Observable in Preview在 Preview 中基于 Observable 更新 SwiftUI View
【发布时间】:2020-01-10 19:43:37
【问题描述】:

尝试在 SwiftUI 中实现登录屏幕。基于其他类似的问题,我将采用在主 ContentView 中使用 Observable EnvironmentObject 和 ViewBuilder 的方法,它会对此做出反应并显示适当的屏幕。

但是,即使属性按照预期更新,预览中的视图也不会更改。在模拟器中构建和运行时一切正常,但在预览中永远不会发生更改。

以下是将代码简化为单个文件中可能的最小示例(仅缺少在 SceneDelegate 中传递环境对象,这不会影响预览)。

import SwiftUI
import Combine

struct ContentView: View {
    @EnvironmentObject var userAuth: UserAuth

    @ViewBuilder
    var body: some View {
        if !userAuth.person.isLoggedin {
                FirstView()
        } else {
                SecondView()
        }    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environmentObject(UserAuth())
    }
}




struct Person {
    var isLoggedin: Bool

    init() {
        self.isLoggedin = false
    }

}

class UserAuth: ObservableObject {
    @Published var person: Person

    init(){
        self.person = Person()
    }

  let didChange = PassthroughSubject<UserAuth,Never>()

  // required to conform to protocol 'ObservableObject'
  let willChange = PassthroughSubject<UserAuth,Never>()

  func login() {
    // login request... on success:
    willChange.send(self)
    self.person.isLoggedin = true
    didChange.send(self)
  }
}


struct SecondView: View {
    var body: some View {
        Text("Second View!")
    }
}

struct SecondView_Previews: PreviewProvider {
    static var previews: some View {
        SecondView().environmentObject(UserAuth())
    }
}

struct FirstView: View {
    @EnvironmentObject var userAuth: UserAuth

    var body: some View {
        VStack {
                Button(action: {
                    self.userAuth.login()
                }) {
                    Text("Login")
                }
            Text("Logged in: " + String(self.userAuth.person.isLoggedin))
        }
    }
}

struct FirstView_Previews: PreviewProvider {
    static var previews: some View {
        FirstView().environmentObject(UserAuth())
    }
}

编辑:根据下面的答案,我已将环境对象添加到内部视图中,但不幸的是,在预览模式下视图仍然没有改变。

【问题讨论】:

  • 它就在那里——你可以在Debug Preview 中看到它进入了SecondView 分支,所以这只是一个预览刷新问题。我会忽略这一点。
  • @Asperi 问题是在我的真实应用程序中,视图位于单独的文件中,因此在查看 ContentView 时,如果预览不打算刷新,它对于调试登录功能毫无用处。

标签: xcode swiftui


【解决方案1】:
struct FirstView_Previews: PreviewProvider {
    static var previews: some View {
        FirstView().environmentObject(UserAuth())
    }
}

环境对象也必须在 PreviewProvider 中设置

更新

【讨论】:

  • 这似乎是合理的!不幸的是,它没有任何区别,我会更新这个问题。谢谢!
  • 我在简单的真实项目中检查了这个,它在模拟器和设备上都适用于我。
【解决方案2】:
struct ContentView: View {
    @ObservedObject var userAuth =  UserAuth () // @ObservedObject

    var body: some View {
    NavigationView{ // Navigation

        }.environmentObject(UserAuth) //.environmentObject
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environmentObject(UserAuth())

    }
}

struct SecondView: View {
    @EnvironmentObject var userAuth: UserAuth // only EnvironmentObject
    var body: some View {
        Text("Second View!")
    }
}

struct SecondView_Previews: PreviewProvider {
    static var previews: some View {
        SecondView().environmentObject(UserAuth()) 
    }
}

【讨论】:

    【解决方案3】:

    Canvas 没有提供预览的问题是我的 ObservableObject 正在从用户默认值读取

    @Published var fName: String  = Foundation.UserDefaults.standard.string(forKey: "fName") ! 
        { didSet {
           Foundation.UserDefaults.standard.set(self.fName, forKey: "fName")
           }
        }
    

    所以可以在模拟器和设备上工作,但没有画布预览。我尝试了很多方法来使用预览数据,因为预览无法从UserDefaults(不是设备)读取,并意识到如果 UserDefault 不存在,我可以放置一个初始/默认值:

    @Published var fName: String  = Foundation.UserDefaults.standard.string(forKey: "fName") ?? "Sean" 
    { didSet {
        Foundation.UserDefaults.standard.set(self.fName, forKey: "fName")
           }
    }
    

    现在 Preview/Canvas 正在显示我的视图,我可以继续使用我的 Observable Object 进行编码。目的是在 Observable Object 中放入一些默认代码以供使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-12
      • 1970-01-01
      • 2020-04-02
      • 2023-02-04
      • 1970-01-01
      • 2013-09-05
      • 2021-03-30
      • 1970-01-01
      相关资源
      最近更新 更多