0

EDIT: This SO answer seems to explain the reason why I'm experiencing the issues described below: UIViewController returns invalid frame?

I've got a problem with a custom animation. I'm doing a modal presentation of a viewController when a cell is tapped. But when the animation starts, it rotates my cell 90 degrees (as you can see in the screenshot below). I initially though it would have something to do with the orientation, but I'm taking a snapshot of the cell, so it shouldn't really affect it..?

1 http://foffer.dk/rotation90.png

Here is the code:

Initiating the viewController and presenting it (PhotosCollectionViewCont.m):

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    //Fetch data to display
    NSArray *photosArray = [self.dishes valueForKeyPath:@"dishes.content.image"];
    NSArray *nameArray = [self.dishes valueForKeyPath:@"dishes.content.name"];




    FOFDishDetailViewController *toVC = [[FOFDishDetailViewController alloc] init];
    toVC.view.backgroundColor = [UIColor lightGrayColor];


    [toVC.imageView setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://xx.yy.zz.qq:4000%@",[photosArray objectAtIndex: indexPath.row]]]];
    toVC.nameLabel.text = [NSString stringWithFormat:@"%@", [nameArray objectAtIndex:indexPath.row]];

    toVC.transitioningDelegate = self;
    toVC.modalPresentationStyle = UIModalPresentationCustom;

    [self presentViewController:toVC animated:YES completion:nil];        

    [self collectionView:collectionView didDeselectItemAtIndexPath:indexPath];
}

And the animation, called in the same PhotosCollectionViewCont.m:

-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{

    NSLog(@"context class is %@", [transitionContext class]);

    NSIndexPath *selected = self.collectionView.indexPathsForSelectedItems[0];
    UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:selected];

    UIView *container = transitionContext.containerView;

    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIView *toView = toVC.view;
    UIView *fromView = fromVC.view;


    NSTimeInterval duration = [self transitionDuration:transitionContext];

    CGRect beginFrame = [container convertRect:cell.bounds fromView:cell];

    CGRect endFrame = [transitionContext initialFrameForViewController:fromVC];

    //Remove comment for non-fullscreen
    //endFrame = CGRectInset(endFrame, 40.0, 40.0);
    fromVC.view.alpha = 1.0;
//    toVC.view.alpha = 0.3;

    CGRect mainScreen = [[UIScreen mainScreen] bounds];



    UIView *intermediateView = [cell snapshotViewAfterScreenUpdates:NO];
    intermediateView.frame = cell.frame;
    [container addSubview:intermediateView];

//    [container addSubview:move];

    [UIView animateKeyframesWithDuration:duration
                                   delay:0.0
                                 options:UIViewKeyframeAnimationOptionCalculationModeCubic
                              animations:^{


                                  [UIView addKeyframeWithRelativeStartTime:0.0
                                                          relativeDuration:0.5 animations:^{

                                                              intermediateView.frame = endFrame;
                                   }];
                                  [UIView addKeyframeWithRelativeStartTime:0.5
                                                          relativeDuration:0.5 animations:^{
                                                              fromVC.view.alpha = 0.0;
                                                              toVC.view.alpha = 1.0;
                                                          }];


                              } completion:^(BOOL finished) {

                                  toVC.view.frame = mainScreen;
                                  [container addSubview:toView];

                                  [intermediateView removeFromSuperview];
                                  fromVC.view.alpha = 1.0;
                                  [transitionContext completeTransition:YES];
                              }];
}

I know the animation itself is really messy, but at the moment, the only thing I care about is getting rid of that rotation issue.

If you need any more code to help me with this problem, please let me know and I will provide it.

Community
  • 1
  • 1
Chris
  • 7,830
  • 6
  • 38
  • 72

1 Answers1

0

This is similar to an issue I have had when doing UIViewControllerTransitioning things in Landscape.

For whatever reason the co-ordinate system in Portrait as far as this transition is concerned. I would guess that when you are getting the cell here:

UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:selected];

and snapshotting it here:

UIView *intermediateView = [cell snapshotViewAfterScreenUpdates:NO];

The cell is portrait whereas you are obviously viewing it in landscape. The solution can be quite complex, but I found that the easiest way to overcome this issue is to create a protocol for the UIViewController with the collection view inside.

In this protocol declare a readonly property called selectedCellSnapshotView.

/** A snapshot of the selected collection view cell.    */
@property (nonatomic, strong, readonly) UIView *selectedCellSnapshotView;

Obviously make the collection view adopt this protocol and in the getter for it do something like this:

/**
 * A snapshot of the selected collection view cell.
 *
 *  @return A snapshot of the selected collection view cell.
 */
- (UIView)selectedCellSnapshotView
{
    UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:self.selectedIndexPath];
    return [cell snapshotViewAfterScreenUpdates:NO];
}

Then move the transitioning delegate to it's own class.

Whilst this sounds mostly illogical, it worked for me, and so I hope it works for you.

An easy way to rule this whole answer out is if it acts differently when transited in Portrait. You have only shown a screenshot of Landscape, so I don't know.

I really hope this helps in some way, and if not, I apologise.

Infinity James
  • 4,667
  • 5
  • 23
  • 36
  • Thanks for your answer @Infinity James, I don't know if it helps, but when I tried to start the animation with the viewController (I.e. the viewController 'pops' out of the cell, it too was rotated 90 degrees. I will try your answer, and come back to you :) – Chris Apr 14 '14 at 07:41
  • Oh, and by the way, when it portrait mode, it displays correctly – Chris Apr 14 '14 at 08:02
  • I found this SO answer, which I think is the root cause of the problem: http://stackoverflow.com/questions/9539676/uiviewcontroller-returns-invalid-frame/9540935#9540935 – Chris Apr 14 '14 at 09:41
  • This is why I suggest separating out the transition delegate. You want to keep the UIViewController in the correct co-ordinate space, and by calling it only to get the snapshot of the cell, it should return the cell in the correct coordinates. The containerView should then be correct, and so adding the snapshot should be fine. – Infinity James Apr 14 '14 at 15:15