简体   繁体   中英

UIView Animation fails when there is a spinner added?

I am currently building a UI plugin. This plugin has 2 imageViews with one on top of another.

When the user swipes on the screen: If the swipe is greater than a threshold, the front imageView will leave the screen in the same direction as the swipe and then appear behind the back imageView.

So we plan to use this for a queue of images. After a successful swipe, the front imageView will leave the screen, then center behind the backImageView (which is now the front), and then it will change the image within.

When there are no more images, I put up a spinner to indicate that we are downloading more images from the server.

We are running into one bug that we couldn't explain ourselves - when we add the spinner into our view, our animation malfunctions .

The swiped imageView no longer leaves the screen but returns to its original position.

We are extremely puzzled by this and confirmed that

                 [self.view addSubview:spinner];

will result in an unexpected behavior for our animation.

Attached please fine the move method:

- (void)move:(UIPanGestureRecognizer *)sender {
    CGPoint translatedPoint = [sender translationInView:self.frontView];

    if([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateBegan) {
        _firstX = self.frontView.center.x;
        _firstY = self.frontView.center.y;
    }

    translatedPoint = CGPointMake(_firstX+translatedPoint.x, _firstY+translatedPoint.y);
    [self.frontView setCenter:translatedPoint];

    if ([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded) {
        // Calculate the if the x-movement is greater than a threshold
        float xTravelled = 0;
        if (self.frontView.center.x > _firstX)
            xTravelled = self.frontView.center.x - _firstX;
        else
            xTravelled = _firstX - self.frontView.center.x;

        // Only move if xTravelled is greater than threshold
        if (xTravelled > self.frontView.window.frame.size.width / 3) {
            NSLog(@"Swipe detected, currentIndex:%d", currentIndex);

            // Lock view from panning until animation is finished.
            self.view.userInteractionEnabled = NO;

            float newXPosition;
            if (self.frontView.center.x > _firstX) {
                newXPosition = (float) self.view.frame.size.width * 2;
            } else {
                newXPosition = (float) -2 * self.view.frame.size.width;
            }

            // We should trigger a fetch as soon as the back image is empty.
            if (![self getBackView].image) {
                double delayInSeconds = 2.0;
                dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
                dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
                    //[self fetchComplete];
                    [spinner stopAnimating];
                    [spinner removeFromSuperview];
                    self.view.userInteractionEnabled = YES;
                });

                // Pop up spinner if necessary
                if (![spinner isAnimating]) {
                    self.view.userInteractionEnabled = NO;
                    [spinner setCenter:CGPointMake(_firstX, _firstY)];
                    [spinner startAnimating];
                    /* THIS BREAKS OUR ANIMATION - Enable it to see 
                     [self.view addSubview:spinner];
                     */
                }
            }

            // Animate the rest of the swipe
            [UIView \
             animateWithDuration:0.5f delay:0.0f options:UIViewAnimationOptionTransitionNone
             animations:^{
                 // Use smooth animation to move the top image out of the way
                 [self.frontView setCenter:CGPointMake(newXPosition, self.frontView.center.y)];
             }
             completion:^(BOOL finished){
                 // Set it to be physically behind the back image
                 [self.view sendSubviewToBack:self.frontView];
                 //[self.frontView setCenter:CGPointMake(_firstX, _firstY)];  // why not necessary??

                 // Now change the picture
                 UIImage *next = [self nextImage];
                 self.frontView.image = next;

                 // update self.top to reference the new back image
                 self.frontView = [self getBackView];

                 // Do not override spinner's block 
                 if (![spinner isAnimating])
                     self.view.userInteractionEnabled = YES;
             }];
        } else {
            NSLog(@"Swipe NOT detected");
            [UIView \
             animateWithDuration:0.5f delay:0.0f options:UIViewAnimationOptionAllowUserInteraction
             animations:^{
                 [self.frontView setCenter:CGPointMake(_firstX, _firstY)];
             } completion:^(BOOL finished) {
                 self.view.userInteractionEnabled = YES;
             }];
        }
    }

I understand that this is rather lenghty, but I have isolated this bug into a sample project that you can download and run on your computer. This sample project is here: http://bit.ly/WO4cEI

We suspect that adding a subview to self.view is causing [self.view sendSubviewToBack:self.frontView] to fail. But we are not sure why this is causing our animation to fail either.

Thanks for the help!!

EDIT

@user2113425's recommendation fixed this problem!!

First of all, you don't have to add the spinner at that point anyway.

- (void)viewDidLoad
{
    [super viewDidLoad];
    UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(move:)];
    [panRecognizer setMinimumNumberOfTouches:1];
    [panRecognizer setMaximumNumberOfTouches:1];

    [panRecognizer setDelegate:self];
    [self.view addGestureRecognizer:panRecognizer];
    self.view.userInteractionEnabled = YES;

    // Index starts at zero so we pick even.
    self.frontView = self.evenImage;

    // Setting the images
    images = [[NSArray alloc] initWithObjects:@"a.jpeg", @"b.jpeg", nil]; // @"c.jpeg", @"d.jpeg", @"e.jpeg", nil];

    currentIndex = 0;
    self.evenImage.image = [self nextImage];
    self.oddImage.image = [self nextImage];

    // Spinner
    spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
    **spinner.hidesWhenStopped = YES;
    [self.view addSubview:spinner];**
}

you can add spinner in viewDidLoad and set hidesWhenStopped to hide it and in move function remove the [spinner removeFromSuperview]; this line.

Problem with addSubView is that it changes the zorder of the subviews, it might be involves with that but not sure..

there are many way to solve this problem..

The reason may be the spinner's animation. The same phenomenon is appeared when a viewController contains a tableView which is refreshing high-frequency.The layer's re-draw break the animation.Case the spinner is coming to be the view's subview,make this kind phenomenon happens.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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