SwiftUI 焦点状态 API 环境变量不起作用
SwiftUI Focus State API environment variable not working
当我们想要观察 SwiftUI 文本字段的焦点状态时,环境值 isFocused 似乎不起作用。除了将值传递给 TextFieldStyle 的初始化(我们必须为每个文本字段执行此操作)之外,还有其他方法可以做到这一点吗?也不适用于设备。
当文本域的焦点状态发生变化时,更改文本域外观的首选方式是什么?
示例:
SwiftUI TextFieldStyle定义如下:
struct MyTextFieldStyle: TextFieldStyle {
@Environment(\.isFocused) var isFocused: Bool
func _body(configuration: TextField<_Label>) -> some View {
configuration
.padding()
.overlay(
RoundedRectangle(
cornerRadius: 10.0, style: .continuous
)
.stroke(isFocused ? .green : .gray, lineWidth: 3)
)
.accentColor(Color(uiColor: .white))
}
}
#if DEBUG
private struct TestView: View {
@FocusState private var focusedTextfield: FocusField?
enum FocusField: Hashable {
case textfield1, textfield2
}
var body: some View {
VStack(spacing: 16) {
TextField("hello", text: .constant("Hi"))
.textFieldStyle(MyTextFieldStyle())
.focused($focusedTextfield, equals: .textfield1)
TextField("hello", text: .constant("Hi"))
.textFieldStyle(MyTextFieldStyle())
.focused($focusedTextfield, equals: .textfield2)
}.onAppear {
focusedTextfield = .textfield1
}
}
}
struct MyTextfieldStyle_Previews: PreviewProvider {
static var previews: some View {
ZStack {
TestView()
}
}
}
#endif
// 编辑:基于
的工作解决方案
struct MyTextFieldStyle: TextFieldStyle {
@FocusState var isFocused: Bool
func _body(configuration: TextField<_Label>) -> some View {
configuration
.padding()
.focused($isFocused)
.overlay(
RoundedRectangle(
cornerRadius: 10.0, style: .continuous
)
.stroke(isFocused ? .green : .gray, lineWidth: 3)
)
.accentColor(Color(uiColor: .white))
}
}
#if DEBUG
private struct TestView: View {
@FocusState private var focusedTextfield: FocusField?
enum FocusField: Hashable {
case textfield1, textfield2
}
var body: some View {
VStack(spacing: 16) {
TextField("hello", text: .constant("Hi"))
.textFieldStyle(MyTextFieldStyle())
.focused($focusedTextfield, equals: .textfield1)
TextField("hello", text: .constant("Hi"))
.textFieldStyle(MyTextFieldStyle())
.focused($focusedTextfield, equals: .textfield2)
}.onAppear {
DispatchQueue.main.async {
focusedTextfield = .textfield1
}
}
}
}
struct MyTextFieldStyle_Previews: PreviewProvider {
static var previews: some View {
ZStack {
TestView()
}
}
}
#endif
您遇到了几个不同的问题:
- 据我所知,自定义 TextFieldStyles 没有 public 协议。但是您可以使用相同的行为来创建自己的 TextField 结构。
- 在此结构中,您可以使用另一个本地
@FocusState
变量。我没有让环境变量正常工作,但这确实有效。
- 要在主视图中设置初始焦点,您必须使用
asyncAfter
等待一段时间
struct MyTextField: View {
@FocusState private var isFocused: Bool
let title: String
@Binding var text: String
init(_ title: String, text: Binding<String>) {
self.title = title
self._text = text
}
var body: some View {
TextField(title, text: $text)
.focused($isFocused) // important !
.padding()
.overlay(
RoundedRectangle(
cornerRadius: 10.0, style: .continuous
)
.stroke(isFocused ? .green : .gray, lineWidth: 3)
)
.accentColor(Color(uiColor: .red))
}
}
struct ContentView: View {
@FocusState private var focusedTextfield: FocusField?
enum FocusField: Hashable {
case textfield1, textfield2
}
@State private var input1 = "Hi"
@State private var input2 = "Hi2"
var body: some View {
VStack(spacing: 16) {
MyTextField("hello", text: $input1)
.focused($focusedTextfield, equals: .textfield1)
MyTextField("hello", text: $input2)
.focused($focusedTextfield, equals: .textfield2)
// test for changing focus
Button("Field 1") { focusedTextfield = .textfield1}
Button("Field 2") { focusedTextfield = .textfield2}
}
.padding()
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
focusedTextfield = .textfield1
}
}
}
}
当我们想要观察 SwiftUI 文本字段的焦点状态时,环境值 isFocused 似乎不起作用。除了将值传递给 TextFieldStyle 的初始化(我们必须为每个文本字段执行此操作)之外,还有其他方法可以做到这一点吗?也不适用于设备。
当文本域的焦点状态发生变化时,更改文本域外观的首选方式是什么?
示例:
SwiftUI TextFieldStyle定义如下:
struct MyTextFieldStyle: TextFieldStyle {
@Environment(\.isFocused) var isFocused: Bool
func _body(configuration: TextField<_Label>) -> some View {
configuration
.padding()
.overlay(
RoundedRectangle(
cornerRadius: 10.0, style: .continuous
)
.stroke(isFocused ? .green : .gray, lineWidth: 3)
)
.accentColor(Color(uiColor: .white))
}
}
#if DEBUG
private struct TestView: View {
@FocusState private var focusedTextfield: FocusField?
enum FocusField: Hashable {
case textfield1, textfield2
}
var body: some View {
VStack(spacing: 16) {
TextField("hello", text: .constant("Hi"))
.textFieldStyle(MyTextFieldStyle())
.focused($focusedTextfield, equals: .textfield1)
TextField("hello", text: .constant("Hi"))
.textFieldStyle(MyTextFieldStyle())
.focused($focusedTextfield, equals: .textfield2)
}.onAppear {
focusedTextfield = .textfield1
}
}
}
struct MyTextfieldStyle_Previews: PreviewProvider {
static var previews: some View {
ZStack {
TestView()
}
}
}
#endif
// 编辑:基于
struct MyTextFieldStyle: TextFieldStyle {
@FocusState var isFocused: Bool
func _body(configuration: TextField<_Label>) -> some View {
configuration
.padding()
.focused($isFocused)
.overlay(
RoundedRectangle(
cornerRadius: 10.0, style: .continuous
)
.stroke(isFocused ? .green : .gray, lineWidth: 3)
)
.accentColor(Color(uiColor: .white))
}
}
#if DEBUG
private struct TestView: View {
@FocusState private var focusedTextfield: FocusField?
enum FocusField: Hashable {
case textfield1, textfield2
}
var body: some View {
VStack(spacing: 16) {
TextField("hello", text: .constant("Hi"))
.textFieldStyle(MyTextFieldStyle())
.focused($focusedTextfield, equals: .textfield1)
TextField("hello", text: .constant("Hi"))
.textFieldStyle(MyTextFieldStyle())
.focused($focusedTextfield, equals: .textfield2)
}.onAppear {
DispatchQueue.main.async {
focusedTextfield = .textfield1
}
}
}
}
struct MyTextFieldStyle_Previews: PreviewProvider {
static var previews: some View {
ZStack {
TestView()
}
}
}
#endif
您遇到了几个不同的问题:
- 据我所知,自定义 TextFieldStyles 没有 public 协议。但是您可以使用相同的行为来创建自己的 TextField 结构。
- 在此结构中,您可以使用另一个本地
@FocusState
变量。我没有让环境变量正常工作,但这确实有效。 - 要在主视图中设置初始焦点,您必须使用
asyncAfter
等待一段时间
struct MyTextField: View {
@FocusState private var isFocused: Bool
let title: String
@Binding var text: String
init(_ title: String, text: Binding<String>) {
self.title = title
self._text = text
}
var body: some View {
TextField(title, text: $text)
.focused($isFocused) // important !
.padding()
.overlay(
RoundedRectangle(
cornerRadius: 10.0, style: .continuous
)
.stroke(isFocused ? .green : .gray, lineWidth: 3)
)
.accentColor(Color(uiColor: .red))
}
}
struct ContentView: View {
@FocusState private var focusedTextfield: FocusField?
enum FocusField: Hashable {
case textfield1, textfield2
}
@State private var input1 = "Hi"
@State private var input2 = "Hi2"
var body: some View {
VStack(spacing: 16) {
MyTextField("hello", text: $input1)
.focused($focusedTextfield, equals: .textfield1)
MyTextField("hello", text: $input2)
.focused($focusedTextfield, equals: .textfield2)
// test for changing focus
Button("Field 1") { focusedTextfield = .textfield1}
Button("Field 2") { focusedTextfield = .textfield2}
}
.padding()
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
focusedTextfield = .textfield1
}
}
}
}