9

Sto presentando un UIViewController utilizzando presentViewController e un modalPresentationStyle personalizzato, nel tentativo di implementare una transizione animata POP di Facebook.Centrare la vista modale con Autolayout

La vista modale è completamente dinamica, definita utilizzando i vincoli di Autolayout nel codice. Non ci sono xib/storyboard per supportare il modal.

Non riesco a ottenere la visualizzazione modale al centro sullo schermo! L'autolayout non è sufficiente, perché non c'è superview per aggiungere vincoli!

Il mio codice presentazione simile a questa (presa da un campione di FB di codice 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"]; 
} 

Questo funziona in modo bello, ma ho bisogno delle dimensioni di visualizzazione effettiva di essere dinamica (a volte il modal avrà quattro righe di testo e due pulsanti, ecc.). Per fare ciò, ho bisogno di impostare translatesAutoresizingMaskIntoConstraints = NO nella sottoclasse VC. Questo ovviamente nega il centraggio del frame che sto facendo nell'animatore della presentazione.

Il risultato finale è una modale bloccata sul bordo sinistro dello schermo; curiosamente, si centra verticalmente, ma non orizzontalmente. Visivamente, sembra qualcosa di simile (pardon i quadrati neri, ho dovuto farlo per motivi legali):

screesshot

La soluzione più ovvia sarebbe quella di aggiungere un vincolo vista che centra la vista. Nessun problema, giusto?

Ma dove lo aggiungo? view.superview è nullo; non c'è superview. Ho provato a creare una proprietà personalizzata 'superview' e ad impostarla, ma l'autoapprendimento non sa come gestire una vista al di fuori della sua gerarchia di visualizzazione (la vc di presentazione). Questo è ciò che la mia gerarchia di vista assomiglia, annotato:

enter image description here

Stai a quanto pare non suppone di accedere direttamente alla UITransitionView. I vincoli sulla UIWindow non hanno alcun effetto.

Qualcuno ha qualche consiglio? Come gestite questo genere di cose?

+0

io in realtà non hanno la soluzione per ma io ti dico cosa farei in questa situazione: ** Commenta qualsiasi Autolayout **, ** Imposta posizione tramite CGRectMake (**. Ancora una cosa: il modo in cui hai strutturato questa domanda è difficile da dire dove il problema è: buona fortuna – carlodurso

+0

Questo è il trucco 22. Devo usare il layout automatico, perché la vista deve essere completamente dinamica. – cdstamper

risposta

3

di programmazione è possibile aggiungi un vincolo di centratura da containerView a toView nel metodo animateTranisition:

(in Swift, ma dovresti riuscire a ottenere l'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) 

Quando ho provato questo, ho anche aggiunto i vincoli di larghezza e altezza a toView per ridimensionarlo rispetto a containerView. Ha funzionato - nessun problema.

Penso che dovrebbe funzionare anche con un auto-dimensionamento di toView. Potrebbe essere necessario sovrascrivere intrinsicSize nella classe toView e/o aggirarlo forzandolo ad aggiornare i suoi vincoli.

+0

funziona PERFETTAMENTE. Sono un po 'preoccupato per il fatto che non sono autorizzato ad accedere direttamente a containerView ... ma questa è una soluzione per ora. Grazie! – cdstamper

+0

Prego! Sono contento che abbia funzionato per te! Sì, sono curioso delle restrizioni sul contenitore della vista. Voglio dire, devi essere in grado di aggiungere viste secondarie, almeno. Forse la restrizione è più di fare affidamento sul suo stato (dimensione, posizione, ecc.) Durante animateTransition? –

+0

Funziona, sto per presentare e vedere cosa succede. Aggiornerò tra qualche mese se viene rifiutato! – cdstamper

2

Puoi provare questo?

Dichiarare la visualizzazione finestra modale come sottoclasse di UIView e implementare 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. 
} 

Questo dovrebbe automaticamente non si centratura quando aggiunto a qualsiasi superview.

Aghi per dire, è necessario anche translatesAutoresizingMaskIntoConstraints = NO e qualsiasi larghezza/altezza vincoli.

Non ho ancora provato con UITransitionView. Forse questo conflitto con il tuo positionAnimation.

EDIT: 2014/09/22

Invece di usare i vincoli layout automatico a modale vista stessa, penso che si dovrebbe utilizzare il metodo systemLayoutSizeFittingSize:senzatranslatesAutoresizingMaskIntoConstraints = 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 

} 

e se si vuole ridimensionare vista modale dopo visualizzazione (come effetto collaterale di modificare il suo contenuto), fare come seguente codice nel vostro punto di vista modale UIViewController sottoclasse:

- (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); 
    } 
} 
+0

Non riesco a passare alla sottoclasse UIView, deve essere un VC . Impostare i vincoli sulla vista del container transitioncontext ha fatto il trucco! Grazie per i consigli – cdstamper

0

In animateTransition: una volta aggiunto vostro punto di vista alla gerarchia è possibile chiamare un metodo privato come [addConstraints auto] e poi fare qualcosa di simile:

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