移动嵌套 NSSplitView 的拆分器使其向外生长

Moving the splitter of a nested NSSplitView makes it grow outwardly

嵌套 NSSplitView 个实例会导致奇怪的行为。拖动嵌套 splitView 的拆分器时,可以移动父级 splitView 的拆分器以容纳更大的子级作为拖动的副作用。

这是一个重现此问题的极简主义项目的 URL。只需将最右侧的分离器向右拖动,直到最左侧的分离器开始自行移动。 http://filebin.ca/20ymCpNmtts7/NestedSplitTest.zip

据我了解,问题的原因是在拖动 splitView 的拆分器时,添加了一个新约束,将拖动拆分器左侧的 contentView 的右边缘绑定到 NSSplitView 本身。拖动完成后,此约束将被移除。当拖动正在进行并且拆分器受到其他面板的最小宽度的约束时,此临时约束的常量值的总和超过了窗格可以调整大小的宽度,这使得 NSSplitView 本身变大,这反过来调整了最外层分离器。

我曾尝试使用 NSSplitViewDelegate 来限制拆分位置,但是由于临时限制,此委托方法是在 NSSplitView 已经增长之后调用的。实施 constrainMinCoordinateconstrainMaxCoordinate 委托方法之一会使 NSSplitView 完全忽略面板上配置的最小宽度。

此外,当拖动开始或结束时,我似乎无法通过通知或子类化 NSSplitView 获得适当的事件。挂钩这两个事件将允许我添加一个临时约束以确保 NSSplitView 不能向外增长。即使覆盖 mouseDown:mouseUp: 也不起作用,因为 mouseUp: 在拖动结束后从未被调用过。

更新 1: 我找到了 mouseUp: 未被调用的原因的解释:这是因为分离器的拖动是使用在 NSEventTrackingRunLoopMode 中运行的嵌套 RunLoop 实现的。这就是 mouseDragged:mouseUp: 事件被静默吞没的方式。这是相关文档:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/EventOverview/HandlingMouseEvents/HandlingMouseEvents.html#//apple_ref/doc/uid/10000060i-CH6-SW4

了解为什么 mouseUp: 没有被调用是实现此问题解决方案的关键。这是一个自定义的 NSSplitView 子类,可以防止在拖动拆分器时 splitView 向外增长。

@interface NestableSplitView : NSSplitView

@property(strong) NSLayoutConstraint* temporaryWidthConstraint;

@end

@implementation NestableSplitView

- (void)mouseDown:(NSEvent *)theEvent
{
    if (!self.temporaryWidthConstraint) {
        self.temporaryWidthConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0];
    }
    self.temporaryWidthConstraint.constant = NSWidth(self.bounds);
    [self addConstraint:self.temporaryWidthConstraint];
    [super mouseDown:theEvent]; // This call is blocking until the drag is finished
    [self removeConstraint:self.temporaryWidthConstraint];
}

@end