I'm kind of new with SwiftUI, and i want to create a login screen with "Quick developers access" button (aka "User1"), that should fill up the user and password fields.
When I click on the "User1" button, the @State
phone and password variables of LoginScreen
are filling up correctly with the correct values, but the CustomLoginField
is no showing the updated values.
I also tried to use @Binding var phone:String
instead @State
, but same result.
I know that @State
should be private.. so i also would like to know the right way to write my code.
import SwiftUI
struct LoginScreen: View {
private let developers:[User] = [
.init(name: "User1", phone: "12345678", password: "1234")]
private let minSpaceFromBottom:CGFloat = 30
@State fileprivate var phone:String = ""
@State fileprivate var password:String = ""
var body: some View {
GeometryReader { geo in
ZStack {
Color(#colorLiteral(red: 0.9965313077, green: 0.9966740012, blue: 0.9965001941, alpha: 1))
VStack(alignment: .center) {
Spacer().frame(height: geo.safeAreaInsets.top)
CustomLoginField(inputStr:phone, keyboard: .numberPad, placeholder:"PHONE", formatedPhoneNumber:true, maxLength: 10)
CustomLoginField(inputStr:password, keyboard: .numberPad, placeholder: "PASSWORD", isSecure: true, maxLength: 4)
HStack {
ForEach(developers, id:\.id) { developer in
Text(developer.name!)
.frame(width: 70, height: 35, alignment: .center)
.onTapGesture {
phone = developer.phone
password = developer.password
}
}
}
Spacer()
}
.frame(width: geo.size.width * 0.9, alignment: .leading)
Spacer()
.frame(height: minSpaceFromBottom)
}
}
}
}
private struct CustomLoginField: View {
@State var inputStr:String
var keyboard:UIKeyboardType = .default
var placeholder:String
var isSecure:Bool = false
var formatedPhoneNumber = false
var maxLength = 0
var underlineColor:UIColor {
let underlineOnColor = UIColor(.blue)
let underlineOffColor:UIColor = .lightGray
return inputStr.count >= maxLength ? underlineOnColor : underlineOffColor
}
var body: some View {
VStack(alignment:.leading, spacing:-8) {
Text(placeholder)
.opacity(!inputStr.isEmpty ? 1 : 0)
.animation(Animation.easeIn(duration: !inputStr.isEmpty ? 0.3 : 0))
if isSecure {
SecureField(placeholder, text: $inputStr)
.modifier(CustomTextFieldModifire(parent: self))
} else {
TextField(placeholder, text: $inputStr)
.modifier(CustomTextFieldModifire(parent: self))
}
Color(underlineColor)
.frame(width: UIScreen.main.bounds.width * 0.88, height: 1)
.animation(Animation.easeIn(duration:0.3))
}
}
}
private struct CustomTextFieldModifire: ViewModifier {
var parent:CustomLoginField
func body(content: Content) -> some View {
content
.frame(height: 45)
.keyboardType(parent.keyboard)
.onReceive(parent.inputStr.publisher.collect()) {
var result = String($0)
if parent.maxLength > 0 && result.count > parent.maxLength {
result = String(result.prefix(parent.maxLength))
}
parent.inputStr = result
}
}
}
private struct User:Identifiable {
let id = UUID()
var name:String?
var phone:String
var password:String
}
struct LoginScreen_Previews: PreviewProvider {
static var previews: some View {
LoginScreen()
.previewDevice("iPhone 8")
}
}