Problems with transitionWithView and animateWithDuration
Asked Answered
C

1

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.

Clerissa answered 14/12, 2012 at 20:13 Comment(0)
I
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.

Illiquid answered 14/12, 2012 at 21:57 Comment(10)
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.Illiquid
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.Clerissa
Please see the revised question. ThanksClerissa
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.Clerissa
@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.Illiquid
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.Illiquid
Thanks @Illiquid 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?Clerissa
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.Clerissa
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.Benisch
"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 !!Wenz

© 2022 - 2024 — McMap. All rights reserved.