简体   繁体   English

更改 setContentOffset:animated:? 的速度

[英]Change the speed of setContentOffset:animated:?

Is there a way to change the speed of the animation when scrolling a UITableView using setContentOffset:animated:?有没有办法在使用 setContentOffset:animated: 滚动UITableView时更改动画的速度? I want to scroll it to the top, but slowly.我想把它滚动到顶部,但是很慢。 When I try the following, it causes the bottom few cells to disappear before the animation starts (specifically, the ones that won't be visible when the scroll is done):当我尝试以下操作时,它会导致底部的几个单元格在动画开始之前消失(特别是滚动完成后不可见的单元格):

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

Any other way around this problem?有没有其他办法解决这个问题? There is a private method _setContentOffsetAnimationDuration that works, but I don't want to be rejected from the app store.有一个私有方法_setContentOffsetAnimationDuration有效,但我不想被应用商店拒绝。

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

It works.有用。

Setting the content offset directly did not work for me.直接设置内容偏移对我不起作用。 However, wrapping setContentOffset(offset, animated: false) inside an animation block did the trick.但是,将setContentOffset(offset, animated: false) animation setContentOffset(offset, animated: false)包装在动画块中就可以了。

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

I've taken nacho4d's answer and implemented the code, so I thought it would be helpful for other people coming to this question to see working code:我已经接受了 nacho4d 的回答并实现了代码,所以我认为其他人来这个问题看看工作代码会有所帮助:

I added member variables to my class:我在类中添加了成员​​变量:

CGPoint startOffset;
CGPoint destinationOffset;
NSDate *startTime;

NSTimer *timer;

and properties:和属性:

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

and a timer callback:和一个计时器回调:

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

then:然后:

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

you'd also need timer cleanup in the dealloc method.您还需要在 dealloc 方法中进行计时器清理。 Since the timer will retain a reference to the target (self) and self has a reference to the timer, some cleanup code to cancel/destroy the timer in viewWillDisappear is likely to be a good idea too.由于计时器将保留对目标(self)的引用,而 self 具有对计时器的引用,因此在 viewWillDisappear 中取消/销毁计时器的一些清理代码也可能是一个好主意。

Any comments on the above or suggestions for improvement would be most welcome, but it is working very well with me, and solves other issues I was having with setContentOffset:animated:.任何对上述内容的评论或改进建议都是最受欢迎的,但它对我来说效果很好,并解决了我在 setContentOffset:animated: 中遇到的其他问题。

There is no a direct way of doing this, nor doing the way you wrote it.没有直接的方法可以做到这一点,也没有按照您编写的方式进行。 The only way I can accomplish this is by making the movement/animation by my own.我能做到这一点的唯一方法是自己制作运动/动画。

For example move 1px every 1/10 second should simulate a very slow scroll animation.例如每 1/10 秒移动 1px 应该模拟一个非常慢的滚动动画。 (Since its a linear animation maths are very easy!) (因为它是一个线性动画数学很容易!)

If you want to get more realistic or fancy and simulate easy-in easy-off effect then you need some maths to calculate a bezier path so you can know the exact position at every 1/10 second, for example例如,如果您想获得更逼真或更花哨的效果并模拟容易进入的轻松关闭效果,那么您需要一些数学运算来计算贝塞尔曲线,以便您可以每 1/10 秒知道准确的位置,例如

At least the first approach shouldn't be that difficult.至少第一种方法不应该那么困难。 Just use or -performSelector:withObject:afterDelay or NSTimers with只需使用或-performSelector:withObject:afterDelay or NSTimers

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

Hope it helps希望能帮助到你

I'm curious as to whether you found a solution to your problem.我很好奇您是否找到了解决问题的方法。 My first idea was to use an animateWithDuration:animations: call with a block setting the contentOffset:我的第一个想法是使用animateWithDuration:animations:调用块设置 contentOffset:

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

Side effects副作用

Although this works for simple examples, it also has very unwanted side effects.虽然这适用于简单的例子,但它也有非常不想要的副作用。 Contrary to the setContentOffset:animated: everything you do in delegate methods also gets animated, like the scrollViewDidScroll: delegate method.setContentOffset:animated:相反setContentOffset:animated:您在委托方法中所做的一切也会被动画化,就像scrollViewDidScroll:委托方法。

I'm scrolling through a tiled scrollview with reusable tiles.我正在滚动带有可重复使用的瓷砖的平铺滚动视图。 This gets checked in the scrollViewDidScroll: .这在scrollViewDidScroll:得到检查。 When they do get reused, they get a new position in the scroll view, but that gets animated, so there are tiles animating all the way through the view.当它们被重用时,它们会在滚动视图中获得一个新的位置,但它会被动画化,所以整个视图中都有瓷砖动画。 Looks cool, yet utterly useless.看起来很酷,但完全没用。 Another unwanted side effect is that possible hit testing of the tiles and my scroll view's bounds is instantly rendered useless because the contentOffset is already at a new position as soon as the animation block executes.另一个不需要的副作用是可能对瓷砖和我的滚动视图的边界进行的命中测试会立即变得无用,因为只要动画块执行, contentOffset 就已经处于新位置。 This makes stuff appear and disappear while they're still visible, as to where they used to be toggled just outside of the scroll view's bounds.这使得内容在它们仍然可见时出现和消失,至于它们曾经在滚动视图边界之外切换的位置。

With setContentOffset:animated: this is all not the case.使用setContentOffset:animated:情况并非如此。 Looks like UIScrollView is not using the same technique internally.看起来 UIScrollView 在内部没有使用相同的技术。

Is there anyone with another suggestion for changing the speed/duration of the UIScrollView setContentOffset:animated: execution?是否有人对更改 UIScrollView setContentOffset:animated: execution 的速度/持续时间有其他建议?

UIView calculates final view and then animates it. UIView 计算最终视图,然后对其进行动画处理。 That's why cells that invisible on finish of animation invisible on start too.这就是为什么在动画结束时不可见的单元格在开始时也不可见。 For prevent this needed add layoutIfNeeded in animation block:为了防止这种情况需要在动画块中添加 layoutIfNeeded:

[UIView animateWithDuration:2.0 animations:^{
    [self.tableView setContentOffset:CGPointMake(0, 0)];
    [self.tableView layoutIfNeeded]
}];

Swift version:迅捷版:

UIView.animate(withDuration: 2) {
    self.tableView.contentOffset.y = 10
    self.tableView.layoutIfNeeded()
}

https://github.com/dominikhofmann/PRTween https://github.com/dominikhofmann/PRTween

subclass UITableview子类 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];


}

