NSLayoutConstraint.constant игнорирует анимацию - PullRequest
16 голосов
/ 29 февраля 2012

Я создаю удобный для разделения класс для одного из моих приложений. Среди его различных функций - возможность сворачивать панели и анимировать их сворачивание, как вы, наверное, видели в NSSplitView.

Поскольку я использую ограничения, я достигаю этого, помещая требуемое ограничение width = (current width) на панель, а затем устанавливаю постоянную ограничения в 0 в анимированном виде:

- (NSLayoutConstraint*)newHiddenConstraintAnimated:(BOOL)animated {
    NSLayoutConstraint * constraint = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:NSWidth(self.view.frame)];
    constraint.priority = NSLayoutPriorityRequired;

    CABasicAnimation * anim = [CABasicAnimation animation];
    anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    anim.duration = 0.2;
    constraint.animations = [NSDictionary dictionaryWithObject:anim forKey:@"constant"];

    [self.view addConstraint:constraint];

    [(animated ? constraint.animator : constraint) setConstant:0.0];

    return constraint;
}

Это прекрасно работает. К сожалению, расширение панели позже не очень хорошо.

- (void)removeHiddenConstraintAnimated:(BOOL)animated {
    if(!animated) {
        [self.view removeConstraint:self.hiddenConstraint];
    }
    else {
        NSLayoutConstraint * constraint = self.hiddenConstraint;
        NSView * theView = self.view;

        [NSAnimationContext beginGrouping];

        [constraint.animator setConstant:self.width];

        [NSAnimationContext currentContext].completionHandler = ^{
            [theView removeConstraint:constraint];
        };

        [NSAnimationContext endGrouping];
    }

    self.hiddenConstraint = nil;
}

Если я вставлю какой-нибудь временный код, то увижу, что обработчик завершения срабатывает почти мгновенно, удаляя ограничение до того, как оно успеет анимироваться. Установка длительности в NSAnimationContext не имеет никакого эффекта.

Есть идеи, что я могу здесь делать неправильно?

Ответы [ 4 ]

17 голосов
/ 01 апреля 2012

Вы должны сначала установить обработчик завершения и только потом отправлять сообщение на прокси-сервер аниматора.В противном случае кажется, что установка обработчика завершения после запуска анимации запускает его немедленно, и константа удаляется до того, как анимация успевает завершиться.Я только что проверил это с помощью простого кода:

[NSAnimationContext beginGrouping];
NSAnimationContext.currentContext.duration = animagionDuration;
NSAnimationContext.currentContext.completionHandler = ^{
  [self removeConstraint:collapseConstraint];
};
[collapseConstraint.animator setConstant:expandedHeight];

[NSAnimationContext endGrouping];Это работает отлично, но если вы установите обработчик завершения после -setConstant:, анимация не сможет запуститься.

12 голосов
/ 17 марта 2012

Согласен, это довольно странно и вполне может быть ошибкой.Я бы определенно сообщил об этом, потому что, насколько мне известно, этот должен работать.

Я смог заставить его работать, используя NSAnimationContext метод класса +runAnimationGroup:completionHandler: вместо операторов beginGrouping и endGrouping:

[NSAnimationContext runAnimationGroup:^(NSAnimationContext* context){
    [constraint.animator setConstant:self.width];   
} completionHandler:^(void){
    [theView removeConstraint:constraint];
    NSLog(@"completed");
}];
3 голосов
/ 14 марта 2012

Обработчик завершения запускается немедленно, потому что считает, что анимации не нужно запускать.Я бы проверил и подтвердил, что созданная вами анимация все еще привязана к представлению.По умолчанию CABasicAnimation настроен на удаление самого себя после завершения с помощью свойства removeOnCompletion, которое он наследует от CAAnimation (для которого по умолчанию установлено значение YES).

вам потребуется

anim.removedOnCompletion = NO;
1 голос
/ 14 марта 2012

Я только сам разбираюсь с этим, так что это может быть наивный анализ, но:

Мне кажется, что вы указываете анимацию в свойствах ограничений (в вашем блоке else), но затем немедленно установите ссылку на ограничение равным nil (потенциально освобождая его) до того, как анимация сможет запустить.

Я ожидаю, что вы захотите установить для hiddenConstraint значение nil изнутри, илизапускается блоком завершения анимации.

Обратите внимание, что, если, скорее всего, я ошибаюсь, я был бы признателен за пару слов о том, почему мне лучше понять это:)

...