我已经尝试了我能想到的所有内容,包括我在SO和其他邮件列表中找到的所有建议,但我无法弄清楚如何以编程方式折叠带有动画的NSSplitView
窗格自动布局已开启。
这就是我现在所拥有的(用Swift写的有趣),但它有多种方式:
@IBAction func toggleSourceList(sender: AnyObject?) {
let isOpen = !splitView.isSubviewCollapsed(sourceList.view.superview!)
let position = (isOpen ? 0 : self.lastWidth)
if isOpen {
self.lastWidth = sourceList.view.frame.size.width
}
NSAnimationContext.runAnimationGroup({ context in
context.allowsImplicitAnimation = true
context.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
context.duration = self.duration
self.splitView.setPosition(position, ofDividerAtIndex: 0)
}, completionHandler: { () -> Void in
})
}
期望的行为和外观是Mail.app,它的动画效果非常好。
我在https://github.com/mdiep/NSSplitViewTest处有一个完整的示例应用程序。
答案 0 :(得分:17)
<强>目标-C:强>
[[splitViewItem animator] setCollapse:YES]
<强>夫特:强>
splitViewItem.animator().collapsed = true
来自Apple的帮助:
子ViewController是否对应 SplitViewItem在SplitViewController中折叠。默认是 没有。这可以使用动画制作代理进行设置,以便为崩溃或动画制作动画 uncollapse。使用的确切动画可以通过设置来定制 在-animations字典中,键是“折叠”。如果是这样的话 在将它添加到SplitViewController之前设置为YES,它将是 最初崩溃,SplitViewController不会导致 视图加载,直到它被解除。这符合KVC / KVO标准 如果值因用户交互而发生变化,则会更新。
答案 1 :(得分:1)
我最终能够在一些帮助下解决这个问题。我已将我的测试项目转换为可重用的NSSplitView
子类:https://github.com/mdiep/MDPSplitView
答案 2 :(得分:0)
如果您正在使用自动布局,并且想要为视图的尺寸/位置的某些方面制作动画,那么您可能会有更多的运气来限制约束本身。我已经快速完成了NSSplitView
,但到目前为止只取得了有限的成功。按下按钮后,我可以进行拆分以进行扩展和折叠,但我最终不得不试图通过干扰约束来解决大量其他问题。如果你不熟悉它,这里是一个简单的约束动画:
- (IBAction)animate:(NSButton *)sender {
/* Shrink view to invisible */
NSLayoutConstraint *constraint = self.viewWidthConstraint;
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[[NSAnimationContext currentContext] setDuration:0.33];
[[NSAnimationContext currentContext] setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
[[constraint animator] setConstant:0];
} completionHandler:^{
/* Do Some clean-up, if required */
}];
请注意,您只能为约束constant
制作动画,但您无法为其priority
制作动画。
答案 3 :(得分:0)
由于某种原因,没有任何动画帧的方法适用于我的scrollview。我没有尝试动画约束。
我最终创建了一个自定义动画来为分隔符位置设置动画。如果有人有兴趣,这是我的解决方案:
动画.h:
@interface MySplitViewAnimation : NSAnimation <NSAnimationDelegate>
@property (nonatomic, strong) NSSplitView* splitView;
@property (nonatomic) NSInteger dividerIndex;
@property (nonatomic) float startPosition;
@property (nonatomic) float endPosition;
@property (nonatomic, strong) void (^completionBlock)();
- (instancetype)initWithSplitView:(NSSplitView*)splitView
dividerAtIndex:(NSInteger)dividerIndex
from:(float)startPosition
to:(float)endPosition
completionBlock:(void (^)())completionBlock;
@end
动画.m
@implementation MySplitViewAnimation
- (instancetype)initWithSplitView:(NSSplitView*)splitView
dividerAtIndex:(NSInteger)dividerIndex
from:(float)startPosition
to:(float)endPosition
completionBlock:(void (^)())completionBlock;
{
if (self = [super init]) {
self.splitView = splitView;
self.dividerIndex = dividerIndex;
self.startPosition = startPosition;
self.endPosition = endPosition;
self.completionBlock = completionBlock;
[self setDuration:0.333333];
[self setAnimationBlockingMode:NSAnimationNonblocking];
[self setAnimationCurve:NSAnimationEaseIn];
[self setFrameRate:30.0];
[self setDelegate:self];
}
return self;
}
- (void)setCurrentProgress:(NSAnimationProgress)progress
{
[super setCurrentProgress:progress];
float newPosition = self.startPosition + ((self.endPosition - self.startPosition) * progress);
[self.splitView setPosition:newPosition
ofDividerAtIndex:self.dividerIndex];
if (progress == 1.0) {
self.completionBlock();
}
}
@end
我这样使用它 - 我有一个3窗格分割器视图,并且正在以固定数量(235)移动/移出右窗格。
- (IBAction)togglePropertiesPane:(id)sender
{
if (self.rightPane.isHidden) {
self.rightPane.hidden = NO;
[[[MySplitViewAnimation alloc] initWithSplitView:_splitView
dividerAtIndex:1
from:_splitView.frame.size.width
to:_splitView.frame.size.width - 235
completionBlock:^{
;
}] startAnimation];
}
else {
[[[MySplitViewAnimation alloc] initWithSplitView:_splitView
dividerAtIndex:1
from:_splitView.frame.size.width - 235
to:_splitView.frame.size.width
completionBlock:^{
self.rightPane.hidden = YES;
}] startAnimation];
}
}
答案 4 :(得分:0)
NSSplitViewItem
(即NSSplitView
的已安排子视图)可以完全折叠,如果它可以达到Zero
尺寸(宽度或高度)。因此,我们只需要在动画之前停用适当的约束,并允许视图达到Zero
维度。在动画之后,我们可以再次激活所需的约束。
请参阅我对SO问题How to expand and collapse NSSplitView subviews with animation?的评论。
答案 5 :(得分:0)
这是一个不需要任何子类或类别的解决方案,无需NSSplitViewController
(需要macOS 10.10+)才能运行,支持自动布局,为视图添加动画效果,并且可以在macOS 10.8+上运行。
正如其他人所建议的那样,解决方案是使用NSAnimationContext,但诀窍是设置context.allowsImplicitAnimation = YES
(Apple docs)。然后只需将分隔线位置设置为通常的位置即可。
#import <Quartz/Quartz.h>
#import <QuartzCore/QuartzCore.h>
- (IBAction)toggleLeftPane:(id)sender
{
[NSAnimationContext runAnimationGroup:^(NSAnimationContext * _Nonnull context) {
context.allowsImplicitAnimation = YES;
context.duration = 0.25; // seconds
context.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
if ([self.splitView isSubviewCollapsed:self.leftPane]) {
// -> expand
[self.splitView setPosition:self.leftPane.frame.size.width ofDividerAtIndex:0];
} else {
// <- collapse
_lastLeftPaneWidth = self.leftPane.frame.size.width;
// optional: remember current width to restore to same size
[self.splitView setPosition:0 ofDividerAtIndex:0];
}
[self.splitView layoutSubtreeIfNeeded];
}];
}
使用自动布局来约束子视图(宽度,最小/最大大小等)。确保在界面生成器中选中“核心动画层”(即,将视图设置为支持图层的视图)以查看拆分视图和所有子视图-要对过渡进行动画处理,这是必需的。 (它仍然可以使用,但是没有动画。)
此处提供了完整的工作项目:https://github.com/demitri/SplitViewAutoLayout。
答案 6 :(得分:0)
/// Collapse the sidebar
func collapsePanel(_ number: Int = 0){
guard number < self.splitViewItems.count else {
return
}
let panel = self.splitViewItems[number]
if panel.isCollapsed {
panel.animator().isCollapsed = false
} else {
panel.animator().isCollapsed = true
}
}
答案 7 :(得分:0)
我还要补充一点,因为我花了很长时间才弄清楚这一点,如果您有很多定义布局的约束,那么在您的 collapseBehavior = .useConstraints
(或项目)上设置 NSSplitViewItem
可能会非常有用您的子视图。在我这样做之前,我的拆分视图动画看起来并不正确。天啊。