如何让 NSScrollView 在 10.9 及更高版本中居中文档视图?
Posted
技术标签:
【中文标题】如何让 NSScrollView 在 10.9 及更高版本中居中文档视图?【英文标题】:How do you get NSScrollView to center the document view in 10.9 and later? 【发布时间】:2014-03-31 02:15:43 【问题描述】:有很多例子说明如何让 NSScrollView 使其文档视图居中。 这里有两个例子 (它们非常相似,以至于有人在没有注明出处的情况下抄袭某人,但关键是如何存在。) http://www.bergdesign.com/developer/index_files/88a764e343ce7190c4372d1425b3b6a3-0.html https://github.com/devosoft/avida/blob/master/apps/viewer-macos/src/main/CenteringClipView.h
这通常通过继承 NSClipView 和覆盖来完成:
- (NSPoint)constrainScrollPoint:(NSPoint)newOrigin;
但此方法在 Mac OS X 10.9 + 中已弃用
我们现在能做什么? 哦不~!
【问题讨论】:
【参考方案1】:嗯,答案很简单,而且远没有那些过于臃肿。 无论如何,这些都不适用于双击放大。
确实如此。它只是工作。 您还可以根据需要自定义调整。
在@implementation中你只需要实现一个constrainBoundsRect:
的覆盖- (NSRect)constrainBoundsRect:(NSRect)proposedClipViewBoundsRect
NSRect constrainedClipViewBoundsRect = [super constrainBoundsRect:proposedClipViewBoundsRect];
// Early out if you want to use the default NSClipView behavior.
if (self.centersDocumentView == NO)
return constrainedClipViewBoundsRect;
NSRect documentViewFrameRect = [self.documentView frame];
// If proposed clip view bounds width is greater than document view frame width, center it horizontally.
if (proposedClipViewBoundsRect.size.width >= documentViewFrameRect.size.width)
// Adjust the proposed origin.x
constrainedClipViewBoundsRect.origin.x = centeredCoordinateUnitWithProposedContentViewBoundsDimensionAndDocumentViewFrameDimension(proposedClipViewBoundsRect.size.width, documentViewFrameRect.size.width);
// If proposed clip view bounds is hight is greater than document view frame height, center it vertically.
if (proposedClipViewBoundsRect.size.height >= documentViewFrameRect.size.height)
// Adjust the proposed origin.y
constrainedClipViewBoundsRect.origin.y = centeredCoordinateUnitWithProposedContentViewBoundsDimensionAndDocumentViewFrameDimension(proposedClipViewBoundsRect.size.height, documentViewFrameRect.size.height);
return constrainedClipViewBoundsRect;
CGFloat centeredCoordinateUnitWithProposedContentViewBoundsDimensionAndDocumentViewFrameDimension
(CGFloat proposedContentViewBoundsDimension,
CGFloat documentViewFrameDimension )
CGFloat result = floor( (proposedContentViewBoundsDimension - documentViewFrameDimension) / -2.0F );
return result;
在@interface 中只添加一个属性。这使您可以不使用居中。可以想象,有时您可能希望关闭居中的条件逻辑。
@property BOOL centersDocumentView;
另外,请务必在您的覆盖中将此BOOL
设置为YES
或NO
initWithFrame
和 initWithCoder:
这样您就可以使用一个已知的默认值。
(记住孩子们,initWithCoder:
允许你做必要的事情并在 nib 中设置视图的类。不要忘记在你的东西之前调用 super!)
当然,如果您需要支持早于 10.9 的任何内容,则需要实现其他内容。
(虽然可能没有其他人那么多......)
编辑: 正如其他人所指出的,Apple 在https://developer.apple.com/library/archive/samplecode/Exhibition/Listings/Exhibition_CenteringClipView_swift.html
处提供了 Swift 中的示例代码(尽管是从 2016 年开始,所以它可能不是当前的 Swift :D)【讨论】:
No visible @interface for 'NSScrollView' declares the selector 'constrainBoundsRect:'
编辑以简化,通过移动 constrainedClipViewBoundsRect = [super constrainBoundsRect:proposedClipViewBoundsRect];在条件守卫之前,因为在任一条件下都会调用 super。
这非常有用!我希望它包含“initWithCoder:允许您做必要的事情并在 nib 中设置视图的类”的代码,因为我正在努力弄清楚那部分。
是的...麻烦在于 initWithCoder:当您进行替换或替换时,它并不是一刀切。但是请参阅developer.apple.com/library/content/documentation/Cocoa/… 文档中的 Making Substitutions during Coding 部分
-2.0F 应该是 2.0F。在我的电脑上,额外的减号给出了错误的结果。【参考方案2】:
这里是 swift 的工人阶级
class CenteredClipView:NSClipView
override func constrainBoundsRect(proposedBounds: NSRect) -> NSRect
var rect = super.constrainBoundsRect(proposedBounds)
if let containerView = self.documentView as? NSView
if (rect.size.width > containerView.frame.size.width)
rect.origin.x = (containerView.frame.width - rect.width) / 2
if(rect.size.height > containerView.frame.size.height)
rect.origin.y = (containerView.frame.height - rect.height) / 2
return rect
【讨论】:
但是应该注意的是,您的 Swift 实现有点简单和不同,因为它不会使其成为可选的。 我应该澄清一下,我将它设置为一个选择加入标志,(避免混淆选项......)因为在某些情况下您可能需要默认行为。 我必须在这里忽略某些东西,因为它不起作用。我的文档是一个图表。 DocumentView 开始时非常小(一个节点),并且会扩展大小。视图似乎停留在左下角。我假设有一些自动布局设置可能有问题。有任何想法吗?我试图将前导和底部约束标记为“在运行时删除”这没有帮助 实际上,这个函数从来没有被调用过。我确实将 ClipView 的类型更改为此类。那么,这怎么可能呢? Apple 编写了一个与此类似的子类,您可以在此处找到:developer.apple.com/library/archive/samplecode/Exhibition/… 这对我来说效果更好。【参考方案3】:1) 确保剪辑视图正下方的视图(documentView)对剪辑视图没有任何约束! (如果是,请在构建时检查并删除)
2) 子类 NSClipView
@implementation CenterClipView
- (NSRect)constrainBoundsRect:(NSRect)proposedClipViewBoundsRect
NSRect rect = [super constrainBoundsRect:proposedClipViewBoundsRect];
NSView * view = self.documentView;
if (view)
if (rect.size.width > view.frame.size.width)
rect.origin.x = (rect.size.width - view.frame.size.width) / 2.;
if(rect.size.height > view.frame.size.height)
rect.origin.y = (rect.size.height - view.frame.size.height) / 2.;
return rect;
@end
3) 将 NSClipView 更改为您的子类
【讨论】:
它真的不应该有,因为你应该是这个的所有者,但是如果你想检查这个,也一定要检查SDK版本。约束不再被移除,而是被停用。 实际上,为documentView设置宽度和高度约束可能很重要,但要删除与clipView本身相关的约束 是的,您可以通过将文档视图实现为具有可设置或可计算值的类来做到这一点,该类具有从您的intrinsicContentSize
的实现返回的值,这是一种提供 h & w 约束和它们在 AppKit 中的常量。最灵活的方法是有一个文档视图类,它可以基于子视图的 h & w,即视图控制器提供的实际显示内容。设置 1 次然后很容易重复使用的时间很多。【参考方案4】:
uchuugaka 上面的答案非常有效,而且正如所指出的,它比旧的解决方案要简单得多。
上面的代码调用了函数centeredCoordinateUnitWithProposedContentViewBoundsDimensionAndDocumentViewFrameDimension()
,但没有提供它的源代码。
函数很简单,但为了完整起见,这里有一个实现:
CGFloat centeredCoordinateUnitWithProposedContentViewBoundsDimensionAndDocumentViewFrameDimension( CGFloat clipDimension, CGFloat docDimension)
CGFloat newOrigin;
newOrigin = roundf((docDimension - clipDimension) / 2.0);
return newOrigin;
【讨论】:
啊,不错。抱歉,为了完整起见,添加了我的实现。 虽然我使用 floor 并选择了 double 版本,因为 CGFloat 现在在 Mac 上肯定是 double。 否则基本一样。可能会讨论选择哪个函数进行舍入。以上是关于如何让 NSScrollView 在 10.9 及更高版本中居中文档视图?的主要内容,如果未能解决你的问题,请参考以下文章
如何在放大的 NSScrollView 中修复 NSTextView 的奇怪大小