tutorial objective life example containerviewcontroller container ios objective-c uiviewcontroller uiviewanimationtransition

objective - lifecycle viewcontroller ios



Centrado de la Vista modal con Autolayout (3)

Presentaré un UIViewController utilizando presentViewController y un modalPresentationStyle personalizado, en un esfuerzo por implementar una transición animada POP de Facebook.

La vista modal en sí es completamente dinámica, definida usando restricciones de autodiseño en el código. No hay xib / storyboard para respaldar el modal.

¡No puedo hacer que la vista modal se centre en la pantalla! El diseño automático no es suficiente, ¡porque no hay una supervista para agregar restricciones!

Mi código de presentación se ve así (tomado de un ejemplo de código FB POP):

- (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"]; }

Esto funciona muy bien, pero necesito que el tamaño de la vista sea dinámico (a veces el modal tendrá cuatro líneas de texto y dos botones, etc.). Para lograr esto, necesito establecer tralationsAutoresizingMaskIntoConstraints = NO en la subclase de VC. Esto obviamente niega el centrado de cuadros que estoy haciendo en el animador de presentación.

El resultado final es un modal que está pegado al borde izquierdo de la pantalla; curiosamente, se centra verticalmente, pero no horizontalmente. Visualmente, se ve algo como esto (perdón por los cuadrados negros, tuve que hacerlo para fines legales):

La solución obvia sería agregar una restricción de vista que centre la vista. No hay problema, ¿verdad?

¿Pero dónde lo agrego? view.superview es nulo; no hay una supervista Intenté crear una propiedad ''superview'' personalizada y configurarla, pero autolayout no sabe cómo manejar una vista que está fuera de su jerarquía de vistas (la vc de presentación). Así es como se ve mi jerarquía de vistas, anotada:

Aparentemente, no se supone que tengas que acceder a UITransitionView directamente. Las restricciones en la ventana UI no tienen ningún efecto.

¿Alguien tiene algún consejo? ¿Cómo manejan este tipo de cosas?


¿Podrías probar esto?

Declare su vista de ventana modal como la subclase de UIView e implemente didMoveToSuperView .

- (void)didMoveToSuperview { UIView *superView = self.superview; if(superView == nil) { return; } [superView addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]]; [superView addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]]; // [superView layoutIfNeeded]; // If this does not work, try uncomment this. }

Esto debería centrarse automáticamente cuando se agrega a cualquier supervista.

Agujas para decir, también necesita translatesAutoresizingMaskIntoConstraints = NO y cualquier restricción de ancho / altura.

Aunque no he probado con UITransitionView . Tal vez este conflicto con su positionAnimation .

EDITAR: 2014/09/22

En lugar de usar restricciones de Autolayout para la vista modal, creo que debería usar systemLayoutSizeFittingSize: method sin translatesAutoresizingMaskIntoConstraints = NO .

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext { // ...snip UIView *toView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view; toView.translatesAutoresizingMaskIntoConstraints = YES; // To clarify. You don''t need this line because this is the default. CGSize sysSize = [toView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; toView.bounds = CGRectMake(0, 0, sysSize.width, sysSize.height); toView.center = CGPointMake(transitionContext.containerView.center.x, -transitionContext.containerView.center.y); toView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin; // ...snip }

y Si desea cambiar el tamaño de la vista modal después de la visualización (como efecto secundario de modificar su contenido), haga UIViewController en el siguiente código en su subclase UIViewController vistas UIViewController :

- (void)yourAppMethod { NSString *message = @""; // <- as u like UILabel *label = self.messageLabel; label.text = message; [self resizeViewIfNeeded]; // <- this will resize self.view } - (void)resizeViewIfNeeded { CGSize sysSize = [self.view systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; if(!CGSizeEqualToSize(sysSize, self.view.bounds.size)) { self.view.bounds = CGRectMake(0, 0, sysSize.width, sysSize.height); } }


En animateTransition: una vez que agrega sus vistas a la jerarquía , puede llamar a un método privado como [self addConstraints] y luego hacer algo como esto:

- (void)addConstraints { [self.toView setTranslatesAutoresizingMaskIntoConstraints:NO]; [self.dimmingView setTranslatesAutoresizingMaskIntoConstraints:NO]; [self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:self.toView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; [self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:self.toView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]]; NSDictionary *views = @{@"dimmingView" : self.dimmingView}; [self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[dimmingView]|" options:0 metrics:nil views:views]]; [self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[dimmingView]|" options:0 metrics:nil views:views]]; }


Puede agregar mediante programación una restricción de centrado de containerView a toView en su método animateTranisition:

(En Swift, pero deberías ser capaz de entender la idea ...)

containerView.addSubview(toView) let centerXLayoutConstraint = NSLayoutConstraint(item: toView, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: containerView, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0) let centerYLayoutConstraint = NSLayoutConstraint(item: toView, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: containerView, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0) containerView.addConstraint(centerXLayoutConstraint) containerView.addConstraint(centerYLayoutConstraint)

Cuando probé esto, también agregué restricciones de ancho y alto a toView para darle un tamaño relativo a containerView. Funcionó, no hay problema.

Creo que debería funcionar también con un auto-tamaño en View. Es posible que tenga que anular el tamaño intrínseco en su clase toView y / o jugar con forzarlo a actualizar sus restricciones.