问题
I implemeted a custom refresh control (my own class, not a subclass), and for some reason since moving to iOS 8, setting the contentInset of the scroll view (specifically, UICollectionView) to start the refresh animation causes a weird jump/stutter. Here is my code:
- (void)containingScrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat scrollPosition = scrollView.contentOffset.y + scrollView.contentInset.top;
if( scrollPosition > 0 || self.isRefreshing )
{
return;
}
CGFloat percentWidth = fabs( scrollPosition ) / self.frame.size.height / 2;
CGRect maskFrame = self.maskLayer.frame;
maskFrame.size.width = self.imageLayer.frame.size.width * percentWidth;
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
self.maskLayer.frame = maskFrame;
[CATransaction commit];
}
- (void)containingScrollViewDidEndDragging:(UIScrollView *)scrollView
{
if( ( self.maskLayer.frame.size.width >= self.imageLayer.frame.size.width ) && !self.isRefreshing )
{
self.isRefreshing = YES;
[self setLoadingScrollViewInsets:scrollView];
[self startAnimation];
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
- (void)setLoadingScrollViewInsets:(UIScrollView *)scrollView
{
UIEdgeInsets loadingInset = scrollView.contentInset;
loadingInset.top += self.frame.size.height;
UIViewAnimationOptions options = UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState;
[UIView animateWithDuration:0.2 delay:0 options:options animations:^
{
scrollView.contentInset = loadingInset;
}
completion:nil];
}
Basically once the user releases to refresh, I animate the contentInset to the height of the refresh control. I figure the animation would reduce stuttering/jumpiness, which it did in iOS 7. But in iOS 8, when the scrollView is released from dragging, instead of just animating to the contentInset, the scroll view content jumps down from the point of release really quickly, and then animates up smoothly. I'm not sure if this is a bug in iOS 8 or what. I've also tried adding:
scrollView.contentOffset = CGPointZero;
in the animation block, which didn't change anything.
Does anyone have any ideas? Any help would be highly appreciated. Thanks!
回答1:
I changed the method with my animation block to:
- (void)setLoadingScrollViewInsets:(UIScrollView *)scrollView
{
UIEdgeInsets loadingInset = scrollView.contentInset;
loadingInset.top += self.view.frame.size.height;
CGPoint contentOffset = scrollView.contentOffset;
[UIView animateWithDuration:0.2 animations:^
{
scrollView.contentInset = loadingInset;
scrollView.contentOffset = contentOffset;
}];
}
回答2:
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
CGPoint point = scrollView.contentOffset;
static CGFloat refreshViewHeight = 200.0f;
if (scrollView.contentInset.top == refreshViewHeight) {
//openning
if (point.y>-refreshViewHeight) {
//will close
//必须套两层animation才能避免闪动!
[UIView animateWithDuration:0 animations:NULL completion:^(BOOL finished) {
[UIView animateWithDuration:0.25 animations:^{
scrollView.contentOffset = CGPointMake(0.0f, 0.0f);
scrollView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
} completion:NULL];
}];
}
}
else{
//closing
static CGFloat openCriticalY = 40.0f;//会执行打开的临界值
if(point.y<-openCriticalY){
//will open
[UIView animateWithDuration:0 animations:NULL completion:^(BOOL finished) {
[UIView animateWithDuration:0.25 animations:^{
scrollView.contentInset = UIEdgeInsetsMake(refreshViewHeight, 0.0f, 0.0f, 0.0f);
scrollView.contentOffset = CGPointMake(0.0f, -refreshViewHeight);
} completion:NULL];
}];
}
}
}
you can try this,the key is using two animation.
回答3:
To remove the jump, ryanthon's answer will do the trick. In my app with iOS9.0, no animation block is even needed to remove the jump, just resetting contentOffset after setting contentInset will do the trick.
If you want to control the tableView's scroll speed when hiding the refresh view, double animation blocks trick in user3044484's answer will do the magic, one animation block is not enough.
if (self.tableView.contentInset.top == 0)
{
UIEdgeInsets tableViewInset = self.tableView.contentInset;
tableViewInset.top = -1.*kTableHeaderHeight;
[UIView animateWithDuration: 0 animations: ^(void){} completion:^(BOOL finished) {
[UIView animateWithDuration: 0.5 animations:^{
//no need to set contentOffset, setting contentInset will change contentOffset accordingly.
self.tableView.contentInset = tableViewInset;
}];
}];
}
回答4:
I had the same problem, and moved my animation block to the...
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
delegate method instead. Not perfect, but I can't pinpoint the problem yet. Looks like scrollView's -layoutSubviews
method is being called more often under iOS8, causing jumpy animations.
回答5:
Two solutions:
- disable bounce when start loading and enable bounce when finished loading
- after setting content inset when start loading, please set content offset with a constant value
回答6:
I have resolved by:-
- Disable the Bounce when ContentInset is being updated to contentOffsetY .
- Keep track of contentOffset and Update to tableView ContentInset.
var contentOffsetY:CGFloat = 100
func tracScrollContent(location: UIScrollView) {
if(location.contentOffset.y == -contentOffsetY ) {
tblView.isScrollEnabled = true
tblView.bounces = false
tblView.contentInset = UIEdgeInsets(top: contentOffsetY, left: 0, bottom: 0, right: 0)
}else if(location.contentOffset.y >= 0 ){
tblView.bounces = true
tblView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
Github Demo Swift-3
回答7:
animating in - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
unfortunately does't work for me. The scrollview bounces anyway.
According to Bounds automatically changes on UIScrollView with content insets this seems like an iOS8-bug to me...
来源:https://stackoverflow.com/questions/26050872/animating-uiscrollview-contentinset-causes-jump-stutter