Подтвердить что ты не робот

NSLayoutConstraint.constant игнорирует анимацию

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

Поскольку я использую ограничения, я достигаю этого, устанавливая ограничение на ширину = (текущая ширина) на панели, а затем устанавливая константу ограничения в 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 не влияет.

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Мне удалось заставить его работать с помощью метода NSAnimationContext class +runAnimationGroup:completionHandler: вместо операторов beginGrouping и endGrouping:

[NSAnimationContext runAnimationGroup:^(NSAnimationContext* context){
    [constraint.animator setConstant:self.width];   
} completionHandler:^(void){
    [theView removeConstraint:constraint];
    NSLog(@"completed");
}];

Ответ 3

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

вы хотите

anim.removedOnCompletion = NO;

Ответ 4

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

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

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

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