Центрирование модального просмотра с автозапуском

Я представляю UIViewController, используя presentViewController и собственный modalPresentationStyle, чтобы реализовать анимированный переход POP на Facebook.

Модальное представление полностью динамично, определяется с помощью ограничений Autolayout в коде. Существует нет xib/раскадровки, чтобы поддержать модальный.

Я не могу получить модальное представление на экране! Autolayout недостаточно, потому что нет супервизора для добавления ограничений на!

Мой представляющий код выглядит так (взятый из примера кода 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"];
}

Это работает красиво, но мне нужен фактический размер представления, чтобы быть динамичным (иногда модальный будет иметь четыре строки текста и две кнопки и т.д.). Для этого мне нужно установить translatesAutoresizingMaskIntoConstraints = NO в подклассе VC. Это, очевидно, отрицает центрирование кадра, которое я выполняю в аниматоре презентации.

Конечный результат - модальный, прикрепленный к левому краю экрана; Любопытно, что он центрирует себя вертикально, но не горизонтально. Визуально это выглядит примерно так (помилуй черные квадраты, я должен был сделать это для юридических целей):

screesshot

Очевидным решением было бы добавить ограничение представления, которое центрирует представление. Нет проблем, правильно?

Но где его добавить? view.superview - nil; нет супервизора. Я попытался создать собственное свойство "superview" и установить его, но автозапуск не знает, как обрабатывать представление, находящееся за пределами его иерархии представлений (презентация vc). Вот как выглядит моя иерархия просмотров, аннотированная:

enter image description here

Вы, очевидно, не должны напрямую обращаться к UITransitionView. Ограничения на UIWindow не влияют.

Есть ли у кого-нибудь советы? Как вы, ребята, справляетесь с этим?

Ответ 1

Вы можете программно добавить ограничение на центрирование из containerView в toView в вашем методе animateTranisition:

(в Swift, но вы должны получить эту идею...)

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)

Когда я попробовал это, я также добавил ограничения ширины и высоты в toView для его размера по сравнению с containerView. Он работал - без проблем.

Я думаю, что он должен работать с саморазметкой toView. Возможно, вам придется переопределить intrinsicSize в вашем классе toView и/или поиграть, заставив его обновить свои ограничения.

Ответ 2

Не могли бы вы попробовать?

Объявите свой вид модального окна как подкласс UIView и реализуйте 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.
}

Это должно автоматически центрировать себя при добавлении в любой супервизор.

Игла, чтобы сказать, вам также нужны translatesAutoresizingMaskIntoConstraints = NO и любые ограничения ширины/высоты.

Я не тестировал с помощью UITransitionView. Возможно, этот конфликт с вашим positionAnimation.

EDIT: 2014/09/22

Вместо того, чтобы использовать ограничения Autolayout для самого модального представления, я думаю, вы должны использовать systemLayoutSizeFittingSize: метод без 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

}

и если вы хотите изменить размер модального представления после (как побочный эффект изменения его содержимого), выполните следующий код в ваших модальных представлениях 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);
    }
}

Ответ 3

В animateTransition: после добавления ваших представлений в иерархию вы можете вызвать частный метод, например [self addConstraints], а затем сделать что-то вроде этого:

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