Home > Mobile >  TextEditor is obscured by keyboard in SwiftUI
TextEditor is obscured by keyboard in SwiftUI

Time:06-10

I would like my TextEditors to avoid on-screen keyboard so that I can type something in and see it :) I guess I'm fine with targeting iOS 15. I believe I tried many solutions on the Internet that handle keyboard events and try to adjust some paddings/offsets etc, but none of them worked for me. Seems like TextFields do not have this issue at all (at least in iOS 15) as they stay visible (container view is scrolled as needed) even when keyboard appears on screen. I have no idea why this essential feature is not given for free... UIKit/UITextView seems to work without additional care from developer side.

So what do I need to do in order to be able to tap into the 3rd text editor (in the Notes section) in the example below and start typing immediately without having to manually scroll the view so that the editor is visible for me?

import SwiftUI

struct ContentView: View {
    @State private var text: String = ""
    
    init() {
        UITextView.appearance().backgroundColor = .clear
    }
    
    var body: some View {
        Form {
            TextEditor(text: $text)
                .frame(height: 300)
                .background(.yellow)
            TextEditor(text: $text)
                .frame(height: 300)
                .background(.mint)
            Section("Notes") {
                TextEditor(text: $text)
                    .frame(height: 300)
                    .background(.teal)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

CodePudding user response:

TextEditor is itself scroll view so OS just can be confused of what/where move/offset... Anyway, a possible approach in such situation (or whey you will wrap all those editors in ForEach) is to use focusable state and force everything up explicitly.

Tested with Xcode 13.4 / iOS 15.5

demo

Here is main part:

        TextEditor(text: $text).id(2)
            .focused($inFocus, equals: 2)
            .frame(height: 300)
            .background(.teal)
        
        if inFocus == 2 {
            Color.clear.frame(height: 300)
        }
    }
    .onChange(of: inFocus) { id in
        withAnimation {
            sp.scrollTo(id)
        }
    }

Complete test code is here

CodePudding user response:

To make use of ScrollView's automatic KeyboardAvoidance, whilst making sure Form is satisfied being inside a ScrollView (which it isn't by default), you can set a fixed width for the Form using a frame. The example below sets a frame that matches the size of the screen, but you can play around with the sizing if needed.

let screenSize = UIScreen.main.bounds.size
    
    var body: some View {
        ScrollView {
            Form {
                TextEditor(text: $text)
                    .frame(height: 300)
                    .background(.yellow)
                TextEditor(text: $text)
                    .frame(height: 300)
                    .background(.mint)
                Section("Notes") {
                    TextEditor(text: $text)
                        .frame(height: 300)
                        .background(.teal)
                }
            }
            .frame(width: screenSize.width, height: screenSize.height)
        }
    }
  • Related