今日说说在开发中很常用的两个特性,它们也同样是两个特点包装器。
@FocusState 用于办理视图元素的输入焦点状态。
@AppStorage 用于将特点存储在 UserDefaults 中
接下来咱们用一个例子来说明
struct FocusStateSample: View {
let textFieldBackgroundColor = #colorLiteral(red: 0.9496834874, green: 0.9635508657, blue: 1, alpha: 1)
@State private var name: String = ""
@State private var password: String = ""
@State private var againPassword: String = ""
@State private var email: String = ""
var body: some View {
NavigationView {
ScrollView {
VStack(spacing: 20) {
markTextField("Input your name", bindingName: $name, submitLabel: .next, keyboardTypeL: .default)
markTextField("Input your password", bindingName: $password, submitLabel: .next, keyboardTypeL: .default)
markTextField("Input your password again", bindingName: $againPassword, submitLabel: .next, keyboardTypeL: .default)
markTextField("Input your email", bindingName: $email, submitLabel: .done, keyboardTypeL: .emailAddress)
Button {
} label: {
Text("Save".uppercased())
.foregroundColor(.white)
.font(.headline)
.fontWeight(.semibold)
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(Color.blue.cornerRadius(10))
}
Spacer()
}
}
.padding()
.navigationTitle("Focus state")
.onTapGesture {
dismissKeyboard()
}
}
}
// Hide keyboard, When you tap blank space
func dismissKeyboard(){
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
private func markTextField(
_ prompt: String,
bindingName: Binding<String>,
submitLabel: SubmitLabel,
keyboardTypeL: UIKeyboardType
) -> some View {
TextField(prompt, text: bindingName)
.font(.headline)
.frame(height: 55)
.submitLabel(submitLabel)
.keyboardType(keyboardTypeL)
.padding(.horizontal)
.background(Color(uiColor: textFieldBackgroundColor))
.cornerRadius(10)
}
}
现有代码是构建了四个输入框和一个Button,而且每个输入框的提交键盘按钮和运用的键盘类型都有所不同。
SubmitLabel
指定提交按钮的文字,SubmitLabel 是一个枚举,能够指定以下枚举中的任何值
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public struct SubmitLabel {
/// Defines a submit label with text of "Done".
public static var done: SubmitLabel { get }
/// Defines a submit label with text of "Go".
public static var go: SubmitLabel { get }
/// Defines a submit label with text of "Send".
public static var send: SubmitLabel { get }
/// Defines a submit label with text of "Join".
public static var join: SubmitLabel { get }
/// Defines a submit label with text of "Route".
public static var route: SubmitLabel { get }
/// Defines a submit label with text of "Search".
public static var search: SubmitLabel { get }
/// Defines a submit label with text of "Return".
public static var `return`: SubmitLabel { get }
/// Defines a submit label with text of "Next".
public static var next: SubmitLabel { get }
/// Defines a submit label with text of "Continue".
public static var `continue`: SubmitLabel { get }
}
办法定义如下:
public func submitLabel(_ submitLabel: SubmitLabel) -> some View
以上button的提交按钮分别设置了 .next 和.done
咱们现在有以下需求,会在上述代码基础上来完结。
需求:
- 需求在 App显现页面时,主动把焦点放在第一个输入用户名的TextField上
- 当点击键盘的Next按钮时,主动把焦点放在下一个TextField中。比如:我现在的焦点在输入用户名的TextField中,当我点击Next时就主动焦点到passwordTextField上
- 点击Save保存一切输入框的数据,下次发动时显现前次保存的值
咱们首先来完结第一个需求
需求在 App显现页面时,主动把焦点放在第一个输入用户名的TextField上
此刻咱们需求声明一个运用FocusState润饰的特点,它的类型FocusState ,详细如下:
- 声明FocusState润饰的特点,Boolean类型的,无初始值
@FocusState var nameFocused: Bool
- 给运用的TextField增加上下面的特点
.focused($nameFocused)
- 在OnAppear办法里边调用(0.5 秒后调用)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
nameFocused.toggle()
})
}
即可实现需求一
接下来完结需求二
当点击键盘的Next按钮时,主动把焦点放在下一个TextField中。比如:我现在的焦点在输入用户名的TextField中,当我点击Next时就主动焦点到passwordTextField上
其实需求二是在需求一的基础上做了加强,咱们需求给咱们一切的TexTField目标加上@FocusState特点包装器
@FocusState var nameFocused: Bool
@FocusState var passwordFocused: Bool
@FocusState var againPasswordFocused: Bool
@FocusState var emailFocused: Bool
一起需求在TextField弥补一个提交办法onSubmit
.onSubmit {
if nameFocused {
nameFocused = false
passwordFocused = true
} else if passwordFocused {
passwordFocused = false
againPasswordFocused = true
} else if againPasswordFocused {
againPasswordFocused = false
emailFocused = true
} else {
print("Done")
}
}
经过需求一,咱们理解。经过切换被@FocusState润饰的特点能够来实现当时TextFiedl是否有焦点,也便是是否能够运用键盘。那么上面办法便是判别当时TextField是否能够主动聚焦。其实你能够运用TextField里边的内容来判别,如果满意你当时的条件,你再切换到下一个TextField输入,例如:name.count > 8 等
点击Save保存一切输入框的数据,下次发动时显现前次保存的值
那么咱们改如何在本次保存数据后,下次发动时主动显现数据呢?(当咱们点击Save Button 时咱们不再判别输入的内容。默许输入框都是有内容的)
接下来咱们会介绍AppStorage,它的底层是UserDefault. 咱们现在需求声明s四个特点来保存四个输入框的值
@AppStorage var currentName: String?
@AppStorage var currentPassword: String?
@AppStorage var currentAgainPassword: String?
@AppStorage var currentEmail: String?
然后需求构建一个Save办法
private func save() {
currentName = name
currentPassword = password
currentAgainPassword = againPassword
currentEmail = email
print("Saved")
}
而且在 Button 办法中调用save办法
Button {
save()
}
保存的时其实就做完了,可是还差一个在下次发动主动填充值的需求。咱们来完结以下
先把数据读出来
?? 的意思是:当?? 前面的值为空时,便是用后面的值
private func autoFillContent() {
name = currentName ?? ""
password = currentPassword ?? ""
againPassword = currentAgainPassword ?? ""
email = currentEmail ?? ""
}
挂载到onAppear办法上
.onAppear {
autoFillContent()
guard name.count == 0 else { return }
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
nameFocused = true
})
}
上述需求已经都完结了,可是代码是存在问题的。在生产环境这样写代码是不行的。我说出以下几点问题:
- save之前需求校验输入的内容
- 主代码视图内需求精简,不要过于太长,避免看的脑壳疼。要做好高内聚
- 暗码框应该是秘文方式
- 考虑:如果我的输入框不止这四个,如果是一个简历输入页面,将有很多信息要录入,那么如果一个页面的TextField有10个该怎样办?还是运用上述办法吗?
下一节咱们将说说怎样去适配多个TextField的情况,加油!
关于今日的内容,大家有什么看法呢?欢迎留言评论。
公众号:RobotPBQ