I’m still quite new to UIKit and I’m working on a chat application, similar to Messages, for learning purpose. I’ve faced an issue when the content of UICollectionView
jumps around the top while trying to dismiss the keyboard with the swipe. It happens only when the content is scrolled down from the very top of UICollectionView
so that there’s a gap between the content and the top of the view.
Here is what it looks like:
I use UICollectionViewController
as a view controller and override its inputAccessoryView
property with my custom input view (which is just a UIView
with UITextView
in it).
I’ve tested it on different devices and simulators with iOS 16 and 17 and I’ve noticed that this only happens on the devices that have safe area at the bottom (iPhone 15 and iPhone) and doesn’t seem to occur on devices without it (iPhone 8 and SE). It makes me think it has something to do with the inputAccessoryView
changing its size dynamically to satisfy the safeAreaLayoutGuide
constraint. However, even if it is caused by that, I don’t know how to make it work properly and achieve the same smooth behaviour as Messages has.
Here’s the code for my UICollectionViewController
:
class PlaygroundViewController: UICollectionViewController {
init() {
super.init(collectionViewLayout: layout)
collectionView.register(PlaygroundCellView.self, forCellWithReuseIdentifier: "cell")
collectionView.keyboardDismissMode = .interactive
}
override func viewDidLoad() {
becomeFirstResponder()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var playgroundInputView: UIView = { PlaygroundInputView(frame: .zero) }()
override var canBecomeFirstResponder: Bool { true }
override var inputAccessoryView: UIView? { playgroundInputView }
}
And here’s the custom input view:
class PlaygroundInputView: UIView {
lazy var textView: UITextView = {
var view = UITextView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemGray6
view.isScrollEnabled = false
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.backgroundColor = .systemGray3
addSubview(textView)
NSLayoutConstraint.activate([
textView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
textView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),
textView.topAnchor.constraint(equalTo: topAnchor, constant: 8),
textView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -8),
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var intrinsicContentSize: CGSize { .zero }
}
Is this even a correct approach? Most of the answers I found online suggest doing it like this (or with slight modifications that don’t change much) but I can’t get rid of these weird jumps.
I have tried using just a simple UIViewController
instead of UICollectionViewController
but it adds a lot more problems and I have to handle contentInset
manually which makes the behaviour even worse.
I’ve also tried using inputAccessoryViewController
with the similar view inside and inputView?.allowsSelfSizing = true
, but the behaviour stayed the same.
A lot of the other things I’ve tried didn’t bring anything good, so I won’t even mention them. Hope someone can help me with this.
m_ornj is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.