문제

More specifically I have a UIScrollView with an embedded UIImageView.

I have an object which describes an "animation" frame

@class AnimationFrame

@property (nonatomic,strong) UIImage *image;
@property (nonatomic) CGRect zoomToRectInImage;
@property (nonatomic) CGFloat duration;

@end

I would like to chain the frames to each other such that the imageview will swap the current image to the animationframe.image and then zoom to that position.

I thought about doing something like this (pseudo code):

-(void)animateImageView
{
      // imagine these were created before hand
      NSArray *myAnimationFrames = @[frame1,frame2,frame3,frame4,frame5]; 

      // now iterate through the frames and start the animation
      for (AnimationFrame *frame in myAnimationFrames)
      {
          self.scrollview.zoomScale = 1.0;
          self.imageView.image = frame.image;
          [self.scrollview zoomToRect:frame.zoomToRectInImage animated:YES];
      }
}

But I'm having trouble synchronizing between the image changes and zooming.

How can I make sure they are not overlapping?

도움이 되었습니까?

해결책

If your array of frames are dynamic, or the array becomes large, the task continuation becomes a bit unwieldy to implement.

With a help of a third party library (well, just one class) it can look like this:

First, define an animation task as follows:

- (RXPromise*) animationTaskWithFrame(AnimationFrame* frame) 
{
    RXPromise* promise = [RXPromise new];
    [UIView animateWithDuration:0.5 animations:^{
        ...  // animation block 
    }
    completion:^(BOOL finished) {
         [promise fulfillWithValue:@"OK"];
    }];

    return promise;
}

Then, assuming you implemented a category for NSArray:

typedef RXPromise* ^(unary_async_t)(id param);

@interface NSArray (RXExtension)
- (RXPromise*) forEachApplyTask:(unary_async_t)task;
@end

You can run your frames as follows:

-(void)animateImageView
{
    NSArray* myAnimationFrames = @[frame1,frame2,frame3,frame4,frame5]; 

    // start the animations asynchronously:
    self.myAnimationFramesPromise = [myAnimationFrames forEachApplyTask:^RXPromise*(id frame){
        return [self animationTaskWithFrame:frame];
    }];

    // When finished, then:
    self.myAnimationFramesPromise.then(^id(id result){
        // All animations finished.
        NSLog(@"Result: %@", result);  // prints @"Finished"
        return nil;
    }, nil /* error handler*/);

}

If you want to cancel the animations before all finished:

// elsewhere:

- (void) viewWillDisappear:(BOOL)animated {

     [self.myAnimationFramesPromise cancel];
}

The implementation of the category is shown below. Note, this is a generic implementation, it simply serially invokes the asynchronous function specified by a block task for each element in the array in order. So, this is a tool which can be reused elsewhere, too - not just for performing animations.

static RXPromise* do_apply_each(NSEnumerator* iter, RXPromise* promiseResult, unary_async_t task)
{
    if (promiseResult.isCancelled) {
        return promiseResult;
    }
    id obj = [iter nextObject];
    if (obj == nil) {
        [promiseResult fulfillWithValue:@"Finished"];
        return promiseResult;
    }
    promiseResult = task(obj).then(^id(id result){
        return do_apply_each(iter, promiseResult, task);
    }, ^id(NSError*error){
        return error;
    });
    return promiseResult;
}


@implementation NSArray (RXExtension)

- (RXPromise*) forEachApplyTask:(unary_async_t)task {
    RXPromise* promise = [RXPromise new];
    NSEnumerator* iter = [self objectEnumerator];
    return do_apply_each(iter, promise, task);
}

@end

RXPromise is available at GitHub: RXPromise. Disclosure: I'm the author.

다른 팁

Try this code,

[UIView animateWithDuration:0.5
                 animations:^
{ 
//do the changes of the view after the animation

}
completion:^(BOOL finished)
 {
    //anything to be done after animation
 }
 ];

UIView has following methods, you can make use of these methods to achieve your requirements.

[UIView animateWithDuration: animations:];
[UIView animateWithDuration: animations: completion:];
[UIView animateWithDuration: delay: options: animations: completion:]
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top