7

I have problems with transitionWithView and animateWithDuration. One of my animateWithDuration blocks doesn't transition, it is a sudden change, and transitionWithView does not temporarily disable user interaction. I have checked the docs and believe I am doing everything correctly, but obviously something is wrong. Here are the two blocks of code:

This is in my main View Controller ViewController which has three container views/child view controllers. This block moves one of the container views, but does not block the user from other interactions in ViewController while the transition is occurring.

[UIView transitionWithView:self.view duration:0.5 options:UIViewAnimationOptionCurveEaseOut animations:^ {
        CGRect frame = _containerView.frame;
        frame.origin.y = self.view.frame.size.height - _containerView.frame.size.height;
        _containerView.frame = frame;
}completion:^(BOOL finished) {
     // do something   
}];

This is in one of my container view controllers. The animation seems to have no effect as the text of the productTitleLabel and productDescriptionTextView changes suddenly as if the animation block does not exist.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{    
    [self.viewController toggleFlavoredOliveOilsTableView];

    if (indexPath.row > 0) {
        NSDictionary *selectedCellDict = [[_flavoredOliveOilsDict objectForKey:@"Unflavored Olive Oils"] objectAtIndex:indexPath.row - 1];

        [UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionTransitionCrossDissolve animations:^ {
            self.viewController.productTitleLabel.text = [_flavoredOliveOilsTableView cellForRowAtIndexPath:indexPath].textLabel.text;
            self.viewController.productDescriptionTextView.text = [selectedCellDict objectForKey:@"Description"];
        }completion:nil];

        if (indexPath.row == 1) {
            [self.viewController setProductDescriptionTextViewFrameForInformationTab];
        }
        else {
            [self.viewController setProductDescriptionTextViewFrameForNonInformationTab];

            //self.viewController.productImageView.image = [UIImage imageNamed:[selectedCellDict objectForKey:@"Image"]];
        }
    }
}

I think the problems are somewhat related as most of my animation and transition blocks don't work completely as expected. Thanks for any help.

Edit

What I am trying to accomplish is moving a container view in ViewController and set the text and image properties of a label, text view, and image view; all of which are in the main view. The details of these properties are sent via the child view controller. The transitionWithView is in a method called toggleFlavoredOiveOilsTableView which is called in didSelectRowAtIndexPath. I think the problem is that I am trying to call two different animation/transition blocks at the same time.

Patrick Veilleux
  • 471
  • 1
  • 4
  • 17

1 Answers1

13

You can experience this behavior of two animations interfering with each other if one animation is done on subview of another view undergoing animation. Thus, if you perform transitionWithView:self.view (i.e. on the main view) like your code snippet suggests, you can have problems. If you perform the two animations on distinct subviews, the problem may go away. In my original answer below, I:

  • Perform transitionWithView on a subview that has the two UILabel controls;

  • Put my view controller containment child views within a subview of the main view, and then the transitionFromViewController is constrained to that subview.

When I put the two animated portions on distinct subviews, the animations can take place simultaneously without incident.


If you want to animate the changing of the contents of two text labels, you can:

[UIView transitionWithView:self.viewController.textLabelsContainerView
                  duration:0.5
                   options:UIViewAnimationOptionTransitionCrossDissolve
                animations:^{
                    self.viewController.productTitleLabel.text = [_flavoredOliveOilsTableView cellForRowAtIndexPath:indexPath].textLabel.text;
                    self.viewController.productDescriptionTextView.text = [selectedCellDict objectForKey:@"Description"];
                }
                completion:nil];

Generally I'd use animateWithDuration, but the text attribute is not an animatable property, so that's why I use transitionWithView, making sure that I have a container for those text fields. But I've tried animating other controls using animateWithDuration, while simultaneously animating the changing of the child controllers, and it works fine.

For transitioning the child view controllers, I use transitionFromViewController to animate the swapping of the containers child controller's views (it's part of the Managing Child View Controllers in a Custom Container family of methods). In order to facilitate that process, I put a container view on my main view controller's view, and add the child controller's views as a subview of that container. That way, when I animate the transition of the child, the animations are nicely constrained to that container view.

So, here is some sample code to add a view controller to my container view, and then the code I use to transition between two child view controllers using a segmented button:

- (void)addFirstChild
{
    UIViewController *child = [self.storyboard instantiateViewControllerWithIdentifier:@"Stooge"];
    [self addChildViewController:child];

    child.view.frame = self.bottomContainerView.bounds;
    [self.bottomContainerView addSubview:child.view];

    [child didMoveToParentViewController:self];
}

