Smoothly scrolling tableview up by fixed constant when keyboard appears so last cells are still visible

不羁岁月 提交于 2021-01-28 21:11:57

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!