SwiftUI3.0用户登录输入非空校验经典案例
在oc和swift里面,通过UITextFiled的代理方法,可以实施监听到用户输入的每个字符,使用正则表达式,进行判断,是否合法。在swiftUI通过 Published 发行者, ObservableObject 观察对象,实现监听绑定 TextField的输入值的变化,结合最新的Combine框架。写一个函数。 先看UI布局核心源码:
import SwiftUI
struct ContentView: View {
@StateObject var loginVM = LoginViewModel()
var body: some View {
VStack{
VStack(alignment: .leading, spacing: 4){
ZStack{
RoundedRectangle(cornerRadius: 10)
.fill(Color.black.opacity(0.14))
RoundedRectangle(cornerRadius: 10)
.stroke(Color.blue,lineWidth: 1)
.opacity(loginVM.userNameFaildFoucs == true ? 1 : 0)
TextField("请输入用户名",text: $loginVM.userName,onEditingChanged: { editChange in
withAnimation {
loginVM.userNameFaildFoucs = editChange
}
})
.padding(.horizontal,20)
}
.frame( height: 35)
Text(loginVM.vatiryUserNameError ?? "")
.font(.system(size: 12, weight: .semibold))
.foregroundColor(.red)
.padding(.leading,20)
}
VStack(alignment: .leading, spacing: 4){
ZStack{
RoundedRectangle(cornerRadius: 10)
.fill(Color.black.opacity(0.14))
RoundedRectangle(cornerRadius: 10)
.stroke(Color.blue,lineWidth: 1)
.opacity(loginVM.userNameFaildFoucs == false ? 1 : 0)
SecureField("请输入密码",text: $loginVM.password)
.padding(.horizontal,20)
}
.frame( height: 35)
Text(loginVM.vatiryPasswordError ?? "")
.font(.system(size: 12, weight: .semibold))
.foregroundColor(.red)
.padding(.leading,20)
}
Button {
debugPrint("登录")
debugPrint(loginVM.userName)
debugPrint(loginVM.password)
} label: {
Text("登录")
.foregroundColor(.white)
.padding(.vertical,4)
.padding(.horizontal,40)
.background(
Capsule().fill(Color.blue.opacity(loginVM.isVatity ? 1 : 0.3))
)
}
.disabled(!loginVM.isVatity)
}
.padding(.horizontal,16)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
逻辑代码
import SwiftUI
import Combine
class LoginViewModel: ObservableObject{
@Published var userNameFaildFoucs: Bool?
@Published var isVatity = false
@Published var userName = ""
@Published var vatiryUserNameError: String?
@Published var password = ""
@Published var vatiryPasswordError: String?
private var callelabelSet: Set<AnyCancellable> = []
private var verityUserNamePublisher: AnyPublisher<String?,Never> {
$userName.debounce(for: 0.5, scheduler: RunLoop.main)
.removeDuplicates()
.map{ _userName in
if _userName.isEmpty {
return "用户名不能为空"
}else if _userName.count < 4 || _userName.count > 16 {
return "用户名介于4~16之间"
}else{
return nil
}
}
.eraseToAnyPublisher()
}
private var veritypasswordPublisher: AnyPublisher<String?,Never> {
$password.debounce(for: 0.5, scheduler: RunLoop.main)
.removeDuplicates()
.map{ _userName in
if _userName.isEmpty {
return "密码不能为空"
}else if _userName.count < 4 || _userName.count > 16 {
return "密码介于4~16之间"
}else{
return nil
}
}
.eraseToAnyPublisher()
}
init(){
Publishers.CombineLatest(verityUserNamePublisher, veritypasswordPublisher)
.dropFirst()
.sink { _vatiryPasswordError,_vatiryUserNameError in
self.isVatity = _vatiryPasswordError == nil && _vatiryUserNameError == nil
}
.store(in: &callelabelSet)
verityUserNamePublisher
.dropFirst()
.sink { _vatiryUserNameError in
self.vatiryUserNameError = _vatiryUserNameError
}
.store(in: &callelabelSet)
veritypasswordPublisher
.dropFirst()
.sink { _vatiryPasswordError in
self.vatiryPasswordError = _vatiryPasswordError
}
.store(in: &callelabelSet)
}
}
如果对Combine不熟悉的话,看起来 比较difficult
|