Search code examples
iosanimationuiviewuiwebviewcgrect

UIView Animation resets views to original rect


I'm trying to animate a UIWebView on to the screen from the right border when a link is tapped. In order to do this, I'm using two UIViews. One called onScreenWebView and another called offScreenWebView. The idea is that I can animate the offscreenWebView onto the screen from the right, and animate the onScreenWebView off the screen to the left. Then swap the views (so the view on screen becomes the onScreenWebView and vice versa) but my animation is having problems. I must note it works great THE FIRST TIME. After that it doesn't work well at all.

The views are aligned as such

      __________ __________
      |        | |        |
      |   on   | |  off   |
      | screen | | screen |
      |________| |________|

Here's the animation code :

offScreenWebView.hidden = true;

offScreenWebView.frame = self.frame;
[offScreenWebView offSetX:self.bounds.size.width + kOffsetPadding];         // move offscreen to the right
[self logWebRects:@"begin"];
offScreenWebView.hidden = false;
[self bringSubviewToFront:offScreenWebView];

[UIView animateWithDuration:0.5f delay:0.0f options:UIViewAnimationCurveEaseInOut animations:^{
    [self logWebRects:@"during 1"];
    offScreenWebView.frame = self.frame;
    onScreenWebView.frame = CGRectMake(-(self.bounds.size.width + kOffsetPadding), 0, onScreenWebView.bounds.size.width, onScreenWebView.bounds.size.height);
    [self logWebRects:@"during 2"];
}completion:^(BOOL finished) {
    [self logWebRects:@"finished b4 swap"];
    [self swapWebViews];
    [self logWebRects:@"finished -- done"];
}];

Here is the output of my logWebRects method (just the origin of each view)

Navigation Type ::      Link Clicked
Rect of self frame   ::  {{0, 0}, {320, 460}} 

99  --  Point of offscreen origin   begin          ::  {335, 0}
0   --  Point of onscreen origin    begin          ::  {0, 0}
99  --  Point of offscreen origin   during 1       ::  {335, 0}
0   --  Point of onscreen origin    during 1       ::  {0, 0}
99  --  Point of offscreen origin   during 2       ::  {0, 0}
0   --  Point of onscreen origin    during 2       ::  {-335, 0}
Navigation Type ::      Other
99  --  Point of offscreen origin   finished b4 swap       ::  {0, 0}
0   --  Point of onscreen origin    finished b4 swap       ::  {-335, 0}
0   --  Point of offscreen origin   finished -- done       ::  {-335, 0}
99  --  Point of onscreen origin    finished -- done       ::  {0, 0}


Navigation Type ::      Link Clicked
Rect of self frame   ::  {{0, 0}, {320, 460}} 

0   --  Point of offscreen origin   begin          ::  {335, 0}
99  --  Point of onscreen origin    begin          ::  {0, 0}
0   --  Point of offscreen origin   during 1       ::  {335, 0}
99  --  Point of onscreen origin    during 1       ::  {0, 0}
0   --  Point of offscreen origin   during 2       ::  {0, 0}
99  --  Point of onscreen origin    during 2       ::  {-335, 0}
Navigation Type ::      Other
0   --  Point of offscreen origin   finished b4 swap       ::  {335, 0}
99  --  Point of onscreen origin    finished b4 swap       ::  {0, 0}
99  --  Point of offscreen origin   finished -- done       ::  {0, 0}
0   --  Point of onscreen origin    finished -- done       ::  {335, 0}

These logs are from the initial run. Then also the second run. You'll notice that for whatever reason, the animation block is resetting the frame of each web view just before the completion block.

I should note that I swap the web views with a classic temp-variable swap. Also, they are sibling views.


Solution

  • I have a similar set up to yours in one of my apps. Unfortunately I can't see anything significantly wrong with your implementation (though we can't see your swapping and logging methods). Mine doesn't call out to other methods or do as much work with frames. I did have a problem with a temp variable swap so reverted to using the next view controller as a parameter to the method, it worked much better.

    centreOffRight and centreOffLeft are CGPoints defining the centre of the screen offset to the left and right respectively. next and previous are the view controllers for each view. This method is called from a container view controller.

    -(void)moveNext:(PlayerViewController*)sender
    {
        // Need to push this one from the right
        next.view.center = centreOffRight; 
        [self.view bringSubviewToFront:next.view];
        [UIView animateWithDuration:0.3 
                         animations:^{
                             next.view.center = centreOfScreen;
                             current.view.center = centreOffLeft;} 
                         completion:^(BOOL finished){ 
                             [current wasMovedOffScreen];
                             current = next;
                             next = sender;
                             currentDouble = nextIndex;}];
    
    }
    

    I'm reluctant to post this as an answer as I can't see why this would work when yours doesn't, but hopefully it helps you.