【发布时间】:2010-02-03 21:45:27
【问题描述】:
有人可以建议一种方法来在用户单击 NSTextField 时选择它的所有文本吗?
我确实找到了将NSTextField 子类化的建议,然后使用mouseDown 或firstResponder,但目前这超出了我的技能范围。所以我希望有一个更简单的解决方案,或者有人可能会详细说明所需的步骤。
【问题讨论】:
标签: cocoa select focus nstextfield
有人可以建议一种方法来在用户单击 NSTextField 时选择它的所有文本吗?
我确实找到了将NSTextField 子类化的建议,然后使用mouseDown 或firstResponder,但目前这超出了我的技能范围。所以我希望有一个更简单的解决方案,或者有人可能会详细说明所需的步骤。
【问题讨论】:
标签: cocoa select focus nstextfield
没有更简单的解决方案,您需要继承 NSTextField 来做您想做的事。如果您要在 Cocoa 中做任何有用的事情,您将需要学习如何处理子类化。
文本字段的子类化可能相对复杂,因为NSTextField 使用称为字段编辑器的单独NSTextView 对象来处理实际编辑。此文本视图由 NSTextField 的 NSWindow 对象返回,并重新用于页面上的所有文本字段。
像任何NSResponder 子类一样,NSTextField 响应方法-acceptsFirstResponder 和-becomeFirstResponder。当窗口想要将焦点赋予特定控件或视图时调用这些函数。如果您从这两种方法中返回YES,那么您的控件/视图将具有第一响应者状态,这意味着它是活动控件。但是,如前所述,NSTextField 实际上会在单击时为字段编辑器提供第一响应者状态,因此您需要在 NSTextField 子类中执行以下操作:
@implementation MCTextField
- (BOOL)becomeFirstResponder
{
BOOL result = [super becomeFirstResponder];
if(result)
[self performSelector:@selector(selectText:) withObject:self afterDelay:0];
return result;
}
@end
这首先调用-becomeFirstResponder 的超类实现,它将完成管理字段编辑器的艰苦工作。然后它调用-selectText: 选择字段中的所有文本,但它会在延迟 0 秒后执行此操作,这将延迟到下一次运行事件循环。这意味着将在字段编辑器完全配置后进行选择。
【讨论】:
@Rob 的答案可能在某一时刻有效,但正如@Daniel 指出的那样,它不再有效。看起来 Cocoa 想要跟踪鼠标并拖出一个选择以响应单击,而尝试选择文本以响应 becomeFirstResponder 并不能很好地配合它。
需要拦截鼠标事件,以防止跟踪。或多或少通过反复试验,我找到了一个似乎适用于 OS X 10.10 的解决方案:
@interface MyAutoselectTextField : NSTextField
@end
@implementation MyAutoselectTextField
- (void)mouseDown:(NSEvent *)theEvent
{
[[self currentEditor] selectAll:nil];
}
@end
据我所知,当mouseDown: 被调用时,字段编辑器已经设置好了,可能是becomeFirstResponder 的副作用。调用selectAll: 然后选择字段编辑器的内容。在self 上调用selectText: 反而效果不佳,大概是因为设置了字段编辑器。请注意,这里对mouseDown: 的覆盖不会不 调用super; super 会运行一个跟踪循环,拖出一个选择,我们不希望这种行为。请注意,一旦文本字段成为第一响应者,此 mouseDown: 覆盖不会影响选择,因为此时将调用字段编辑器的 mouseDown:。
我不知道这适用于哪些 OS X 版本;如果你在乎,你需要测试它。不幸的是,使用NSTextField 总是有点脆弱,因为字段编辑器的工作方式非常奇怪,并且非常依赖于super 中的实现细节。
【讨论】:
Swift 的一些更新:
import Cocoa
class TextFieldSubclass: NSTextField {
override func mouseDown(theEvent: NSEvent) {
super.mouseDown(theEvent)
if let textEditor = currentEditor() {
textEditor.selectAll(self)
}
}
}
或者为了精确选择:
import Cocoa
class TextFieldSubclass: NSTextField {
override func mouseDown(theEvent: NSEvent) {
super.mouseDown(theEvent)
if let textEditor = currentEditor() {
textEditor.selectedRange = NSMakeRange(location, length)
}
}
}
Swift 版本,适合我:
import Cocoa
class TextFieldSubclass: NSTextField {
override func becomeFirstResponder() -> Bool {
let source = CGEventSourceCreate(CGEventSourceStateID.HIDSystemState)
let tapLocation = CGEventTapLocation.CGHIDEventTap
let cmdA = CGEventCreateKeyboardEvent(source, 0x00, true)
CGEventSetFlags(cmdA, CGEventFlags.MaskCommand)
CGEventPost(tapLocation, cmdA)
return true
}
}
【讨论】:
我喜欢 Konstantin 的解决方案,但它会在每次按下鼠标时都进行选择。这是我用来在mouseDown 方法中选择的一个变体,但前提是它刚刚成为第一响应者:
class SelectingTextField: NSTextField {
var wantsSelectAll = false
override func becomeFirstResponder() -> Bool {
wantsSelectAll = true
return super.becomeFirstResponder()
}
override func mouseDown(with event: NSEvent) {
super.mouseDown(with: event)
if wantsSelectAll {
selectText(self)
wantsSelectAll = false
}
}
}
【讨论】:
我知道这是一个老问题,但这里有一个更新,供那些可能会遇到这个问题的人使用。
重写 NSTextField 并挂接到 becomeFirstResponder()。您不必担心管理委托或点击。棘手的部分是先找到焦点文本字段的字段编辑器,然后再要求它选择所有文本。
Objective-C
// in AutoselectOnFocusTextField.h
@interface AutoselectOnFocusTextField : NSTextField
@end
// in AutoselectOnFocusTextField.m
@implementation AutoselectOnFocusTextField
- (BOOL)becomeFirstResponder {
if (![super becomeFirstResponder]) {
return NO;
}
NSText* fieldEditor = [self currentEditor];
if (fieldEditor != nil) {
[fieldEditor performSelector:@selector(selectAll:) withObject:self afterDelay:0.0];
}
return YES;
}
@end
斯威夫特
class AutoselectOnFocusTextField: NSTextField {
override func becomeFirstResponder() -> Bool {
guard super.becomeFirstResponder() else {
return false
}
if let editor = self.currentEditor() {
editor.perform(#selector(selectAll(_:)), with: self, afterDelay: 0)
}
return true
}
}
【讨论】: