web-dev-qa-db-ja.com

setContentOffset:animated :?の速度を変更します

SetContentOffset:animated :?を使用してUITableViewをスクロールするときにアニメーションの速度を変更する方法はありますか?一番上までスクロールしたいが、ゆっくりしたい。次のことを試してみると、アニメーションが開始する前に下のいくつかのセルが消えてしまいます(具体的には、スクロールが完了したときに表示されないセル)。

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:3.0];
[self.tableView setContentOffset:CGPointMake(0, 0)];
[UIView commitAnimations];

この問題を回避する他の方法はありますか?プライベートメソッド_setContentOffsetAnimationDurationが機能しますが、アプリストアから拒否されたくありません。

38
Jordan Kay

これを直接行う方法も、あなたが書いた方法もありません。これを達成できる唯一の方法は、自分で動き/アニメーションを作成することです。

たとえば、1/10秒ごとに1pxを移動すると、非常に遅いスクロールアニメーションがシミュレートされます。 (線形アニメーションなので、数学はとても簡単です!)

より現実的または派手になり、イージーインイージーオフ効果をシミュレートする場合は、1/10秒ごとの正確な位置を知ることができるように、ベジェパスを計算するための数学が必要です。たとえば、

少なくとも最初のアプローチはそれほど難しくないはずです。または-performSelector:withObject:afterDelay or NSTimerswith

