Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI TextField touchable Area

Tags:

swiftui

SwiftUI layout is very different from what we are used to. Currently I'm fighting against TextFields. Specifically their touchable Area.

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

This results in a very small TextField (height wise)

enter image description here

Adding the frame modifier fixes the issue (visually)

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

but the touchable area remains the same.

I'm aware of the fact that the frame modifier does nothing else other than wrap the textField in another View with the specified height.

Is there any equivalent to resizable() for Image that will allow a taller TextField with wider touchable Area?

like image 936
Giuseppe Lanza Avatar asked Jun 27 '19 17:06

Giuseppe Lanza


1 Answers

Solution with Button

If you don't mind using Introspect you can do it by saving the UITextField and calling becomeFirstResponder() on button press.

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     } } 

Example usage:

TextField(...)     .padding(100)     .textFieldFocusableArea() 

Since I use this myself as well, I will keep it updated on github: https://gist.github.com/Amzd/d7d0c7de8eae8a771cb0ae3b99eab73d

New solution using ResponderChain

The Button solution will add styling and animation which might not be wanted therefore I now use a new method using my ResponderChain package

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             }     } } 

You'll have to set the ResponderChain as environment object in the SceneDelegate, check the README of ResponderChain for more info.

like image 140
Casper Zandbergen Avatar answered Sep 21 '22 23:09

Casper Zandbergen