You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Sheet视图中SwiftUI键盘Toolbar不显示的问题及修复需求

问题:Sheet中嵌入的视图无法显示键盘Toolbar

单独展示ManualDeviceCodeInput视图时,绑定在数字键盘上的Toolbar能正常显示;但将该视图通过.sheet modifier嵌入弹窗后,Toolbar完全不出现。需求是为第一个仅接受N/O字符的输入框,在数字键盘上方添加快捷按钮Toolbar,希望Toolbar能显示在键盘与Sheet底部之间。

解决方案

在Sheet环境中,键盘Toolbar必须附加到Sheet的根视图层级才能被正确渲染,不能仅绑定在单个TextField上。同时需要确保Toolbar的条件逻辑能正确响应焦点状态,以下是修改后的实现:

修改后的完整代码

import SwiftUI
import Combine

struct CharInputModifier: ViewModifier {
    @Binding var char: String
    var textLimit = 1

    func limitText(_ upper: Int) {
        if char.count > upper {
            char = String(char.prefix(upper))
        }
    }

    func body(content: Content) -> some View {
        content
            .multilineTextAlignment(.center)
            .keyboardType(.numberPad)
            .onReceive(Just(char)) { _ in limitText(textLimit) }
            .font(.largeTitle)
            .frame(height: 100)
            .background(Color(.graySecondary).opacity(0.6))
            .cornerRadius(20)
            .overlay(
                RoundedRectangle(cornerRadius: 20)
                    .stroke(.gray, lineWidth: 1)
                    .opacity(0.5)
            )
    }
}

struct ManualDeviceCodeInput: View {
    enum FieldFocus {
        case fieldOne, fieldTwo, fieldThree, fieldFour, fieldFive
    }

    @FocusState private var fieldFocusState: FieldFocus?
    @State var fieldOne: String = ""
    @State var fieldTwo: String = ""
    @State var fieldThree: String = ""
    @State var fieldFour: String = ""
    @State var fieldFive: String = ""

    var body: some View {
        VStack {
            HStack(spacing: 15) {
                TextField("", text: $fieldOne)
                    .modifier(CharInputModifier(char: $fieldOne))
                    .onChange(of: fieldOne) { newVal in
                        if !"NO".contains(newVal) {
                            fieldOne = ""
                        } else if newVal.count == 1 {
                            fieldFocusState = .fieldTwo
                        }
                    }
                    .focused($fieldFocusState, equals: .fieldOne)

                TextField("", text: $fieldTwo)
                    .modifier(CharInputModifier(char: $fieldTwo))
                    .onChange(of: fieldTwo) { newVal in
                        if newVal.count == 1 {
                            fieldFocusState = .fieldThree
                        } else if newVal.count == 0 {
                            fieldFocusState = .fieldOne
                        }
                    }
                    .focused($fieldFocusState, equals: .fieldTwo)

                TextField("", text: $fieldThree)
                    .modifier(CharInputModifier(char: $fieldThree))
                    .onChange(of: fieldThree) { newVal in
                        if newVal.count == 1 {
                            fieldFocusState = .fieldFour
                        } else if newVal.count == 0 {
                            fieldFocusState = .fieldTwo
                        }
                    }
                    .focused($fieldFocusState, equals: .fieldThree)

                TextField("", text: $fieldFour)
                    .modifier(CharInputModifier(char: $fieldFour))
                    .onChange(of: fieldFour) { newVal in
                        if newVal.count == 1 {
                            fieldFocusState = .fieldFive
                        } else if newVal.count == 0 {
                            fieldFocusState = .fieldThree
                        }
                    }
                    .focused($fieldFocusState, equals: .fieldFour)

                TextField("", text: $fieldFive)
                    .modifier(CharInputModifier(char: $fieldFive))
                    .onChange(of: fieldFive) { newVal in
                        if newVal.count == 0 {
                            fieldFocusState = .fieldFour
                        }
                    }
                    .focused($fieldFocusState, equals: .fieldFive)
            }
        }
        // 将Toolbar附加到视图根层级,Sheet环境下才能正确渲染
        .toolbar {
            ToolbarItem(placement: .keyboard) {
                if fieldFocusState == .fieldOne {
                    HStack(spacing: 20) {
                        Button(action: { fieldOne = "N" }) {
                            Text("N")
                                .font(.headline)
                                .foregroundStyle(.white)
                        }
                        Divider()
                        Button(action: { fieldOne = "O" }) {
                            Text("O")
                                .font(.headline)
                                .foregroundStyle(.white)
                        }
                    }
                    .padding()
                    .background(Color.blue)
                    .cornerRadius(10)
                } else {
                    // 其他输入框聚焦时可以留空或添加其他内容
                    Spacer()
                }
            }
        }
    }
}

// 示例:如何在Sheet中调用该视图
struct ContentView: View {
    @State private var showSheet = false

    var body: some View {
        Button("打开输入弹窗") {
            showSheet = true
        }
        .sheet(isPresented: $showSheet) {
            ManualDeviceCodeInput()
                .padding()
        }
    }
}

关键修改说明

  1. Toolbar层级调整:将.toolbar从单个TextField移到ManualDeviceCodeInput的根VStack上,确保Sheet环境能识别到Toolbar配置。
  2. 条件渲染优化:调整Toolbar内的判断逻辑,确保只有当第一个输入框聚焦时才显示N/O按钮组,其他情况可以添加占位或留空。
  3. 视觉优化:给Toolbar按钮组添加背景色,避免在Sheet中与键盘背景融合,提升辨识度。

内容的提问来源于stack exchange,提问作者jonasmylesx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 08:24:59