我有一个窗口,带有单个文本编辑字段,旁边是滚动视图。滚动视图的文档视图包含多个子视图,其中一些子视图包含以编程为nstextviews创建的文本编辑字段。
应用程序启动窗口时,以焦点显示了顶级文本编辑字段,因此(我认为(是第一响应者。然后,用户键入选项卡键,但是第一个文本编辑字段(其中包括(是文档视图的子视图,该文档视图已被用户视而不见。
应用程序的默认行为是将焦点移至"下一个"文本字段。除了用户不知道这在哪里,因为它是不可能的。
因此,该应用程序应有两种可能的方法。该应用程序应确定下一个响应者明显超出了界限,并防止Tab键更改当前焦点。或应用程序应确定哪个文本编辑字段已接收到新的焦点,并自动滚动,以便用户可以看到该文本字段。可以说,这两种情况都是合乎逻辑的,但我认为后者更有用。
如何确定焦点已自动更改为认为它是可见但不可见的香草文本编辑控件。
听起来您有几个问题。它们很容易解决,但这都不是自动的。
首先,按下选项卡键时,您循环的视图顺序是"键视图循环"。您可以对此进行阅读。有点自动,但您可以通过设置文本字段的 nextKeyView
和 previousKeyView
属性来表达明确的顺序(按钮,其他控件,...(。
如果您希望在滚动视图的文档视图中看到任何内容,则需要重新定位剪辑视图。有很多方法可以做到这一点(其中大多数很难理解(,但是您想要的是如此普遍,以至于NSView
中有一种便利的方法可以做到这一点:scrollRectToVisible:
因此,当您的文本字段变得活跃时,您要做的就是[textField scrollRectToVisible:textField.bounds]
。
每当文本字段开始编辑时,可以做到的一个地方,可以通过将代表附加到文本字段并捕获textDidBeginEditing:
或观察NSControlTextDidBeginEditingNotification
通知并确定它是您的文本字段之一来完成的。
因为我是在nstextfield的子类别,我相信正确的(或至少工作(的答案是,正如建议的那样,可以覆盖 becomeFirstResponder:
,沿着以下内容的方式覆盖 CC_8是在视图层次结构中更高的某个地方的父滚动视图的滚动内容:
- (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);
}