9

Ich präsentiere einen UIViewController, der presentViewController und einen benutzerdefinierten modalPresentationStyle verwendet, um einen animierten Facebook-POP-Übergang zu implementieren.Zentrieren von Modal View mit Autolayout

Die modale Ansicht selbst ist vollständig dynamisch und wird mit Autolayout-Einschränkungen im Code definiert. Es gibt kein XIB/Storyboard, um das Modal zu unterstützen.

Ich kann nicht die modale Ansicht auf dem Bildschirm zentrieren! Autolayout ist nicht ausreichend, da es keinen Superview zum Hinzufügen von Constraints gibt!

Mein präsentierenden Code sieht wie folgt aus (von FB POP Code Probe entnommen):

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

Dies funktioniert wunderbar, aber ich brauche die aktuelle Ansicht Größe dynamisch zu sein (manchmal wird die modale haben vier Zeilen Text und zwei Schaltflächen usw.). Um dies zu erreichen, muss ich translateAutoresizingMaskIntoConstraints = NO in der VC-Unterklasse setzen. Dies negiert offensichtlich die Rahmenzentrierung, die ich in dem Präsentationsanimator mache.

Das Endergebnis ist ein Modal, das am linken Bildschirmrand hängt; Interessanterweise zentriert es sich vertikal, aber nicht horizontal. Optisch sieht es so etwas wie dies (die schwarzen Quadrate verzeihen, ich hatte es aus rechtlichen Gründen zu tun):

screesshot

Die offensichtliche Lösung, die eine Ansicht Einschränkung hinzuzufügen wäre, die die Ansicht Mitte. Kein Problem, oder?

Aber wo füge ich es hinzu? view.superview ist null; Es gibt keinen Superview. Ich habe versucht, eine benutzerdefinierte "Superview" -Eigenschaft zu erstellen und sie zu setzen, aber das automatische Layout kann nicht mit einer Ansicht umgehen, die sich außerhalb ihrer Ansichtshierarchie (dem präsentierenden VC) befindet. Dies ist, was meiner Ansicht nach Hierarchie aussieht, kommentierte:

enter image description here

Sie sind offenbar die UITransitionView den direkten Zugriff nicht zugelassen. Einschränkungen für das UIWindow haben keine Auswirkungen.

Hat jemand einen Rat? Wie gehst du mit so etwas um?

+0

Ich habe nicht wirklich die Lösung für Sie aber ich sage Ihnen, was ich in dieser Situation tun würde: ** Kommentieren Sie alle Autlayout **, ** Setzen Sie Position über CGRectMake (**. Noch eine Sache: Die Art, wie Sie diese Frage strukturiert haben, ist es schwer zu sagen, wo Das Problem ist, viel Glück – carlodurso

+0

Das ist die catch-22 Ich muss Auto-Layout verwenden, da die Ansicht vollständig dynamisch sein muss – cdstamper

Antwort

3

Sie können programmatisch Fügen Sie in Ihrer animateTransition-Methode eine Zentrierungseinschränkung von containerView toView hinzu:

(in Swift, aber Sie sollten in der Lage sein, die Idee zu bekommen ...)

Wenn ich dies versuchte, fügte ich auch toView, um es relativ zu ContainerView Größe zu bestimmen, Breite und Höhe Einschränkungen. Es hat funktioniert - kein Problem.

Ich denke, es sollte auch mit einem Self-Sizing-toView arbeiten. Möglicherweise müssen Sie intrinsicSize in Ihrer toView-Klasse überschreiben und/oder damit umgehen, dass Sie gezwungen sind, seine Beschränkungen zu aktualisieren.

+0

Funktioniert PERFEKT. Ich bin ein wenig besorgt, dass ich nicht direkt auf containerView zugreifen darf ... aber das ist eine Lösung für den Moment. Vielen Dank! – cdstamper

+0

Gern geschehen! Schön, dass es für dich funktioniert hat! Ja, ich bin neugierig auf die Einschränkungen des View-Containers. Ich meine, Sie müssen zumindest Unteransichten hinzufügen können. Vielleicht besteht die Einschränkung eher darin, sich bei animateTransition auf seinen Zustand (Größe, Position usw.) zu verlassen? –

+0

Es funktioniert, ich werde einreichen und sehen, was passiert. Ich werde in ein paar Monaten aktualisieren, wenn es abgelehnt wird! – cdstamper

2

Könnten Sie das versuchen?

Deklarieren Sie Ihre modale Fensteransicht als Unterklasse von UIView und implementieren Sie 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. 
} 

Diese automatisch selbst, wenn sie jede Superview hinzugefügt haben Zentrierung sollte.

Nadeln zu sagen, müssen Sie auch translatesAutoresizingMaskIntoConstraints = NO und alle Width/Höhe Einschränkungen.

Ich habe nicht mit UITransitionView getestet, obwohl. Vielleicht dieser Konflikt mit Ihrem positionAnimation.

EDIT: 2014/09/22

Statt Automatische Anordnung Einschränkungen der Verwendung Ansicht selbst modal, ich glaube, Sie systemLayoutSizeFittingSize: Methode ohnetranslatesAutoresizingMaskIntoConstraints = NO verwenden sollten.

- (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 

} 

und Wenn Sie Ansicht modal wollen nach Anzeige Größe ändern (als Nebenwirkung modifizieren Inhalt es ist), tun wie in Ihrem modalen Ansichten folgenden Code UIViewController Unterklasse:

- (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

Ich kann nicht zu UIView-Unterklasse ändern, es muss ein VC sein . Das Festlegen von Einschränkungen für die transitioncontext-Container-Ansicht hat den Zweck erfüllt! Danke für die Tipps. – cdstamper

0

In animateTransition: sobald Sie Ihre Ansichten der Hierarchie fügen Sie eine private Methode wie [self addConstraints] aufrufen und dann etwas tun, wie folgt aus:

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