You can set the duration as follows:您可以按如下方式设置持续时间:

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

This will also allow you to get all of your regular delegate callbacks.这也将允许您获得所有常规委托回调。

IF all your trying to do is scroll your scrollview I think you should use scroll rect to visible.如果您所做的只是滚动您的滚动视图,我认为您应该使用 scroll rect 来显示。 I just tried out this code我刚试过这个代码

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

and it scrolls the scrollview to the location i need for whatever duration i am setting it for.它将滚动视图滚动到我需要设置的任何持续时间的位置。 The key is setting animate to false.关键是将 animate 设置为 false。 When it was set to true, the animation speed was the default value set by the method设置为true时,动画速度为方法设置的默认值

For people who also have issues with disappearing items while scrolling a UITableView or a UICollectionView you can expand the view itself so that we hold more visible items.对于在滚动 UITableView 或 UICollectionView 时也遇到项目消失问题的人,您可以展开视图本身,以便我们保存更多可见项目。 This solution is not recommended for situations where you need to scroll a great distance or in situations where the user can cancel the animation.对于需要滚动很远距离或用户可以取消动画的情况,不建议使用此解决方案。 In the app I'm currently working on I only needed to let the view scroll a fixed 100px.在我目前正在开发的应用程序中,我只需要让视图滚动固定的 100 像素。

