Cocoa 动画自动布局更改与NSPopover contentSize更改同时进行

Cocoa 动画自动布局更改与NSPopover contentSize更改同时进行,cocoa,animation,autolayout,nspopover,Cocoa,Animation,Autolayout,Nspopover,我试图在popover中重现iTunes11的导航视图行为。不过,我似乎找不到一种方法让我的动画与popover的contentSize变化同时发生 我的基本设置是一个自定义视图子类MyPopoverNavigationView,它有两个子视图:我希望popover在新旧视图之间导航。popover的contentViewController有一个MyPopoOverNavigationView实例作为其视图。我这样做: // Configure constraints how I want t

我试图在popover中重现iTunes11的导航视图行为。不过,我似乎找不到一种方法让我的动画与popover的
contentSize
变化同时发生

我的基本设置是一个自定义视图子类MyPopoverNavigationView,它有两个子视图:我希望popover在新旧视图之间导航。popover的
contentViewController
有一个MyPopoOverNavigationView实例作为其
视图
。我这样做:

// Configure constraints how I want them to show the new popover view
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *ctx) {
    [ctx setDuration:0.25];
    [ctx setAllowsImplicitAnimation:YES];
    [self layoutSubtreeIfNeeded];
} completionHandler:nil];
从自动布局WWDC 2012视频中可以看出,这是一种推荐的方法,可以通过约束更改对视图帧进行动画更改。它可以工作,但动画分两个阶段进行:

  • 首先,popover的
    contentSize
    将更改以适应我要移动到的新视图(在该视图可见之前,因此它会部分遮挡现有内容)
  • 第二,视图按我所期望的那样设置动画,以便满足我安装的约束系统
通过设置一些断点,看起来
-layoutSubtreeIfNeeded
最终调用了popover上名为
\u fromConstraintsSetWindowFrame:
的私有方法,该方法在我的动画组之外执行popover大小的动画。我的上下文的持续时间不受尊重,我的动画也不会发生,直到popover的大小更改完成


如何使我的视图与popover的大小更改一起设置动画?

结果表明,技巧是在动画和完成块之外显式设置popover的
contentSize
属性。我将其中的相关片段放在一起进行分析,结果如下所示:

// Configure constraints for post-navigation view layout
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *ctx) {
    [ctx setDuration:0.25];
    [ctx setAllowsImplicitAnimation:YES];
    [self layoutSubtreeIfNeeded];
} completionHandler:^{
    // Tear down some leftover constraints from before the transition
}];

// Explicitly set popover's contentSize so its animation happens simultaneously
containingPopover.contentSize = postTransitionView.frame.size;

这对我在Sierra的工作很好:

let deltaHeight = 8 
let contentSize = popover.contentSize  
NSAnimationContext.runAnimationGroup({ (context) -> Void in
    context.allowsImplicitAnimation = true
    popover.contentSize = NSSize(width: contentSize.width, height: contentSize.height+deltaHeight)
})