keyboardType 键盘类型

.keyboardType() 修饰符仅在 iOS 下有用,否则会报错,因此在做跨平台项目时,注意要用 #if os(iOS) 判断。

.numberPad 和 .decimalPad 键盘类型告诉 SwiftUI 显示数字 0 到 9,也可以选择显示小数点,但这并不能阻止用户输入其他值。例如,如果他们有硬件键盘,他们可以输入自己喜欢的内容,如果他们从其他地方复制一些文本,他们就可以将其粘贴到文本字段中,无论文本内有什么内容。不过没关系——当按下回车键时,文本字段会自动过滤掉错误的值。

Ascii Capable .keyboardType(.asciiCapable)
Numbers and Punctuation .keyboardType(.numbersAndPunctuation)
URL .keyboardType(.URL)
Numpad .keyboardType(.numberPad)
Phonepad .keyboardType(.phonePad)
Name Phonepad .keyboardType(.namePhonePad)
Email .keyboardType(.emailAddress)
Decimal Pad .keyboardType(.decimalPad)
Twitter 社交媒体 .keyboardType(.twitter)
Web Search .keyboardType(.webSearch)
Ascii Capable Numpad .keyboardType(.asciiCapableNumberPad)

设置键盘下方背景色

在 swiftUI 视图中,如果弹起 keyboard 键盘,现在键盘的上方是大圆角样式的,导致它两个角会露出底部的背景。这里原因是:SwiftUI 默认会把键盘当成一种 safe area,内容区域会避让键盘。Apple 文档里 SafeAreaRegions.keyboard 就是“当前软件键盘覆盖范围对应的 safe area”;ignoresSafeArea 的作用是让某个视图扩展进这些区域。

正确做法是:不要让主内容忽略键盘,只让背景色忽略键盘。

struct EditView: View {
    var body: some View {
        ZStack {
            Color.myBackground
                .ignoresSafeArea(.container)
                .ignoresSafeArea(.keyboard, edges: .bottom)

            content
        }
    }
}

这样背景可以延伸到键盘后面,而内容仍然正常避让键盘。

// 如果背景是通过修饰符这样写的:
VStack {
    content
}
.background(Color.myBackground)

// 那可以改成
VStack {
    content
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background {
    Color.myBackground
        .ignoresSafeArea(.container)
        .ignoresSafeArea(.keyboard, edges: .bottom)
}

如果里面是 List / Form,还要注意它们自己有滚动背景,会盖住你的背景。最终结论:

优化键盘升起动画

在 SwiftUI 视图中通过 .onAppear{} 在页面出现时激活键盘,似乎键盘弹起的动画特别突然,总是突然一下把整个页面内容顶上去,动画不够自然。其他应用似乎是激活时,内容先顶上去,然后键盘升起来。中间可能有个短暂的延时。

这个可以优化,但不是调“键盘动画本身”,wiftUI 没有公开 API 自定义系统键盘弹出动画曲线。我们能做的是:不要在 onAppear 里立刻聚焦,而是让页面先完成出现动画/布局稳定后,再触发键盘。

推荐改成 .task 延迟一点,不要用 .onAppear{} 。原因是:onAppear 太早。页面刚被插入视图树,导航转场、sheet 转场、safe area 计算、键盘避让几件事可能挤在同一轮更新里,视觉上就会像“突然被顶上去”。

enum Field: Hashable {
    case name
}

@FocusState private var focusedField: Field?

var body: some View {
    TextField("Name", text: $name)
        .focused($focusedField, equals: .name)
        .task {
            await Task.yield()
            try? await Task.sleep(for: .milliseconds(250))
            focusedField = .name
        }
}

总结:

  1. 不要用 onAppear 立即聚焦
  2. 改用 task + Task.yield()
  3. 再加 150–300ms 延迟
  4. 需要更自然时,加一个轻微的 padding / offset 预动画
  5. 不要硬编码完整键盘高度

submit 按钮及其行为

submitLabel 自定义键盘按钮

使用 .submitLabel() 修饰符,可以定制输入框附带键盘的右下角的提交按钮的样式,例如显示成 “Done” 或 “Next”

TextField("Enter your email address", text: $emailAddress)
		.textContentType(.emailAddress)
		.submitLabel(.join)

onSubmit 处理键盘提交

onSubmit() 修饰符可以附加到层次结构中的任何视图。使用此修饰符,用户在 TextField 或 SecureField 完成文字输入后,可以选择运行一个指定的方法。

// 例如,我们可以要求用户输入密码,然后在按回车键时运行一些代码:
struct ContentView: View {
    @State private var password = ""
    var body: some View {
        SecureField("Password", text: $password)
            .onSubmit {
                print("Authenticating…")
            }
    }
}

使用 onSubmit() 的优点是它可以捕获在其上下文中提交的所有文本值,这使得填写表单变得更容易:

struct ContentView: View {
    @State private var username = ""
    @State private var password = ""
    var body: some View {
        Form {
            TextField("Username", text: $username)
            SecureField("Password", text: $password)
        }
        .onSubmit {
            guard username.isEmpty == false && password.isEmpty == false else { return }
            print("Authenticating…")
        }
    }
}

toolbar 添加键盘工具栏

SwiftUI 允许向键盘添加输入的附加视图,这意味着当用户激活某些文本输入时,我们可以在那里显示自定义按钮。这是通过将 toolbar() 修饰符附加到相应的视图来完成的。创建工具栏项组时,使用 .keyboard 位置将此工具栏附加到键盘,如下所示:

struct ContentView: View {
    @State private var name = "Taylor"
    var body: some View {
        TextField("Enter your name", text: $name)
            .textFieldStyle(.roundedBorder)
            .toolbar {
                ToolbarItemGroup(placement: .keyboard) {
		                // 里面是具体视图的内容
                    Button("Click me!") {
                        print("Clicked")
                    }
                }
            }
    }
}

// 这个工具栏实际上是放置 “隐藏键盘” 按钮的好地方:
struct ContentView: View {
    @State private var name = "Taylor Swift"
    @FocusState var isInputActive: Bool

    var body: some View {
        NavigationStack {
            TextField("Enter your name", text: $name)
                .textFieldStyle(.roundedBorder)
                .focused($isInputActive)
                .toolbar {
                    ToolbarItemGroup(placement: .keyboard) {
                        Spacer()
                        Button("Done") {
                            isInputActive = false
                        }
                    }
                }
        }
    }
}