因为我是 NSTextField 的子类,所以我相信正确的(或至少是有效的)答案是,如建议的那样,用以下内容覆盖,父滚动视图的滚动内容在becomeFirstResponder:
哪里myDocView
视图层次结构:
- (BOOL)becomeFirstResponder
{
BOOL done = [super becomeFirstResponder];
if (done) {
// Ensure new focus ring is shown also (shouldn't be hardwired, but for now ...)
NSSize margin = NSMakeSize(20.0, 20.0);
// Get where text field lives in myDocView's coordinates
NSRect r = [myDocView convertRect:[self bounds] fromView:self];
// Try scrolling if partially visible first, and if not ...
if (![myDocView adjustRectIntoView:r withMargin:margin]) {
// Text edit field is not visible in parent scroll view
// so tell document view where to scroll itself to
margin = NSMakeSize(-30.0, -30.0);
[myDocView specialScrollTo:self withOffset:margin];
}
}
return done;
}
myDocView
当以编程方式创建文本编辑对象时,必须设置属于文本编辑字段子类的属性字段,以便文本编辑字段在成为第一响应者时知道将滚动消息发送给谁。这是因为在我的特定情况下,文本编辑子类对象实际上比滚动文档视图低几个视图级别。
部分是出于一般用户界面的原因,部分是因为我的滚动内容视图布局的细节,有必要在三种情况下做一些不同的事情。第一种情况是成为第一响应者的文本编辑字段已经完全可见。这很简单,adjustRectIntoView
除了返回什么都不做YES
,因为用户可以看到对焦环的变化。
在第二种情况下,文本编辑字段是部分可见的。在这种情况下,该方法adjustRectIntoView:withMargin:
使其完全可见(如果可能,否则,它使原点区域可见)。但它只使用最小的滚动移动,水平或垂直(并且仅在必要时两者),将字段留在滚动视图的可见矩形的最近边缘旁边。这最不“吓到”用户。
最后,如果该字段完全不可见,那么在我的特殊情况下,我必须对与文本编辑视图相关的其他附近视图进行特殊分析,以便将其(或全部)显示给用户查看。
adjustRectIntoView:withMargin:
和都是添加到正在滚动specialScrollTo:withOffset:
的(子类)的方法。myDocView
我的后一个特殊例程太特定于应用程序,但前者非常通用,看起来像这样(并且可以轻松修改以完成后者):
- (BOOL)adjustRectIntoView:(NSRect)r withMargin:(NSSize)margin
{
CGRectInset(r, -margin.width, -margin.height);
NSRect vis = [myScroller documentVisibleRect];
if (CGRectContainsRect(vis, r)) {
// The enhanced rectangle `r` is already fully visible,
// so we're done (no change)
return(YES);
}
if (!CGRectIntersectsRect(vis, r)) {
// The enhanced rectangle `r` is fully invisible, so caller
// must apply whatever other custom strategy it needs to
// scroll `r` into view; or don't return and fall through.
return(NO);
}
// Rectangle `r` is partly visible in scroll view. So nudge the
// scrolling view enough to bring `r` into view near where it
// already is, with a minimum of motion. If `r` contains `vis`,
// which can happen if `r` is part of a highly magnified view,
// this gives preference to `r`'s origin becoming visible.
NSPoint ul = r.origin;
NSPoint ur = NSMakePoint(r.origin.x+r.size.width, r.origin.y);
NSPoint ll = NSMakePoint(r.origin.x, r.origin.y+r.size.height);
NSSize amt;
if (ul.x < vis.origin.x)
amt.width = (ul.x - vis.origin.x);
else if (ur.x > vis.origin.x+vis.size.width)
amt.width = (ur.x - (vis.origin.x+vis.size.width));
else
amt.width = 0.0;
if (ul.y < vis.origin.y)
amt.height = (ul.y - vis.origin.y);
else if (ll.y > vis.origin.y+vis.size.height)
amt.height = (ll.y - (vis.origin.y+vis.size.height));
else
amt.height = 0.0;
vis.origin.x += amt.width;
vis.origin.y += amt.height;
[[myScroller documentView] scrollPoint:vis.origin];
return(YES);
}