问题
When the keyboard appears, I am shifting the bottom-most view up by a constant. But due to this the hight of the UITableView also reduces and the cells that were previously visible are no longer visible. What I want to do is scroll the table view up by a height equal to the keyboard's height so that at least the last messages will be visible like they were before the keyboard appeared.
The last yellow cell that was half-visible in the image on left should be visible like that on the right too.
I think scrolling the table view by an offset equal to keyboard height should work. But I can't find a method to arbitrarily scroll a tableview. I tried messing with y value of content offset but that is creating odd behavior. I would also really appreciate if the transition would be smooth so the table view cells look like they are moving up along with the keyboard. Kind of how all the other chat apps set this transition up.
EDIT 3
Okay so I have figured the math out for both keyboard appearing and disappearing. But the animation still jumps a bit before following the rest of the animation's curve. How can I fix that?
NotificationCenter.default.addObserver(self, selector: #selector(keyboardNotification), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
extension DiscussionsViewController {
@objc func keyboardNotification(notification: NSNotification) {
guard let userInfo = notification.userInfo else { return }
let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let endFrameY = endFrame?.origin.y ?? 0
let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
let offset = -1 * (endFrame?.size.height ?? 0.0)
if endFrameY >= UIScreen.main.bounds.size.height {
self.discussionsMessageBoxBottomAnchor.constant = 0.0
let yOffset = discussionChatView.discussionTableView.contentOffset.y
discussionChatView.discussionTableView.contentOffset.y = yOffset + 2 * offset
} else {
self.discussionsMessageBoxBottomAnchor.constant = offset
let yOffset = discussionChatView.discussionTableView.contentOffset.y
discussionChatView.discussionTableView.contentOffset.y = yOffset - offset
}
discussionChatView.discussionTableView.scrollIndicatorInsets = discussionChatView.discussionTableView.contentInset
UIView.animate(
withDuration: duration,
delay: TimeInterval(0),
options: animationCurve,
animations: { self.view.layoutIfNeeded() },
completion: nil)
}
}
Screen recording of choppy animation
EDIT 2
The animation when the keyboard appears is a lot better now. The cells move up with the same animation properties. Though still there is a jump up when the last cell is at the bottom. I am still having trouble figuring out the math for offset when the keyboard will disappear. And also how the animation can get smoother.
extension DiscussionsViewController {
@objc func keyboardNotification(notification: NSNotification) {
guard let userInfo = notification.userInfo else { return }
let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let endFrameY = endFrame?.origin.y ?? 0
let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
let offset = -1 * (endFrame?.size.height ?? 0.0)
if endFrameY >= UIScreen.main.bounds.size.height {
self.discussionsMessageBoxBottomAnchor.constant = 0.0
//STILL NOT AS EXPECTED
let yOffset = discussionChatView.discussionTableView.contentOffset.y
discussionChatView.discussionTableView.contentOffset.y = yOffset - offset
} else {
self.discussionsMessageBoxBottomAnchor.constant = offset
let yOffset = discussionChatView.discussionTableView.contentOffset.y
discussionChatView.discussionTableView.contentOffset.y = yOffset - offset
}
UIView.animate(
withDuration: duration,
delay: TimeInterval(0),
options: animationCurve,
animations: { self.view.layoutIfNeeded() },
completion: nil)
}
}
EDIT 1
This is shifting the cells as expected, but the animation is choppy with cells going missing and not in line with the rest of the animation. Is there a way to override the animation curve and other properties? Should I put this inside UIView.animate
with animated false for setContentOffset
? I am not sure how to work this out completely though.
if endFrameY >= UIScreen.main.bounds.size.height {
self.discussionsMessageBoxBottomAnchor.constant = 0.0
self.discussionChatView.discussionTableView.contentInset.bottom = 0
} else {
let offset = -1 * (endFrame?.size.height ?? 0.0)
self.discussionsMessageBoxBottomAnchor.constant = offset
// discussionChatView.discussionTableView.contentInset.bottom = -1 * offset
let yOffset = discussionChatView.discussionTableView.contentOffset.y
discussionChatView.discussionTableView.setContentOffset(CGPoint(x: 0, y: yOffset - offset), animated: true)
}
回答1:
You almost got it.
Scrolling the table view won't work because it's already at the edge of the content, but you can "pad" the content using contentInset
.
What you'll want to do is to calculate how far into the table view the keyboard is, and set that value to contentInset.bottom
. This will add a "blank space" at the bottom of the table view.
I don't know if that will automatically scroll your view, but if that's not the case then you can use setContentOffset(_:animated:)
. on the table.
来源:https://stackoverflow.com/questions/65735513/smoothly-scrolling-tableview-up-by-fixed-constant-when-keyboard-appears-so-last