Keyboard dismissing for views that are added at run time UIKit. Telegram-Drawing-Text-Editing, ep. 1

Ivan Pryhara
2 min readNov 4, 2022

--

This article is the part of the project Telegram-Like-Drawing-Text-Editing. But you can find this information useful for your your own code.

The very first issue I encountered with was a keyboard-dismissing by tapping somewhere outside TextView when editing is active. Fairly straightforward task for predefined ui element. However it took forever to understand how to handle this for views that’s added at run time, e.g. our TextView which is the main object in our Text-Editing feature.

I tried a lot of different stuff, but the only one seems to me beautiful and logically correct.

I decided to handle dismissing as it’s usually done. By overriding touchesBegun(:with:) method.

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {guard let touch = touches.first else { return }
// Here!
guard let firstResponder = view.firstResponder else { return }

let point = touch.location(in: view)
if !firstResponder.bounds.contains(point) {
firstResponder.resignFirstResponder()
firstResponder.endEditing(true)
}
}

At the first glance everything seems usual. Except a thing that we need to get a first responder which is the TextView we are currently using and which probably want to dismiss.

Don’t try to find firstResponder property for UIView class because there’s no one.

So we need to implement such property by ourselves. Made a little research I found pretty neat solution on StackOverflow:

extension UIView {
var firstResponder: UIView? {
guard !isFirstResponder else { return self }

for subview in self.subviews {
if let firstREsponder = subview.firstResponder {
return firstREsponder
}
}
return nil
}
}

Here we check whether current view is first responder, if so return that view, otherwise move on.

The second step is iterating through all subviews our view has and then check for each view that this view is first responder by calling property we just implemented. When we find our view that is first responder we return it back

However, if we haven’t found anything nil would returned.

Let’s go back to touchesBegun(:with:) method, to be precise to this piece of code:

let point = touch.location(in: view)if !firstResponder.bounds.contains(point) {
firstResponder.resignFirstResponder()
firstResponder.endEditing(true)
}

We track touch location in our parent view. Then we just check whether this touch was performed in the bounds of our TextView. If the answer is NO we just call default methods that are used for dismissing the keyboard.

That’s it. Thank you for your attention, I hope you found that article useful.

--

--

Ivan Pryhara
Ivan Pryhara

No responses yet