我有一个 SwiftUI List
(在 macOS 上),我想在每一行显示一些内容TextFields
,并观察焦点在选择或取消选择单个文本字段时如何变化。如果我只在每一行List
显示一个纯文本TextField
,这似乎可以正常工作,但如果我将其嵌入视图TextField
内部Button
,焦点更改通知将停止工作。我想要“按钮”功能,以便用户可以点击行上的任意位置并激活文本字段,而不是精确点击文本字段边界。
以下是一些代码来演示问题。List
如果您RowView
取消注释 (1),则焦点更改通知会起作用。如果您注释掉 (1) 并取消注释RowViewWithButton
(2),则按钮功能会起作用,但我无法再收到焦点更改通知。
import SwiftUI
// test on macOS target
struct ContentView: View {
@State private var textFields = Array(repeating: "", count: 4)
@FocusState private var focusedField: Int?
var body: some View {
List(0..<4, id: \.self) { index in
// 1. works with focus notification changes
RowView(index: index, text: $textFields[index], focusedField: $focusedField)
// 2. button works, but no focus notifications on text field
//RowViewWithButton(index: index, text: $textFields[index], focusedField: $focusedField)
}
}
}
struct RowView: View {
let index: Int
@Binding var text: String
@FocusState.Binding var focusedField: Int?
var body: some View {
HStack {
Text("Row \(index + 1):")
TextField("", text: $text)
.multilineTextAlignment(.leading)
.focused($focusedField, equals: index)
}
.onChange(of: focusedField) { newFocus in
if newFocus == index {
print("TextField \(index) is in focus")
} else {
print("TextField \(index) lost focus")
}
}
.padding(.vertical, 4)
}
}
struct RowViewWithButton: View {
let index: Int
@Binding var text: String
@FocusState.Binding var focusedField: Int?
var body: some View {
Button (action: {
print("RowView - button selected at index \(index)")
focusedField = index
}) {
HStack {
Text("Row \(index + 1):")
TextField("", text: $text)
.multilineTextAlignment(.leading)
.focused($focusedField, equals: index)
}
.onChange(of: focusedField) { newFocus in
if newFocus == index {
print("TextField \(index) is in focus")
} else {
print("TextField \(index) lost focus")
}
}
.foregroundColor(.primary)
}
.buttonStyle(BorderlessButtonStyle())
.padding(.vertical, 4)
}
}
#Preview {
ContentView()
.frame(width: 320, height: 250)
}
有趣的是,当我在 iOS 上测试时,两种情况下都可以正常工作。但我需要适用于 macOS 的解决方案。
更新:
使用onTapGesture
而不是Button
确实可以改善情况,但会引入一个新错误。如果我最初点击该行,它最初可以正常工作,点击任意位置都会激活文本字段。但是在输入一些文本并按下回车键(或以任何方式失去焦点)后,如果我尝试点击 所在的行部分TextField
,它只有在点击现有文本时才有效......而不是点击它前面的空白处。
我将其添加.background(.red)
到了 HStack,并.background(.blue)
添加了一个屏幕截图来演示该问题。
在 中
RowViewWithButton
,将按钮样式更改为.plain
:在 macOS 上,
.borderless
吸收所有交互,直接影响可聚焦视图,同时.plain
保留正常交互。更新:
由于更改样式
.plain
会导致在 TextField 中键入空格时触发按钮,因此最简单的方法可能是根本不使用按钮,而只使用.onTapGesture
:从您的评论来看
I want the 'button' functionality so that the user can tap anywhere on the row and have the text field activated, instead of tapping exactly on the text field boundary
。无需使用 即可实现此目的
Button
。尝试使用这种方法来捕获列表行上的
.overlay(...)
点击HStack
,同时仍然能够
同时检查焦点的变化。
示例代码,已在 MacOS 15.4 上测试
}