SwiftUI TextField 可触摸区域

Giu*_*nza 26 swiftui

SwiftUI 布局与我们习惯的非常不同。目前我正在与TextFields战斗。特别是他们的可触摸区域。

TextField(
    .constant(""),
    placeholder: Text("My text field")
    )
        .padding([.leading, .trailing])
        .font(.body)
Run Code Online (Sandbox Code Playgroud)

这导致一个非常小的TextField(高度明智的)

在此处输入图片说明

添加框架修改器修复了问题(视觉上)

TextField(
    .constant(""),
    placeholder: Text("My text field")
    ).frame(height: 60)
        .padding([.leading, .trailing])
        .font(.body)
Run Code Online (Sandbox Code Playgroud)

但可触摸区域保持不变。

我知道框架修饰符除了将 textField 包装在另一个具有指定高度的视图中之外什么都不做。

是否有任何相当于resizable()Image,将允许更高的文本字段与更广泛的可触摸区域?

Dyl*_*and 30

该解决方案仅需要 a @FocusState、 anonTapGesture和 a.background带有颜色,并允许用户点击任何地方(包括填充区域)来聚焦该字段。使用 iOS 15 进行测试。

struct MyView: View {
    @Binding var text: String
    @FocusState private var isFocused: Bool
    var body: some View {
        TextField("", text: $text)
            .padding()
            .background(Color.gray)
            .focused($isFocused)
            .onTapGesture {
                isFocused = true
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

奖金:

如果您发现自己在多个文本字段上执行此操作,则进行自定义TextFieldStyle将使事情变得更容易:

struct TappableTextFieldStyle: TextFieldStyle {
    @FocusState private var textFieldFocused: Bool
    func _body(configuration: TextField<Self._Label>) -> some View {
        configuration
            .padding()
            .background(Color.gray)
            .focused($textFieldFocused)
            .onTapGesture {
                textFieldFocused = true
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后将其应用到您的文本字段:

TextField("", text: $text)
    .textFieldStyle(TappableTextFieldStyle())
Run Code Online (Sandbox Code Playgroud)


Cas*_*gen 7

用按钮解决

如果您不介意使用Introspect,您可以通过保存 UITextField 并调用becomeFirstResponder()按钮按下来实现。

extension View {
    public func textFieldFocusableArea() -> some View {
        TextFieldButton { self.contentShape(Rectangle()) }
    }
}

fileprivate struct TextFieldButton<Label: View>: View {
    init(label: @escaping () -> Label) {
        self.label = label
    }
    
    var label: () -> Label
    
    private var textField = Weak<UITextField>(nil)
    
    var body: some View {
        Button(action: {
            self.textField.value?.becomeFirstResponder()
        }, label: {
            label().introspectTextField {
                self.textField.value = $0
            }
        }).buttonStyle(PlainButtonStyle())
    }
}

/// Holds a weak reference to a value
public class Weak<T: AnyObject> {
    public weak var value: T?
    public init(_ value: T?) {
        self.value = value
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

TextField(...)
    .padding(100)
    .textFieldFocusableArea()
Run Code Online (Sandbox Code Playgroud)

由于我自己也使用这个,我会在github上保持更新:https : //gist.github.com/Amzd/d7d0c7de8eae8a771cb0ae3b99eab73d

使用 ResponderChain 的新解决方案

Button 解决方案将添加可能不需要的样式和动画,因此我现在使用使用ResponderChain 包的新方法

import ResponderChain

extension View {
    public func textFieldFocusableArea() -> some View {
        self.modifier(TextFieldFocusableAreaModifier())
    }
}

fileprivate struct TextFieldFocusableAreaModifier: ViewModifier {
    @EnvironmentObject private var chain: ResponderChain
    @State private var id = UUID()
    
    func body(content: Content) -> some View {
        content
            .contentShape(Rectangle())
            .responderTag(id)
            .onTapGesture {
                chain.firstResponder = id
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

您必须在 SceneDelegate 中将 ResponderChain 设置为环境对象,请查看 ResponderChain 的 README 以获取更多信息。


小智 0

我不知道哪个更适合你。所以,我发布了两个解决方案。

1)如果只想缩小输入区域。

在此输入图像描述

  var body: some View {
     Form {
        HStack {
           Spacer().frame(width: 30)
           TextField("input text", text: $inputText)
           Spacer().frame(width: 30)
        }
     }
   }
Run Code Online (Sandbox Code Playgroud)

2)缩小整个表格区域

在此输入图像描述

  var body: some View {
    HStack {
        Spacer().frame(width: 30)
        Form {
            TextField("input text", text: $restrictInput.text)
        }
        Spacer().frame(width: 30)
    }
 }
Run Code Online (Sandbox Code Playgroud)