0

I am trying to auto scroll my text view and reset it to the top once it's arrived at the end.

I use this code:

-(void)scrollTextView
{

    CGPoint scrollPoint = stationInfo.contentOffset; 

    scrollPoint = CGPointMake(scrollPoint.x, scrollPoint.y + 2);

    if (scrollPoint.y == originalPoint.y + 100)
    {
        NSLog(@"Reset it");

        scrollPoint = CGPointMake(originalPoint.x, originalPoint.y);
        [stationInfo setContentOffset:scrollPoint animated:YES];

        [scroller invalidate];
        scroller = nil;

        scroller = [NSTimer
                    scheduledTimerWithTimeInterval:0.1
                    target:self
                    selector:@selector(scrollTextView)
                    userInfo:nil
                    repeats:YES];

    }
    else
    {
        [stationInfo setContentOffset:scrollPoint animated:YES];
    }

}

As a result, the text view jumps around wildly, but I don't quite know why. Is there maybe a better way of detecting that the text view is at the bottom? Am I setting the the scrollPoint value wrong?

Edit:

ISSUE SOLVED! I stuck to NSTimer - the missing key was calling -display for the layer.

    -(void)scrollTextView
    {
        //incrementing the original point to get movement
        originalPoint = CGPointMake(0, originalPoint.y + 2);
        //getting the bottom
        CGPoint bottom = CGPointMake(0, [stationInfo contentSize].height);
        //comparing the two to detect a reset
        if (CGPointEqualToPoint(originalPoint,bottom) == YES) 
        {
            NSLog(@"Reset");
            //killing the timer
            [scroller invalidate];
            scroller == nil;
            //setting the reset point
            CGPoint resetPoint = CGPointMake(0, 0);
            //reset original point
            originalPoint = CGPointMake(0, 0);
            //reset the view.
            [stationInfo setContentOffset:resetPoint animated:YES];
            //force display
            [stationInfo.layer display];

            scroller = [NSTimer
                        scheduledTimerWithTimeInterval:0.1
                        target:self
                        selector:@selector(scrollTextView)
                        userInfo:nil
                        repeats:YES];
        }
        else
        {   
            [stationInfo setContentOffset:originalPoint animated:YES];
        }


}
Pripyat
  • 2,937
  • 2
  • 35
  • 69
  • The callback method has the wrong signature. Look in my answer to see the right one. – GorillaPatch Nov 20 '10 at 19:08
  • This code works and that is fine. Just be aware that this is very low-level code. CoreAnimation would really made it easier. I admit it takes some time to read the docs, but it is worth it. One question remains: why do you interact with the CALayer directly? As you are not dealing with layers directly (or do you) I would call [stationInfo setNeedsDisplay]. – GorillaPatch Nov 21 '10 at 08:35
  • Why do you set the content offset animated? You are animating with your timer, so I would not use an animation for each 2 pixel step. The real problem could then be that the implicit animation you are triggering there is longer than your 0.1 time step and this would mess things up badly. Try to set it without animations when scrolling downwards. Maybe also your display call on a CALayer can be omitted also, which still seems odd to me. – GorillaPatch Nov 21 '10 at 08:39
  • i've changed it to `[stationInfo setNeedsDisplay];` but I'm keeping the rest - it works exactly as I want it to. The memory footprint is low also, so it's no problem for me. – Pripyat Nov 21 '10 at 10:14
  • 1
    As I said: as long as it is working keep it. – GorillaPatch Nov 21 '10 at 10:25

1 Answers1

1

You could also use CoreAnimation and animate the bounds property directly. First animate the scrolling, then in the delegate callback that the animation has finished you reset the content offset.

The callback method has to have the signature

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context

You could also use the new block-based methods if you are targeting iOS 4.0 and above. Then there are two blocks to be passed: in the first one you specify what to animate and in the second what to do when the animation is over.

+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion

Your problem than collapses into a single line of code:

[stationInfo animateWithDuration:10.f delay:0.f options:0 animations:^{
    [stationInfo setContentOffset:CGPointMake(0, [stationInfo contentSize].height)];
} completion:^(BOOL finished){
    if (finished) [stationInfo setContentOffset:CGPointMake(0,0)];
}];

To be honest I am not 100% sure about the precise block syntax but this is how it should work.

GorillaPatch
  • 5,007
  • 1
  • 39
  • 56
  • Resetting causes mayhem, which is why I asked how I can reset it correctly.. :/ – Pripyat Nov 19 '10 at 20:49
  • The solution would be to leave away your timer and use CoreAnimation. you then get a callback once it is finished and in this you set the content offset to (0,0) – GorillaPatch Nov 19 '10 at 20:51
  • I'm happy to keep the timer - and it detects the end ok, but if I reset the offset to 0,0 it jumps around again. – Pripyat Nov 19 '10 at 21:07
  • sorry, this code is badly formatted, i can't make out what is what. Meanwhile, I've been successfully working on my own solution, but the old problem pops back up that even if i set the offset to 0,0, it won't reset. I know when it arrives at the bottom, so i stop the timer and set the offset, but it just wont reset. – Pripyat Nov 20 '10 at 21:43
  • Well the code SEEMS badly formatted, but it is like this: the animations parameter of the first function call "animateWithDuration" takes a block as an argument. In the first line this block is opened with ^{ and then the contentOffset is set inside the block. Then the block is closed, and the last argument completion: takes another block which will be executed when the animation is over. The block takes one argument, a BOOL if its finished and then the contentOffset is reset if the animation is finished. – GorillaPatch Nov 21 '10 at 08:29