TextEditor is obscured by keyboard in SwiftUI
Asked Answered
S

1

7

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()
    }
}
Stellarator answered 8/6, 2022 at 17:59 Comment(0)
V
7

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

Vacuum answered 8/6, 2022 at 19:35 Comment(4)
Thank you, this does work indeed. But after posting the sample code in my question I realised that I need my TextEditors to be inside Form (instead of ScrollView) and with Form the proposed solution does not work for the first try (when the app was just launched), but it starts working if you continue to switch focuses. Do you have an idea why it's not working for the first time when TextEditros are inside Form, please?Stellarator
And what was the purpose of Color.clear.frame(height: 300)? I'm asking because with TextEditors inside ScrollView your solutions works even without if inFocus == 2 { Color.clear.frame(height: 300) }Stellarator
@Stellarator if your TextEditor doesn't have 300 height of views under it, the scroll view will have no room to grow. So by adding Color.clear it adds room and you can't see it. 300 is the height a keyboard takes up.Cochard
Even your sample code is not working. It only works if we have all three textEditors. I tried replacing middle with textfield. And your code get abnormalLingwood

© 2022 - 2024 — McMap. All rights reserved.