web-dev-qa-db-ja.com

UIScrollView contentInsetをアニメートするとジャンプが途切れる

カスタムリフレッシュコントロール(サブクラスではなく、自分のクラス)を実装しました。iOS8に移行してから、スクロールビュー(具体的にはUICollectionView)のcontentInsetを設定してリフレッシュアニメーションを開始すると、奇妙なジャンプ/スタッターが発生します。ここに私のコードがあります:

- (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];
}

基本的に、ユーザーが更新のためにリリースしたら、contentInsetを更新コントロールの高さにアニメーション化します。アニメーションは、iOS 7のように、utter音/ジャンプを軽減しますが、iOS 8では、scrollViewのドラッグが解除されると、contentInsetにアニメートするだけでなく、スクロールビューのコンテンツが実際にリリースポイントから下にジャンプします素早く、そしてスムーズにアニメーション化します。これがiOS 8のバグなのかどうかはわかりません。私も追加してみました:

scrollView.contentOffset = CGPointZero;

アニメーションブロックでは、何も変更されませんでした。

誰にもアイデアはありますか?どんな助けも大歓迎です。ありがとう!

30
ryanthon

アニメーションブロックのメソッドを次のように変更しました。

- (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;
    }];
}
34
ryanthon
- (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];
            }];
        }
    }
}

これを試すことができます。キーは2つのアニメーションを使用することです。

5
user3044484

ジャンプを削除するには、ryanthonの答えが役立ちます。 iOS9.0を使用する私のアプリでは、ジャンプを削除するためにアニメーションブロックさえ必要ありません。contentInsetを設定した後にcontentOffsetをリセットするだけで、トリックが実行されます。

更新ビューを非表示にするときにtableViewのスクロール速度を制御する場合、user3044484の答えのダブルアニメーションブロックトリックは魔法を行います。1つのアニメーションブロックでは不十分です。

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;  
        }];
    }];
}
3
Zack Zhu

私は同じ問題を抱えていて、アニメーションブロックを...に移動しました.

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView

代わりにデリゲートメソッド。完璧ではありませんが、まだ問題を特定することはできません。 scrollViewの-layoutSubviewsメソッドはiOS8でより頻繁に呼び出され、アニメーションがびびります。

2
Siôn

2つのソリューション:

  1. ロード開始時にバウンスを無効にし、ロード終了時にバウンスを有効にします
  2. ロードを開始するときにコンテンツのインセットを設定した後、コンテンツオフセットを一定の値で設定してください
2
Linkou Bian

私は以下によって解決しました:-

  • ContentInsetがcontentOffsetYに更新されている場合、バウンスを無効にします。
  • ContentOffsetを追跡し、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 DemoSwift-3

1
Shrawan

残念ながら- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollViewでアニメーション化してもうまくいきません。とにかくスクロールビューはバウンスします。

コンテンツのインセットを含むUIScrollViewの境界は自動的に変更されます これは、iOS8のバグのようです...

0
niggeulimann