本文为翻译内容,来自swiftwithmajid
翻译:张洪Heo
推荐访问原文地址:立即访问

SwiftUI 在上次 WWDC 期间变得非常强大。我们获得了许多新功能,其中之一是全新的FocusState属性包装器。FocusState属性包装器允许我们读取和写入视图层次结构中的当前焦点位置。本周我们将学习如何使用FocusState属性包装器和聚焦视图修饰符在 SwiftUI 应用程序中管理焦点。

关于FocusState

SwiftUI 提供了一个新的FocusState属性包装器,适用于所有 Apple 平台,并允许我们专注于特定视图或检查该视图是否已经获得焦点。使用起来毫不费力。让我们看看如何使用它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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)
}
}
}
}

焦点绑定

正如你在上面的示例中看到的,我们需要使用FocusState属性包装器定义一个布尔变量。我们也有它的价值结合使用特定视图的焦点状态集中视图调节。一旦用户关注它,SwiftUI 就会将视图的布尔值设置为 true。一旦视图失去焦点,它也会将其更改为 false。

你可以根据需要定义任意数量的 FocusState变量来覆盖你的焦点管理逻辑。SwiftUI 通过将焦点视图与其绑定保持同步来处理它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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")
}
}
}

在上面的例子中,我们有两个变量绑定到电子邮件和密码文本字段。SwiftUI 可以一起管理它们并使它们与用户界面保持同步。请记住,你可以通过编程方式将该值更改为 false 以隐藏键盘或将该值设置为 true 以将焦点移动到特定视图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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不仅适用于布尔值,还适用于任何Hashable类型。这意味着我们可以使用符合Hashable协议的枚举类型对聚焦状态进行建模。让我们来看看这个例子。

1
2
3
4
enum FocusableField: Hashable {
case email
case password
}

这里我们有符合HashableField枚举并定义了我们管理的所有可聚焦视图。现在,我们可以使用这个枚举将不同视图的焦点状态绑定到各种枚举案例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
struct ContentView: View {
@State private var email = ""
@State private var password = ""
@FocusState private var focus: FocusableField?

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")
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
focus = .email
}
}
}
}

private func login() {
// your logic here
}
}

如你所见,我们使用了另一个版本的聚焦视图修饰符来将视图绑定到Field枚举的具体情况。每当用户关注任何绑定视图时,SwiftUI都会更新FocusState属性的值。请记住,我们应该将FocusState属性设为可选,以便与Hashable枚举结合使用,因为目前可能没有聚焦视图。