ios - SwiftUI - 编辑后视图未更新
问题描述
我正在编写一个简单的密码管理器作为熟悉 SwiftUI 的学习练习。到目前为止一切顺利,但是我遇到了一个问题,即我View
在编辑值后没有按预期更新。View
我现在正在处理2 个:一个用于显示秘密,另一个用于编辑其属性。我将 MVVM 模式用于我的底层数据模型,Core Data/Combine 用于访问数据存储。我的“显示”视图模型如下所示:
class SecretDisplayViewModel: BaseViewModel, ObservableObject {
private let secretIdentifier: String
@Published var secret: Secret? = nil
private var cancellables = [AnyCancellable]()
init(managedObjectContext: NSManagedObjectContext, secretIdentifier: String) {
self.secretIdentifier = secretIdentifier
super.init(managedObjectContext: managedObjectContext)
}
init(managedObjectContext: NSManagedObjectContext, secret: Secret) {
self.secretIdentifier = secret.id
self.secret = secret
super.init(managedObjectContext: managedObjectContext)
}
func loadSecret() {
let _ = secretsService.loadSecret(secretIdentifier: self.secretIdentifier)
.map({ (entities: [SecretEntity]) -> [Secret] in
entities.compactMap { $0.toSecret() }
})
.replaceError(with: []) // FIXME: How should I handle errors?
.sink { completion in
if case .failure(let error) = completion {
// TODO: Handle error
print("ERROR: \(error)")
}
} receiveValue: { [weak self] secrets in
guard let strongSelf = self else {
return
}
if secrets.count > 0 {
strongSelf.secret = secrets[0]
print("strongSelf.secret=\(String(describing: strongSelf.secret))")
}
}.store(in: &cancellables)
}
}
...并且显示View
设置为这样loadSecret()
调用onAppear
:
struct SecretDisplayView: View {
<snip>
@ObservedObject var viewModel: SecretDisplayViewModel
@State private var isEditing: Bool = false
var body: some View {
buildViewForSecret(secret: viewModel.secret)
.listStyle(GroupedListStyle())
.navigationTitle("Secret")
.onAppear {
if isPreview {
// Do nothing
} else {
self.viewModel.loadSecret()
}
}
.toolbar {
Button("Edit", action: { self.isEditing.toggle() })
}
.sheet(isPresented: $isEditing, onDismiss: {
// FIXME: Secret not refreshed after dismiss
self.viewModel.loadSecret()
}, content: {
if let secret = self.viewModel.secret {
SecretCreateEditView(editViewModel: SecretEditViewModel(managedObjectContext: self.viewContext,
secret: secret))
} else {
EmptyView()
}
})
}
@ViewBuilder
private func buildViewForSecret(secret: Secret?) -> some View {
List {
if let secretImpl = secret {
SecretDisplayHeaderView(name: secretImpl.name, category: secretImpl.category.title)
} else {
EmptyView()
}
if let secretImpl = secret as? LoginSecret {
LoginSecretDisplayForm(secret: secretImpl)
} else if let secretImpl = secret as? BankAccountSecret {
BankAccountSecretDisplayForm(secret: secretImpl)
} else if let secretImpl = secret as? CreditCardSecret {
CreditCardSecretDisplayForm(secret: secretImpl)
} else if let secretImpl = secret as? WifiNetworkSecret {
WifiNetworkSecretDisplayForm(secret: secretImpl)
} else if let secretImpl = secret as? RewardProgramSecret {
RewardProgramSecretDisplayForm(secret: secretImpl)
} else {
// TODO: Display a loading view or something
EmptyView()
}
if let notes = secret?.notes {
Section(header: Text("Notes")) {
TextEditor(text: Binding.constant(notes))
}
.isHidden(notes.isEmpty)
}
// TODO: Secret actions (favorite/delete/share/etc.)
}
}
<snip>
}
这工作得很好,我的秘密按预期显示。如您所见,View
当用户点击“编辑”按钮时,编辑显示为工作表。编辑视图模型如下所示:
class SecretEditViewModel: SecretCreateViewModel {
@Published var secret: Secret
init(managedObjectContext viewContext: NSManagedObjectContext, secret: Secret) {
self.secret = secret
super.init(managedObjectContext: viewContext)
self.name = secret.name
self.notes = secret.notes
<snip>
}
func updateSecret() {
let updatedSecret = self.buildSecret(secretCategory: secret.category,
secretIdentifier: secret.id,
createdDate: secret.createdDate)
secretsService.updateSecret(secret: updatedSecret)
}
}
...我的编辑View
看起来像这样:
struct SecretCreateEditView: View {
<snip>
@ObservedObject var viewModel: SecretCreateViewModel
@State var secretCategory: SecretCategory?
private let editMode: Bool
init(viewModel: SecretCreateViewModel) {
self.viewModel = viewModel
self.editMode = false
}
init(viewModel: SecretCreateViewModel, secretCategory: SecretCategory) {
self.viewModel = viewModel
_secretCategory = State(initialValue: secretCategory)
self.editMode = false
self.viewModel.secretCategory = secretCategory
}
init(editViewModel: SecretEditViewModel) {
self.viewModel = editViewModel
_secretCategory = State(initialValue: editViewModel.secret.category)
self.editMode = true
}
var body: some View {
NavigationView {
List {
buildViewForSecretCategory(secretCategory: self.secretCategory)
Section(header: Text("Notes")) {
TextEditor(text: $viewModel.notes)
.lineLimit(10)
}
.isHidden(secretCategory == nil)
}
.listStyle(GroupedListStyle())
.navigationTitle("Edit Secret")
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") {
self.presentationMode.wrappedValue.dismiss()
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: saveSecret) {
Text(self.editMode ? "Done" : "Save")
}
.disabled(self.secretCategory == nil || self.viewModel.name.isEmpty)
}
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
// MARK: - Private methods
private func saveSecret() {
if let model = self.viewModel as? SecretEditViewModel {
model.updateSecret()
self.presentationMode.wrappedValue.dismiss()
} else {
if let _ = self.secretCategory {
self.viewModel.saveSecret()
self.presentationMode.wrappedValue.dismiss()
} else {
// TODO: Handle error
}
}
}
<snip>
}
正如您在 2View
秒中看到的,当我的编辑视图被关闭时,显示View
将调用self.viewModel.loadSecret()
以使用来自核心数据的值刷新视图模型。如果我设置断点或打印出视图模型的描述,我会看到底层数据模型中的值正在按预期更新。但是,我View
的没有更新!例如,如果我更改了密码的名称或用户名,则视图模型中的数据在编辑View
被关闭后显示为正确,但显示View
本身并未使用适当的新值进行更新。如果我回到我的NavigationView
堆栈中的秘密列表(代码未显示,因为它不是必需的),然后再次查看有问题的秘密,则值会按预期更新。只是没有立即编辑后,即使数据本身已更新。
我在这里错过了什么吗?我是, , 等的新手@State
,所以我很可能会误解某些东西。非常感谢您提供的任何帮助!@Published
@ObservedObject
解决方案
推荐阅读
- vim - 如何在 vim 命令中转义一个点?
- java - X509Cerificate 检查是否从某个 CA 发出
- html - 即使 HREF 是“绝对的”,是否有任何方法可以制作适用的 BASE URL?
- javascript - 第一次单击时动画过渡未运行
- spring-webflux - 使用 Netty 调试 Spring Cloud Gateway 性能
- android - 如何对访问 Provider.of(context) 的小部件进行 Flutter 测试
- python - 谷歌 api 令牌已过期或被撤销?
- c - gethostbyname() 函数的源码实现在哪里?
- c# - 为什么用户控件的 WPF 上下文菜单只显示用户控件中的特定位置?
- javascript - 使用 mongoose 获取路由的错误处理