-[UIScrollView setContentOffset:(CGPoint*)];`

それが役に立てば幸い

11
nacho4d
[UIView animateWithDuration:2.0 animations:^{
    scrollView.contentOffset = CGPointMake(x, y);
}];

できます。

90
TK189

私はnacho4dの答えを受け取ってコードを実装したので、この質問に来た他の人が実際に動作するコードを見るのに役立つと思いました。

クラスにメンバー変数を追加しました:

CGPoint startOffset;
CGPoint destinationOffset;
NSDate *startTime;

NSTimer *timer;

およびプロパティ:

@property (nonatomic, retain) NSDate *startTime;
@property (nonatomic, retain) NSTimer *timer;

タイマーコールバック:

- (void) animateScroll:(NSTimer *)timerParam
{
    const NSTimeInterval duration = 0.2;

    NSTimeInterval timeRunning = -[startTime timeIntervalSinceNow];

    if (timeRunning >= duration)
    {
        [self setContentOffset:destinationOffset animated:NO];
        [timer invalidate];
        timer = nil;
        return;
    }
    CGPoint offset = [self contentOffset];

    offset.x = startOffset.x +
        (destinationOffset.x - startOffset.x) * timeRunning / duration;

    [self setContentOffset:offset animated:NO];
}

その後:

- (void) doAnimatedScrollTo:(CGPoint)offset
{
    self.startTime = [NSDate date];
    startOffset = self.contentOffset;
    destinationOffset = offset;

    if (!timer)
    {
        self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01
                                                      target:self
                                                    selector:@selector(animateScroll:)
                                                    userInfo:nil
                                                     repeats:YES];
    }
}

また、deallocメソッドでタイマーをクリーンアップする必要があります。タイマーはターゲット(自己)への参照を保持し、自己はタイマーへの参照を保持するため、viewWillDisappearでタイマーをキャンセル/破棄するクリーンアップコードもお勧めです。

上記についてのコメントや改善の提案を歓迎しますが、それは非常にうまく機能しており、setContentOffset:animated:で抱えていた他の問題を解決します。

14
JosephH

コンテンツオフセットを直接設定しても機能しませんでした。ただし、setContentOffset(offset, animated: false)をアニメーションブロック内にラップするとうまくいきました。

UIView.animate(withDuration: 0.5, animations: {
                self.tableView.setContentOffset(
               CGPoint(x: 0, y: yOffset), animated: false)
            })
8
astro4

私はあなたがあなたの問題の解決策を見つけたかどうかについて興味があります。私の最初のアイデアは、contentOffsetを設定するブロックでanimateWithDuration:animations:呼び出しを使用することでした:

[UIView animateWithDuration:2.0 animations:^{
    scrollView.contentOffset = CGPointMake(x, y);
}];

副作用

これは簡単な例では機能しますが、非常に望ましくない副作用もあります。 setContentOffset:animated:に反して、デリゲートメソッドで行うすべての操作も、scrollViewDidScroll:デリゲートメソッドのようにアニメーション化されます。

再利用可能なタイルを含むタイルスクロールビューをスクロールしています。これはscrollViewDidScroll:でチェックされます。それらが再利用されると、スクロールビューで新しい位置を取得しますが、それはアニメーション化されるので、ビュー全体にアニメーション化するタイルがあります。格好いいが、まったく役に立たない。もう1つの望ましくない副作用は、タイルとスクロールビューの境界のヒットテストの可能性が、アニメーションブロックが実行されるとすぐにcontentOffsetが既に新しい位置にあるため、即座に役に立たないことです。これにより、スクロールビューの境界のすぐ外側でどこに切り替えられていたのかを、表示されている間に表示および非表示にします。

setContentOffset:animated:の場合、これはすべて当てはまりません。 UIScrollViewは内部的に同じ手法を使用していないようです。

UIScrollView setContentOffset:animated:実行の速度/期間を変更するための別の提案がある人はいますか?

5
epologee

https://github.com/dominikhofmann/PRTween

サブクラスUITableview

#import "PRTween.h"


@interface JPTableView : UITableView{
  PRTweenOperation *activeTweenOperation;
}



- (void) doAnimatedScrollTo:(CGPoint)destinationOffset
{
    CGPoint offset = [self contentOffset];

    activeTweenOperation = [PRTweenCGPointLerp lerp:self property:@"contentOffset" from:offset to:destinationOffset duration:1.5];


}
2
johndpope

次のように期間を設定できます。

scrollView.setValue(5.0、forKeyPath: "contentOffsetAnimationDuration")scrollView.setContentOffset(CGPoint(x:100、y:0)、animated:true)

これにより、通常のデリゲートコールバックをすべて取得することもできます。

1
eGanges

あなたがやろうとしているのがスクロールビューをスクロールするだけなら、私はあなたが見えるようにスクロール長方形を使うべきだと思います。私はこのコードを試しました

 [UIView animateWithDuration:.7
                      delay:0
                    options:UIViewAnimationOptionCurveEaseOut
                 animations:^{  
                     CGRect scrollToFrame = CGRectMake(0, slide.frame.Origin.y, slide.frame.size.width, slide.frame.size.height + kPaddingFromTop*2);
                     CGRect visibleFrame = CGRectMake(0, scrollView.contentOffset.y,
                                                      scrollView.frame.size.width, scrollView.frame.size.height);
                     if(!CGRectContainsRect(visibleFrame, slide.frame))
                         [self.scrollView scrollRectToVisible:scrollToFrame animated:FALSE];}];

そして、それは私がそれを設定している期間にかかわらず、私が必要とする場所にスクロールビューをスクロールします。キーは、animateをfalseに設定することです。 trueに設定された場合、アニメーション速度はメソッドによって設定されたデフォルト値でした

1
Esko918

UITableViewまたはUICollectionViewのスクロール中にアイテムが消える問題もある人は、ビュー自体を展開して、より多くの表示アイテムを保持できます。このソリューションは、遠くまでスクロールする必要がある状況や、ユーザーがアニメーションをキャンセルできる状況にはお勧めできません。現在作業中のアプリでは、ビューを100ピクセルに固定してスクロールする必要がありました。

NSInteger scrollTo = 100;

CGRect frame = self.collectionView.frame;
frame.size.height += scrollTo;
[self.collectionView setFrame:frame];

[UIView animateWithDuration:0.8 delay:0.0 options:(UIViewAnimationOptionCurveEaseIn) animations:^{
    [self.collectionView setContentOffset:CGPointMake(0, scrollTo)];
} completion:^(BOOL finished) {
    [UIView animateWithDuration:0.8 delay:0.0 options:(UIViewAnimationOptionCurveEaseIn) animations:^{
        [self.collectionView setContentOffset:CGPointMake(0, 0)];

    } completion:^(BOOL finished) {
        CGRect frame = self.collectionView.frame;
        frame.size.height -= scrollTo;
        [self.collectionView setFrame:frame];
    }];
}];
0
Ben Groot

私が使う transitionWithView:duration:options:animations:completion:

        [UIView transitionWithView:scrollView duration:3 options:(UIViewAnimationOptionCurveLinear) animations:^{
        transitionWithView:scrollView.contentOffset = CGPointMake(contentOffsetWidth, 0);
    } completion:nil];

UIViewAnimationOptionCurveLinearは、アニメーションを均等に発生させるオプションです。

アニメーション期間中に、デリゲートメソッドscrollViewDidScrollがアニメーションが終了するまで呼び出されないことがわかりました。

0
FFur