17

我正在为我的一个应用程序创建一个自动布局友好的拆分视图类。它的各种特性之一是它可以折叠窗格,并且可以为它们的折叠设置动画,就像你可能看到的 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 上设置持续时间无效。

知道我在这里做错了什么吗?

4

4 回答 4

17

您必须首先设置完成处理程序,然后才将消息发送到动画代理。否则,似乎在动画开始后设置完成处理程序会立即触发它,并且在动画有时间完成之前删除常量。我刚刚用一段简单的代码检查了这一点:

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

[NSAnimationContext endGrouping]; 这完美地工作,但如果你在 之后设置完成处理程序-setConstant:,动画就没有机会运行。

于 2012-04-01T05:15:35.353 回答
13

我同意,这很奇怪,很可能是一个错误。我肯定会这样报告它,因为据我所知,这应该有效。

NSAnimationContext我能够通过使用类方法+runAnimationGroup:completionHandler:而不是beginGroupingandendGrouping语句来使其工作:

[NSAnimationContext runAnimationGroup:^(NSAnimationContext* context){
    [constraint.animator setConstant:self.width];   
} completionHandler:^(void){
    [theView removeConstraint:constraint];
    NSLog(@"completed");
}];
于 2012-03-17T01:03:41.417 回答
3

完成处理程序立即触发,因为它认为没有任何动画需要运行。我会检查并确认您创建的动画仍然附加到视图。默认情况下,CABasicAnimation 设置为通过从 CAAnimation 继承的 removedOnCompletion 属性在完成时移除自身(默认情况下设置为 YES)。

你会想要

anim.removedOnCompletion = NO;
于 2012-03-14T19:50:47.880 回答
1

我自己只是在掌握这些东西,所以这可能是一个幼稚的分析,但是:

在我看来,您正在指定约束属性上的动画(在您的 else 块中),但是在动画有机会运行之前立即将约束的引用设置为 nil (可能释放它)。

我希望您希望从动画完成块内部或由动画完成块触发将 hiddenConstraint 设置为 nil。

请注意,如果我很可能是错的,我会感谢一两句话来帮助我更好地理解它:)

于 2012-03-14T14:09:45.743 回答