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

macOS SwiftUI中Sheet内TextField无法获取焦点问题求助

macOS SwiftUI Sheet内TextField无法获取焦点问题及解决方法

问题描述

在macOS应用的SwiftUI Sheet中放置TextField后,Sheet弹出时TextField无法获取任何焦点,焦点状态似乎被系统机制覆盖。但将TextField放在Sheet外时可正常工作,怀疑这是Sheet、Alert等弹出组件的通用Bug。

问题验证代码

无法正常工作的代码

import SwiftUI
import PlaygroundSupport

struct ContentView: View {
    @State private var text = ""
    @FocusState private var focus: Bool
    @State private var showSheet = false
    
    var body: some View {
        Button(action : { showSheet = true }) {
            Label("Add", systemImage: "plus")
        }
        .frame(width: 150, height: 50)
        .sheet(isPresented: $showSheet) {
            LabeledContent {
                TextField("Enter Text", text: $text, onCommit: { focus = false })
                    .focused($focus) // 此设置无效
            } label: {
                Text("Field")
            }
            .padding()
            .defaultFocus($focus, false) // 此设置也无效
        }
    }
}

PlaygroundPage.current.setLiveView(ContentView())

可正常工作的代码

import SwiftUI
import PlaygroundSupport

struct ContentView: View {
    @State private var text = ""
    @FocusState private var focus: Bool

    var body: some View {
        LabeledContent {
            TextField("Enter Text", text: $text, onCommit: { focus = false })
                .focused($focus)
        } label: {
            Text("Field")
        }
        .padding()
        .defaultFocus($focus, false)
    }
}

PlaygroundPage.current.setLiveView(ContentView())

测试环境:Xcode 15.0.1,macOS 14.1.1

临时解决方法

方法一:延迟触发焦点设置

利用onAppear结合短暂延迟,手动将焦点赋予TextField:

.sheet(isPresented: $showSheet) {
    LabeledContent {
        TextField("Enter Text", text: $text, onCommit: { focus = false })
            .focused($focus)
    } label: {
        Text("Field")
    }
    .padding()
    .onAppear {
        // 延迟0.1秒确保Sheet完成渲染后设置焦点
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            focus = true
        }
    }
}

方法二:通过AppKit底层API设置第一响应者

自定义基于NSViewRepresentable的TextField,直接调用AppKit的makeFirstResponder方法强制获取焦点:

struct FocusableTextField: NSViewRepresentable {
    @Binding var text: String
    @Binding var isFocused: Bool
    
    func makeNSView(context: Context) -> NSTextField {
        let textField = NSTextField()
        textField.delegate = context.coordinator
        textField.placeholderString = "Enter Text"
        return textField
    }
    
    func updateNSView(_ nsView: NSTextField, context: Context) {
        nsView.stringValue = text
        if isFocused && nsView.window?.firstResponder != nsView {
            nsView.window?.makeFirstResponder(nsView)
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, NSTextFieldDelegate {
        var parent: FocusableTextField
        
        init(_ parent: FocusableTextField) {
            self.parent = parent
        }
        
        func controlTextDidChange(_ obj: Notification) {
            guard let textField = obj.object as? NSTextField else { return }
            parent.text = textField.stringValue
        }
        
        func controlTextDidEndEditing(_ obj: Notification) {
            parent.isFocused = false
        }
    }
}

// 在ContentView中使用自定义TextField
.sheet(isPresented: $showSheet) {
    LabeledContent {
        FocusableTextField(text: $text, isFocused: $focus)
    } label: {
        Text("Field")
    }
    .padding()
    .onAppear {
        focus = true
    }
}

Bug说明

这是SwiftUI在macOS平台上针对弹出组件(Sheet、Alert)的已知焦点管理问题,在Xcode 15及macOS 14版本中存在,目前官方尚未修复,上述方法可作为临时解决方案绕过该问题。

内容的提问来源于stack exchange,提问作者Denny L.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 06:47:15