SwiftUI属性包装器之FocusState
所属分类:ios | 发布于 2023-12-10 16:41:50
在iOS15中,SwiftUI提供了FocusState属性包装器,用来帮助我们判断该视图内的TextField是否获得焦点。通过focused将FocusState与特定的TextField关联起来。
先看代码:
import SwiftUI
struct SignInView: View {
@FocusState private var isEmailFocused: Bool
@State private var email = ""
var body: some View {
NavigationView {
Form {
TextField("email", text: $email, prompt: Text("email"))
.focused($isEmailFocused)
}
.navigationTitle("Sign in")
.onChange(of: isEmailFocused) { newValue in
print(newValue)
}
}
}
}
在上面的代码中,我们定义了一个isEmailFocused的属性包装器,在TextField组件上添加focused()修饰符,并绑定到定义的isEmailFocused属性包装器,当TextField获取到焦点时,isEmailFocused被设置为true,当TextField失去焦点时,isEmailFocused被设置为false。
可以定义多个属性包装器,如下:
import SwiftUI
struct SignInView: View {
@FocusState private var isEmailFocused: Bool
@FocusState private var isPasswordFocused: Bool
@State private var email = ""
@State private var password = ""
var body: some View {
NavigationView {
Form {
TextField("email", text: $email, prompt: Text("email"))
.focused($isEmailFocused)
SecureField("password", text: $password, prompt: Text("password"))
.focused($isPasswordFocused)
}
.navigationTitle("Sign in")
}
}
}
在上面的代码中,定义了两个FocusState属性包装器,用来管理email和password的状态。
再进一步,点击登录按钮发现字段为空时,字段自动获取焦点,如下:
import SwiftUI
struct SignInView: View {
@FocusState private var isEmailFocused: Bool
@FocusState private var isPasswordFocused: Bool
@State private var email = ""
@State private var password = ""
var body: some View {
NavigationView {
Form {
TextField("email", text: $email, prompt: Text("email"))
.focused($isEmailFocused)
SecureField("password", text: $password, prompt: Text("password"))
.focused($isPasswordFocused)
Button("login") {
if email.isEmpty {
isEmailFocused = true
} else if password.isEmpty {
isPasswordFocused = true
} else {
isPasswordFocused = false
isEmailFocused = false
login()
}
}
}
.navigationTitle("Sign in")
}
}
private func login() {
// your logic here
}
}
在复杂的视图中定义很多个FocusState包装器会显得很麻烦,幸运的是,FocusState不仅支持Bool类型,还支持任何实现了Hashable协议的类型。这样,我们就可以定义一个实现了Hashable协议的enum类型来管理所有的focused状态。
示例代码如下:
struct ContentView: View {
@State private var email = ""
@State private var password = ""
@FocusState private var focus: FocusableField?
enum FocusableField: Hashable {
case email
case password
}
var body: some View {
NavigationView {
Form {
TextField("email", text: $email, prompt: Text("email"))
.focused($focus, equals: .email)
SecureField("password", text: $password, prompt: Text("password"))
.focused($focus, equals: .password)
Button("login", action: login)
}
.toolbar {
ToolbarItem(placement: .keyboard) {
Button("next") {
if email.isEmpty {
focus = .email
} else if password.isEmpty {
focus = .password
} else {
focus = nil
}
}
}
}
.navigationTitle("Sign in")
.defaultFocus($focus, .email)
}
}
private func login() {
// your logic here
}
}
在上面的代码中,任何一个焦点的改变都会更新到focus属性包装器上。
需要注意的是,Hashable enum类型的属性包装器必须设置为可选类型,因为在大部分时间这些元素都是没有焦点的。
传递@FocusState属性包装器给子视图
struct View1: View {
enum Field {
case username, password
}
@State var passwordText: String = ""
@FocusState var focusedField: Field?
var body: some View {
View2(text: $passwordText, placeholder: "Password", focused: $focusedField)
}
}
struct View2: View {
@Binding var text: String
let placeholder: String
var focused: FocusState<View1.Field?>.Binding // << here !!
var body: some View {
HStack {
TextField(placeholder, text: $text)
.frame(minHeight: 44)
.padding(.leading, 8)
.focused(focused, equals: .password) // << here !!
if text.count > 0 {
Image(systemName: "xmark.circle.fill")
.font(.headline)
.foregroundColor(.secondary)
.padding(.trailing, 8)
}
}
}
}
视图显示,立刻让指定的TextField获取焦点并弹出键盘
struct OnFocusDemo:View{
@FocusState var focus:FocusedField?
@State var name = ""
@State var password = ""
var body: some View{
List{
TextField("name:",text:$name)
.focused($focus, equals: .name)
SecureField("password:",text:$password)
.focused($focus,equals: .password)
}
.onAppear{
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5){
focus = .name
}
}
}
enum FocusedField:Hashable{
case name,password
}
}