NSInteger scrollTo = 100; NSInteger 滚动到 = 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];
    }];
}];

I use transitionWithView:duration:options:animations:completion:我使用transitionWithView:duration:options:animations:completion:

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

UIViewAnimationOptionCurveLinear is an option make animation to occur evenly over. UIViewAnimationOptionCurveLinear是使动画均匀发生的选项。

While I found that in an animation duration, the delegate method scrollViewDidScroll did not called until animation finished.虽然我发现在动画持续时间内,委托方法scrollViewDidScroll直到动画完成scrollViewDidScroll调用。

You can simply use block based animation to animate the speed of scrollview.您可以简单地使用基于块的动画来为滚动视图的速度设置动画。 First calculate the offset point to which you want to scroll and then simply pass that offset value as here.....首先计算您想要滚动到的偏移点,然后简单地将偏移值传递给这里.....

    [UIView animateWithDuration:1.2
                      delay:0.02 
                      options:UIViewAnimationCurveLinear  
                      animations:^{
     [colorPaletteScrollView setContentOffset: offset ];
 }
 completion:^(BOOL finished)
 { NSLog(@"animate");
 } ];

here colorPaletteScrollView is my custom scrollview and offset is the value passed .这里 colorPaletteScrollView 是我的自定义滚动视图,偏移量是传递的值。

this code works perfectly fine for me.这段代码对我来说非常好。

Is there a reason you're using setContentOffset and not scrollRectToVisible:animated:?您是否有理由使用 setContentOffset 而不是 scrollRectToVisible:animated:?

- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated

I would recommend doing it like this:我建议这样做:

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:3.0];
[self.tableView scrollRectToVisible:CGRectMake(0, 0, 320, 0) animated:NO];
[UIView commitAnimations];

Unless that doesnt work.除非那不起作用。 I still think you should try it.我还是觉得你应该试试。

Actually TK189's answer is partially correct.实际上 TK189 的答案是部分正确的。

To achieve a custom duration animated contentOffset change, with proper cell reuse by UITableView and UICollectionView components, you just have to add a layoutIfNeeded call inside the animations block:要实现自定义持续时间动画 contentOffset 更改,通过 UITableView 和 UICollectionView 组件正确重用​​单元格,您只需在动画块内添加layoutIfNeeded调用:

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

On Xcode 7.1 - Swift 2.0 :在 Xcode 7.1 - Swift 2.0 上:

func textFieldShouldEndEditing(textField: UITextField) -> Bool {

    dispatch_async(dispatch_get_main_queue()) {
        UIView.animateWithDuration(0, animations: { self.scrollView!.setContentOffset(CGPointZero,animated: true) })
    }
    return true
}

OR或者

func textFieldShouldReturn(textField: UITextField) -> Bool {

    if(textField.returnKeyType == UIReturnKeyType.Next) {
        password.becomeFirstResponder()
    }

    dispatch_async(dispatch_get_main_queue()) {
        UIView.animateWithDuration(0, animations: { self.scrollView!.setContentOffset(CGPointZero,animated: true) })
    }

    textField.resignFirstResponder()
    return true
}

Note: self.scrollView!.setContentOffset(CGPointZero,animated: true) can have different positions depending on the requirement注意:self.scrollView!.setContentOffset(CGPointZero,animated: true) 根据需求可以有不同的位置

Example:例子:

let scrollPoint:CGPoint = CGPointMake(0,textField.frame.origin.y/2);
scrollView!.setContentOffset(scrollPoint, animated: true);

I wanted to change the contentOffSet of tableview when textfield begins to edit.当文本字段开始编辑时,我想更改 tableview 的 contentOffSet。

Swift 3.0斯威夫特 3.0

func textFieldDidBeginEditing(_ textField: UITextField) {

DispatchQueue.main.async { 
    UIView.animate(withDuration: 0, animations: {

     self.sampleTableView.contentOffset = CGPoint(x: 0, y: 0 - (self.sampleTableView.contentInset.top - 200 ))
    }) 
}    
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM