为啥即使发布者发出一个值,我的视图也没有在 SwiftUI 中呈现
Posted
技术标签:
【中文标题】为啥即使发布者发出一个值,我的视图也没有在 SwiftUI 中呈现【英文标题】:Why my views are not Rendered in SwiftUI even though the Publisher emits a value为什么即使发布者发出一个值,我的视图也没有在 SwiftUI 中呈现 【发布时间】:2021-06-30 00:39:20 【问题描述】:我在 swiftUI 中有一个用户名 textField。我正在尝试在发布者的帮助下验证输入。
这是我的代码:
View
struct UserView: View
@StateObject private var userViewModel = UserViewModel()
init()
UITextField.appearance().semanticContentAttribute = .forceRightToLeft
UITextField.appearance().keyboardAppearance = .dark
var body: some View
SecureField("", text: $userViewModel.passwordText)
Text(userViewModel.passwordError).foregroundColor(.red)
.frame(width: 264, alignment: .trailing)
视图模型
ViewModel
final class UserViewModel: ObservableObject
private var cancellables = Set<AnyCancellable>()
@Published var userText: String = ""
@Published var userTextError = ""
private var usernamevalidation: AnyPublisher<(username:String, isValid: Bool), Never>
return $userText
.dropFirst()
.map(username:$0, isValid: !$0.isEmpty)
.eraseToAnyPublisher()
private var usernamevalidated: AnyPublisher<Bool,Never>
return usernamevalidation
.filter$0.isValid
.map$0.username.isValidUserName()
.eraseToAnyPublisher()
init()
usernamevalidation.receive(on: RunLoop.main)
.map$0.isValid ? "": "Emptyusername "
.assign(to: \.userTextError, on: self)
.store(in: &cancellables)
usernamevalidated.receive(on: RunLoop.main)
.map$0 ? "" : "wrong username "
.assign(to: \.userTextError, on: self)
.store(in: &cancellables)
扩展
extension String
func isValidUserName() -> Bool
let usernameRegex = "^[a-zA-Z0-9_-]*$"
let usernamepred = NSPredicate(format:"SELF MATCHES %@", usernameRegex)
return usernamepred.evaluate(with: self)
在 ViewModel 的 init() 块中的 usernamevalidated 中,我将错误分配给 userTextError 属性,该属性应该反映在 textview 中。如果输入了 @ 或 % .. 等特殊字符,就会发生这种情况。发生的情况是,有时即使我尝试在映射运算符之后打印字符串的值,错误也会以红色显示,而其他错误则显示为红色,我可以看到打印正常的字符串。只是错误有时会反映在视图中,有时不会。我是否遗漏了什么或做一些根本错误的事情
【问题讨论】:
【参考方案1】:问题在于usernamevalidation
和usernamevalidated
都是计算属性。将它们存储起来可以解决问题,但您也可以通过观察 userText
的更改、验证它们并分配给 userTextError
来简化视图模型,如下所示:
final class UserViewModel: ObservableObject
@Published var userText: String = ""
@Published private(set) var userTextError = ""
init()
$userText
.dropFirst()
.map username in
guard !username.isEmpty else
return "Username is empty"
guard username.isValidUserName() else
return "Username is invalid"
return ""
.receive(on: RunLoop.main)
.assign(to: &$userTextError)
还值得一提的是,将.assign(to: \.userTextError, on: self)
替换为.assign(to: &$userTextError)
可以消除内存泄漏,这意味着您确实需要将其存储在cancellables
中。
【讨论】:
这很好用,但是为什么将发布者设为私有时它不起作用 UserText 发布者不能是私有的,因为视图需要读取和写入它。 UserTextError 可以是私有的(设置),因为视图需要能够读取它,但不需要写入它。【参考方案2】:而不是为您的发布者使用可计算的存储属性(以使它们保持活力),例如
private lazy var usernamevalidation: AnyPublisher<(username:String, isValid: Bool), Never> =
return $userText
.dropFirst()
.map(username:$0, isValid: !$0.isEmpty)
.eraseToAnyPublisher()
()
【讨论】:
仍然无法正常工作,特别是 usernamevalidated 发布者,有时会显示错误,有时不会 我在每个发布者的 dropfirst 之后发现了我的问题,我需要在主线程上接收这些。添加以下 .receive(on: DispatchQueue.main) 解决了我的问题【参考方案3】:以前的答案(如果需要,请参阅编辑)不正确,因为 Emptyusername
错误将被覆盖,这不是我们需要的(我的错误)。
原来,问题是 ios 14 更新了 UI!我之前不得不为TextField
使用的修复程序看起来有点不寻常,但确实有效。
只需在视图正文中添加:
var body: some View
let _ = userViewModel.userTextError
/* Rest of view as before */
【讨论】:
尽管您说的有道理,但问题仍然存在。问题是有时会显示错误,有时不会。我不知道这是 xcode 中的错误还是什么,但它很痛苦 @Ahmed 在此更改之后,它对我有用。使用 Xcode 13b2 'Version 13.0 beta (13A5155e)' 测试 让我检查一下我的 xcode 版本。您可以尝试多次,因为它有时有效,有时无效。喜欢尝试6-10次,你能和我确认一下吗 @Ahmed 啊哈!我的适用于 iOS 15 上的模拟器和真实设备,但不适用于 iOS 14 上的模拟器。 @Ahmed 老实说,我不确定为什么会有所不同,但我会让你去调查。好像是iOS版本的问题,目前我只知道这些以上是关于为啥即使发布者发出一个值,我的视图也没有在 SwiftUI 中呈现的主要内容,如果未能解决你的问题,请参考以下文章
为啥我的 onBindViewHolder () 根本没有运行,即使我的所有其他回收器视图方法都运行了?
为啥即使我使用 Alamofire 发出请求,我的应用程序仍然冻结?