I have an app that contains a TabView component in SwiftUI. My tabs contain both views that are in SwiftUI and UIViewControllers that use the UIViewRepresentable.
However, if I use a text field in a ScrollView component, when opening the keyboard, a "White View" appears behind the keyboard, almost imperceptible. It happens both on iOS 14 to the latest 16.4. Has anyone experienced this or is it really a SwiftUI bug?
Here is code and evidence for example:
struct View1: View {
@State private var fields: [String] = Array(repeating: "", count: 100)
var body: some View {
VStack {
ScrollView {
ForEach(0..<fields.count, id: \.self) { index in
TextField("Campo \(index + 1)", text: $fields[index])
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(10)
}
}
.onTapGesture {
hideKeyboard()
}
.padding()
}
}
private func hideKeyboard() {
UIApplication.shared.sendAction(
#selector(UIResponder.resignFirstResponder),
to: nil,
from: nil,
for: nil
)
}
}
struct ContentView: View {
var body: some View {
TabView {
View1()
.tabItem {
Label("Explorar", systemImage: "tray.and.arrow.down.fill")
}
ViewControllerWrapper()
.tabItem {
Label("Anunciar", systemImage: "tray.and.arrow.down.fill")
}
}
}
}
UIViewController in UiKit
import UIKit
import SwiftUI
struct ViewControllerWrapper: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> ViewController {
return ViewController()
}
func updateUIViewController(_ uiViewController: ViewController, context: Context) {}
}
class ViewController: UIViewController {
let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setupScrollView()
addTextFields()
setupTapGesture()
}
private func setupScrollView() {
view.addSubview(scrollView)
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
private func addTextFields() {
var previousTextField: UITextField?
for i in 0..<100 {
let textField = UITextField()
textField.placeholder = "Campo \(i+1)"
textField.borderStyle = .roundedRect
textField.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(textField)
NSLayoutConstraint.activate([
textField.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 20),
textField.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -20),
textField.heightAnchor.constraint(equalToConstant: 40),
])
if let previous = previousTextField {
textField.topAnchor.constraint(equalTo: previous.bottomAnchor, constant: 10).isActive = true
} else {
textField.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 20).isActive = true
}
previousTextField = textField
}
if let lastTextField = previousTextField {
lastTextField.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -20).isActive = true
}
}
private func setupTapGesture() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
tapGesture.cancelsTouchesInView = false
view.addGestureRecognizer(tapGesture)
}
@objc private func handleTap() {
view.endEditing(true)
}
}