Disable keyboard avoidance for SwiftUI view

I'm having trouble disabling keyboard avoidance for a SwiftUI view that is embedded in a UIHostingController. When the UITextField becomes first responder, the SwiftUI view jumps out of the way for the keyboard, and I want it to stay in place.

To test this out, set the ViewController as the rootViewController in a UIKit App Delegate.

Code Block Swift
import SwiftUI
struct ContentView: View {
    var body: some View {
        Text("I want this text to stay put.")
/* THIS DOESN'T WORK
        .ignoresSafeArea(.keyboard)
*/
    }
}
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let textField = UITextField(frame: CGRect(x: 10, y: 100, width: 200, height: 50))
        textField.backgroundColor = .white
        textField.placeholder = "Tap here!"
        view.addSubview(textField)
        let button = UIButton(type: .system, primaryAction: UIAction(title: "Dismiss Keyboard", handler: { _ in
            textField.resignFirstResponder()
        }))
        button.frame = CGRect(x: 220, y: 100, width: 140, height: 50)
        view.addSubview(button)
        let hostingController = UIHostingController(rootView: ContentView())
        hostingController.view.frame = CGRect(x: 10, y: UIScreen.main.bounds.size.height - 510, width: UIScreen.main.bounds.size.width - 20, height: 500)
        view.addSubview(hostingController.view)
    }
}


Accepted Reply

Several other developers and I have been hit by this:
we've filed multiple feedbacks (FB8305409, FB8240609, FB8176223, and more) and would love to know of a solution for this 🙏🏻

Replies

Several other developers and I have been hit by this:
we've filed multiple feedbacks (FB8305409, FB8240609, FB8176223, and more) and would love to know of a solution for this 🙏🏻
This is still reproducible on the Xcode 12 GM seed. It really seems like a big issue - it basically prevents any developer from using SwiftUI inside a UIHostingController if there might be a keyboard-editable view on the screen.
I have finally found a solution!

Use this modifier which has been introduced with iPadOS 14 and set it to ignore the keyboard:
Code Block
.ignoresSafeArea(.keyboard)

This problem seems to be fixed in Xcode 12.2 beta
If you are looking for a solution that disables keyboard avoidance for UIHostingController and you're cool with shipping something slightly terrible, here you go: https://gist.github.com/steipete/da72299613dcc91e8d729e48b4bb582c
This is still reproducible on Xcode 12.1 GM. It seems to have been fixed in Xcode 12.2 beta 1 and onward
Fixed in 12.2, but does that mean it will be fixed for iOS 13?
For anyone scrolling past this post trying to figure out how to ignore the keyboard but maybe needs to maintain a Textfield's keyboard avoidance I did this ⤵

Scenario

I needed to have a Text view sit at the bottom of a page and then have a Textfield in the middle that would be affected by the keyboard's size.

Solution

What I ended up doing was placing both the Text view and the Textfield into their own VStack's and then placed both VStack's in a ZStack. Then I simply added the .ignoresSafeArea(.keyboard) modifier to the bottom VStack containing the Text view. So it looked something like this ⤵

Code Block swift
struct Home: View {
  var body: some View {
/**
Placing all elements in a ZStack allow us to place a layer with
the copyright info at the bottom, ignoring the keyboard safe area
while still maintaining spacer flexibility on the user input layer.
*/
ZStack {
/** Top content */
VStack(alignment: .center, content: {
Spacer()
Textfield("Textfield", text: $textObject)
Spacer()
})
/** Bottom Content */
VStack {
Spacer()
Text("Some Text")
}.ignoresSafeArea(.keyboard)
}
}
}


Problems

This solution isn't ideal because now the Textfield isn't reacting to the content in the View field. However, this solution will work well for some situations such as a login screen.

I hope this helped someone! 😊

Edit

You could also change the ZStack to a VStack and then place a Spacer().ignoresSafeArea(.keyboard) above the two inner VStack's. However, I found this creates weird behaviour occasionally as SwiftUI doesn't know what to space out as it'd technically have no where to go, but it some how still makes space.

Maybe it's just ramming the stacks together and hoping for the best?
Code Block
.ignoresSafeArea(.keyboard)

This seems like the answer to our issues, but we are experiencing this still in iOS 14 with Xcode 12.3.
I'm going to see if this is fixed in Xcode 12.3. The gist that @steipete linked to fixed another (slightly unrelated) issue I was having, which was an infinite layout crash that I was seeing on xcode 12.1 on iOS14 (only on notched devices):
  • Pin UIHostingController's view to the edges of a parent UIView

  • Translate that parent UIView to be partially below the screen

  • Resize the SwiftUI content to be a larger size than it was originally

You'll get an infinite layoutSubviews crash without that slightly terrible gist. SwiftUI can't seem to handle having partially off-screen content when safeAreas are in the mix.

There's something about SwiftUI's layout system that tries to automatically avoid those areas of the screen which causes lots of issues.
@rivrasd can you clarify your solution? I'm running into the same problem in which a UIHostingView's height becomes infinite when a TextField is tapped. I have a custom UIScrollView through the use of UIViewControllollerRepresentable and UIViewController. Ideally, when a TextField is tapped I don't want the UIScrollView to scroll at all. https://gist.github.com/schwjustin/71e83480e70ad0589902fd41a980e6c7
My workaround is placing a GeometryReader as the root View, within the body of whatever View needs to ignore the keyboard. I believe this works because the GeometryReader will expand to fill the entire area it's contained within, ignoring safe area insets in the process.

I can confirm this works when GeometryReader is the root View in a presented sheet, as well as the root view within an individual tab of a TabView.

3 years later and this is still an issue, sadly the gist snippet here doesn't work for me neither does the .ignoreSafeArea(.keyboard).