- (void)changedChild
{
    UIViewController *oldController = [self.childViewControllers lastObject];
    UIViewController *newController;

    if (self.segmentedControl.selectedSegmentIndex == 0)
        newController = [self.storyboard instantiateViewControllerWithIdentifier:@"Stooge"];
    else
        newController = [self.storyboard instantiateViewControllerWithIdentifier:@"Marx"];

    [oldController willMoveToParentViewController:nil];
    [self addChildViewController:newController];

    // start off screen below

    CGRect startFrame = self.bottomContainerView.bounds;
    startFrame.origin.y += startFrame.size.height;

    // end up where it's supposed to be, right in the container

    newController.view.frame = startFrame;

    [self transitionFromViewController:oldController
                      toViewController:newController
                              duration:0.5
                               options:0
                            animations:^{
                                newController.view.frame = self.bottomContainerView.bounds;
                            }
                            completion:^(BOOL finished) {
                                [oldController removeFromParentViewController];
                                [newController didMoveToParentViewController:self];
                            }];

}

Bottom line, I have no problem animating both container child controllers and the fields in the parent controller. Perhaps I'm not understanding the problem you're describing. Or maybe there's something subtle about the differences in how we're animating. If you want, I've put this above code on github if you want to take a look.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • If you want to animate both labels at the same time, put the two labels on their own label container view and then use that as the first parameter to `transitionWithView`. – Rob Dec 14 '12 at 22:17
  • I have one of them partly working correctly now. Is it not possible to combine `animateWithDuration` and `UIViewAnimationOptionTransitions` or to combine `transitionWithView` and `UIViewAnimationOptions` (non-transitions)? It seems when I have these they don't work but if I use transition options with `transitionWithView` it does work, and likewise with animations. I have read the docs and it seems like it is possible to combine all of them together but it hasn't worked for me. – Patrick Veilleux Dec 16 '12 at 22:27
  • Please see the revised question. Thanks – Patrick Veilleux Dec 17 '12 at 01:40
  • I saw your code on github and I can get the two animations to work if I call them separately. What I want though is to have a cross dissolve animation and frame change with a curve ease out option at the same time. Right now, if I call them at the same time the cross dissolve works, but the frame change hangs until the cross dissolve finishes and then frame changes without the animation. Maybe this is a clearer explanation. – Patrick Veilleux Dec 17 '12 at 19:48
  • @MusicMathTech Hmm, my demo could do simultaneous animations (if you could tap on a name and then quickly change the segmented control). I've changed the project so it does three animations simultaneously: 1. change the values of the two labels; 2. change the segmented control; and 3. swap out the child view controller. (In my particular demo, it doesn't make functional sense to swap the child view controller when you select a comedian from one list, but we're trying to demonstrate the feasibility of simultaneous animations and it appears to work fine.) I'm not sure why you can't do the same. – Rob Dec 17 '12 at 20:33
  • 1
    By the way, @MusicMathTech, make sure your `transitionWithView` is _not using_ `self.view` as the target. You really want your labels on a container subview of your main view controller. Likewise, when you first do the `addSubview` of your first container child controller's view, make sure you're not adding that to the main `self.view`, but rather that you've got some container subview that you're adding it to. That should minimize the unintended interactions between animations. If I change my `transitionWithView` to use `self.view`, I see the behavior you describe. – Rob Dec 17 '12 at 20:57
  • Thanks @Rob for the feedback. I am using storyboards so I don't manually use `addSubview`. I have restructured using your method but am uncertain if you mean to keep the parent view controller methods in the root view controller which contains the container view which holds the text view, image, and label, or do you mean to have them be in the view controller which is connected to the container view. I have tried the latter which does not change the result, but as I write this it seems like more and more like the former would be the way to go. Is this correct? – Patrick Veilleux Dec 17 '12 at 23:26
  • After much troubleshooting I now know why it was not working for me. I forgot that I was calling another animation block that was moving the frame (sometimes) of my `productDescriptionTextView` at the same time I had a cross dissolve for the text. This was causing the oddities to occur. If I comment out the method call I don't have these problems. Thanks for the help. – Patrick Veilleux Dec 18 '12 at 00:05
  • FWIW I was getting artefacts caused by simultaneous `animateWithDuration` on myView.alpha and `transitionWithView(myView.view, ...)`. Changing the former to instead animate myView.view.alpha fixed the conflict. I think the takeaway is that concurrent animations/transitions will be handled properly provided UIKit is told that the same root view is involved. – sleep Apr 29 '16 at 05:32
  • "You can experience this behavior of two animations interfering with each other"....I was making that mistake. I found it and now, it is working. Upvoted !! – NSPratik Feb 21 '17 at 10:16