It’s quite common to have a text field inside a scroll view in iOS apps. In order to provide a nice user experience we need to scroll the text field to a visible range when it becomes active and the keyboard appears on the screen. In UIKit we can call scrollRectToVisible() method in the scroll view to achieve this. What would be the simplest way to achieve this functionality in SwiftUI?
![A screenshot of an iPhone with a list of text fields and an on-screen keyboard where the active text field scrolls to a visible range](https://nilcoalescing.com/static/blog/ScrollTextFieldIntoVisibleRange/focused.JWpgAN9doFEyVwOL09DxsOfeanfhBtiPcnsWNJ-T0GE.png)
We wanted to wrap as few UIKit views as possible, so we decided to wrap only UITextField
. This approach works when the text field is a child (direct or nested) of SwiftUI ScrollView, list display or form.
We will examine a simple example of a List
A display with multiple text fields inside. We use our custom display TextFieldWithKeyboardObserver
Instead of regular SwiftUI TextField
.
List {
ForEach(0...textStrings.count - 1, id: .self) { index in
TextFieldWithKeyboardObserver(
text: self.$textStrings[index],
placeholder: "TextField number (index + 1)"
)
}
}
You can find the Complete code for the TextFieldWithKeyboardObserver file in our GitHub repository. You can read more about wrapping UIKit views Apple documentation on UIViewRepresentable.
![Integrating SwiftUI in UIKit apps by Natalia Panprovova book cover](https://nilcoalescing.com/static/BookBanner/Book-cover.UoiQYaybPkUU9hftrulXRS0HwGI-eo1P_kKBA4-ZR6c.png)
![Integrating SwiftUI in UIKit apps by Natalia Panprovova book cover](https://nilcoalescing.com/static/BookBanner/Book-cover-dark.8KoOlssgOrnpoHPLxnyA3JnfIBiCJXAeP-wTua5tCgA.png)
SwiftUI integration in UIKit apps
SwiftUI integration intoUIKit apps
A detailed guide to the gradual adoption of SwiftUI in UIKit projects.
- Discover different ways to add SwiftUI views to existing UIKit projects
- Use Xcode previews when designing and building a user interface
- Update your UIKit apps with iOS 16 features such as Swift Charts and lock screen widgets
- Move larger parts of your apps to SwiftUI while reusing UIKit’s built-in views and controllers
The first interesting part is that instead of returning normal UITextField
B makeUIView()
In the method, we return our subclass UITextFieldWithKeyboardObserver
And we call his method setupKeyboardObserver()
.
func makeUIView(context: Context) -> UITextField {
let view = UITextFieldWithKeyboardObserver()
view.delegate = context.coordinator
view.text = text
view.placeholder = placeholder
view.setupKeyboardObserver()
return view
}
In this method our view is subscribed to keyboard updates.
func setupKeyboardObserver() {
keyboardPublisher = KeybordManager
.shared
.$keyboardFrame
.receive(on: DispatchQueue.main)
.sink { [weak self] keyboardFrame in
if
let strongSelf = self,
let keyboardFrame = keyboardFrame
{
strongSelf.update(with: keyboardFrame)
}
}
}
and in update()
A method that we check if our text field is a child of a UIScrollView
. It doesn’t have to be a direct child, just anywhere in the world UIScrollView
The hierarchy of. We then define the inserts and tell the parent’s scrollview to scroll the frame of our text field to a visible range.
func update(with keyboardFrame: CGRect) {
if
let parentScrollView = superview(of: UIScrollView.self),
isFirstResponder
{
let keyboardFrameInScrollView = parentScrollView.convert(
keyboardFrame,
from: UIScreen.main.coordinateSpace
)
let scrollViewIntersection = parentScrollView.bounds
.intersection(keyboardFrameInScrollView).height
let contentInsets = UIEdgeInsets(
top: 0.0, left: 0.0,
bottom: scrollViewIntersection, right: 0.0
)
parentScrollView.contentInset = contentInsets
parentScrollView.scrollIndicatorInsets = contentInsets
parentScrollView.scrollRectToVisible(frame, animated: true)
}
}
Another thing to keep in mind is to set the inserted content back to it zero
When the text field ends with editing. We do it in Coordinator
A class that acts as a delegate for our text field.
func textFieldDidEndEditing(_ textField: UITextField) {
if let parentScrollView = textField.superview(of: UIScrollView.self) {
if !(parentScrollView.currentFirstResponder() is UITextFieldWithKeyboardObserver) {
parentScrollView.contentInset = .zero
}
}
}
You can find the complete example project with code for the keyboard viewer and other supporting functionality here GitHub folder for this article.
If you enjoy our blog and would like to support us, you can Sponsor us on GitHub.
For blog updates and development tips, follow us on Twitter @nilcoalescing.