I'm presenting a UIViewController using presentViewController and a custom modalPresentationStyle, in an effort to implement a Facebook POP animated transition.
The modal view itself is completely dynamic, defined using Autolayout constraints in code. There is no xib/storyboard to back the modal.
I can't get the modal view to center on screen! Autolayout isn't sufficient, because there is no superview to add constraints on!
My presenting code looks like this (taken from a FB POP code sample):
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
UIView *fromView = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view;
fromView.tintAdjustmentMode = UIViewTintAdjustmentModeDimmed;
fromView.userInteractionEnabled = NO;
UIView *dimmingView = [[UIView alloc] initWithFrame:fromView.bounds];
dimmingView.backgroundColor = [UIColor colorWithRed:(24/255.0) green:(42/255.0) blue:(15/255.0) alpha:1.0];
dimmingView.layer.opacity = 0.0;
UIView *toView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view;
toView.frame = CGRectMake(0,
0,
CGRectGetWidth(transitionContext.containerView.bounds) - 104.f,
CGRectGetHeight(transitionContext.containerView.bounds) - 320.f);
toView.center = CGPointMake(transitionContext.containerView.center.x, -transitionContext.containerView.center.y);
[transitionContext.containerView addSubview:dimmingView];
[transitionContext.containerView addSubview:toView];
POPSpringAnimation *positionAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPositionY];
positionAnimation.toValue = @(transitionContext.containerView.center.y);
positionAnimation.springBounciness = 10;
[positionAnimation setCompletionBlock:^(POPAnimation *anim, BOOL finished) {
[transitionContext completeTransition:YES];
}];
POPSpringAnimation *scaleAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY];
scaleAnimation.springBounciness = 20;
scaleAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(1.2, 1.4)];
POPBasicAnimation *opacityAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity];
opacityAnimation.toValue = @(0.2);
[toView.layer pop_addAnimation:positionAnimation forKey:@"positionAnimation"];
[toView.layer pop_addAnimation:scaleAnimation forKey:@"scaleAnimation"];
[dimmingView.layer pop_addAnimation:opacityAnimation forKey:@"opacityAnimation"];
}
This works beautifully, but I need the actual view size to be dynamic (sometimes the modal will have four lines of text and two buttons, etc). To accomplish this, I need to set translatesAutoresizingMaskIntoConstraints=NO in the VC subclass. This obviously negates the frame centering I'm doing in the presentation animator.
The end result is a modal that's stuck to the left edge of the screen; curiously, it centers itself vertically, but not horizontally. Visually, it looks something like this (pardon the black squares, I had to do it for legal purposes):
The obvious solution would be to add a view constraint that centers the view. No problem, right?
But where do I add it? view.superview is nil; there is no superview. I tried creating a custom 'superview' property and setting it, but autolayout doesn't know how to handle a view that's outside of its view hierarchy (the presenting vc). This is what my view hierarchy looks like, annotated:
You're apparently not supposed to access the UITransitionView directly. Constraints on the UIWindow have no effect.
Does anyone have any advice? How do you guys handle this sort of thing?
Could you try this?
Declare your modal window view as subclass of
UIView
, and implementdidMoveToSuperView
.This should automatically do centering itself when added to any superview.
Needles to say, you also need
translatesAutoresizingMaskIntoConstraints = NO
and any width/height constraints.I haven't tested with
UITransitionView
, though. Maybe this conflict with yourpositionAnimation
.EDIT: 2014/09/22
Instead of using Autolayout constraints to modal view itself, I think you should use
systemLayoutSizeFittingSize:
method withouttranslatesAutoresizingMaskIntoConstraints = NO
.and If you want resize modal view after display (as a side effect of modifying it's content), do like following code in your modal views
UIViewController
subclass:In animateTransition: once you add your views to the hierarchy you can call a private method like [self addConstraints] and then do something like this:
You can programmatically add a centering constraint from containerView to toView in your animateTranisition method:
(in Swift, but you should be able to get the idea...)
When I tried this, I also added width and height constraints to toView to size it relative to containerView. It worked -- no problem.
I think it should work with a self-sizing toView as well. You might have to override intrinsicSize in your toView class and/or play around with forcing it to update its constraints.