Reputation: 1258
Solution which worked for me
After struggling for days I finally manage to find the fix for view animation issue. For me keyboardWillChangeFrameNotification worked. The solution is discussed in the following thread:
Move textfield when keyboard appears swift
I've a bunch of views embedded inside stack view along with text view. I've given text view height of <= 120. In keyboardWillShow view doesn't animate despite adding code as required. I've played with duration value but it's all same result. I was wondering if it's due to text view? How to fix it?
@objc func keyboardWillShow(notification:NSNotification) {
guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
let keyboardScreenEndFrame = keyboardValue.cgRectValue
let keyboardFrame = view.convert(keyboardScreenEndFrame, from: view.window)
if #available(iOS 11.0, *) {
scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardFrame.height - view.safeAreaInsets.bottom, right: 0)
} else {
scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardFrame.height, right: 0)
}
scrollView.scrollIndicatorInsets = scrollView.contentInset
let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height + keyboardFrame.height - scrollView.bounds.size.height)
scrollView.setContentOffset(bottomOffset, animated: true)
UIView.animate(withDuration: 0.5, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
===
extension FirstViewController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
let estimatedSize = textView.sizeThatFits(textView.frame.size)
if estimatedSize.height > textViewMaxHeight {
if estimatedSize.height - textViewMaxHeight < textView.font!.lineHeight && !didExpandTextView {
didExpandTextView = true
var contentInset:UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.savedKbHeight, right: 0.0)
if let v = self.tabBarController?.tabBar {
contentInset.bottom -= v.frame.height
}
scrollView.contentInset = contentInset
scrollView.scrollIndicatorInsets = contentInset
if textView.isFirstResponder {
let fr = textView.frame
scrollView.scrollRectToVisible(fr, animated: false)
}
}
textView.isScrollEnabled = true
textView.showsVerticalScrollIndicator = true
} else {
if let lineHeight = textView.font?.lineHeight, Int(estimatedSize.height / lineHeight) != numberOfLines {
numberOfLines = Int(estimatedSize.height / textView.font!.lineHeight)
var contentInset:UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.savedKbHeight, right: 0.0)
print("contentInset: \(contentInset)")
scrollView.contentInset = contentInset
scrollView.scrollIndicatorInsets = contentInset
if textView.isFirstResponder {
let fr = textView.frame
scrollView.scrollRectToVisible(fr, animated: false)
}
didExpandTextView = false
}
textView.isScrollEnabled = false
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
Upvotes: 1
Views: 639
Reputation: 77477
Try this approach:
func textViewDidChange(_ textView: UITextView) {
let estimatedSize = textView.sizeThatFits(textView.frame.size)
textView.isScrollEnabled = estimatedSize.height > textViewMaxHeight
if !textView.isScrollEnabled {
let maxBottom = self.view.frame.height - self.savedKbHeight
// Use following line if you have Extended Edges set
// let maxBottom = self.view.frame.height - self.savedKbHeight - topLayoutGuide.length - bottomLayoutGuide.length
var r = self.textView.frame
r.size.height = estimatedSize.height
let tvBottom = self.scrollView.convert(r, to: self.view).maxX
if tvBottom > maxBottom {
self.scrollView.scrollRectToVisible(r, animated: true)
}
}
}
Edit Note: This is not intended to be Production Ready code. There are many things that affect layout / positioning, and there is no guarantee that just "dropping this in" will work in all situations.
Upvotes: 1