如何检测NSTextField何时具有焦点或是否已选择内容cocoa

时间:2014-09-05 18:47:20

标签: macos cocoa focus nstextfield first-responder

我在NSTableCellView中有一个NSTextField,我想要一个事件告诉我当我的NSTextField有关于禁用几个按钮的焦点时,我发现了这个方法:

-(void)controlTextDidBeginEditing:(NSNotification *)obj{
    NSTextField *textField  = (NSTextField *)[obj object];

    if (textField != _nombreDelPaqueteTextField) {
        [_nuevaCuentaActivoButton   setEnabled:FALSE];
        [_nuevaCuentaPasivoButton   setEnabled:FALSE];
        [_nuevaCuentaIngresosButton setEnabled:FALSE];
        [_nuevaCuentaEgresosButton  setEnabled:FALSE];
    }
}

但它会在我的文本字段开始编辑时触发,因为这样说,我希望在我关注textField时禁用按钮,而不是在我已经开始编辑时


编辑:根据Joshua Nozzi的帮助我的代码,它仍然无法正常工作

MyNSTextField.h

#import <Cocoa/Cocoa.h>
@class MyNSTextField;

@protocol MyNSTextFieldDelegate

@optional -(BOOL)textFieldDidResignFirstResponder:(NSTextField *)sender;
@optional -(BOOL)textFieldDidBecomeFirstResponder:(NSTextField *)sender;

@end

@interface MyNSTextField : NSTextField

@property (strong, nonatomic)           id <MyNSTextFieldDelegate> cellView;

@end

MyNSTextField.m

#import "MyNSTextField.h"

@implementation MyNSTextField

- (BOOL)becomeFirstResponder
{
    BOOL status = [super becomeFirstResponder];
    if (status)

        [self.cellView textFieldDidBecomeFirstResponder:self];
    return status;
}

- (BOOL)resignFirstResponder
{
    BOOL status = [super resignFirstResponder];
    if (status)
        [self.cellView textFieldDidResignFirstResponder:self];
    return status;
}

@end

在我的viewcontroller EdicionDeCuentasWC.m

#import "MyNSTextField.h"


@interface EdicionDeCuentasWC ()<NSTableViewDataSource, NSTableViewDelegate, NSControlTextEditingDelegate, NSPopoverDelegate, MyNSTextFieldDelegate>
@end


@implementation EdicionDeCuentasWC
#pragma mark MyNSTextFieldDelegate
-(BOOL)textFieldDidBecomeFirstResponder:(NSTextField *)sender{
    NSLog(@"textFieldDidBecomeFirstResponder");
    return TRUE;
}

-(BOOL)textFieldDidResignFirstResponder:(NSTextField *)sender{
    NSLog(@"textFieldDidResignFirstResponder");
    return TRUE;
}
#pragma mark --
@end

在可视化编辑器中说,重要的是,我已将所有NSTextFields更改为MyNSTextField类,并将委托设置为我的文件所有者(EdicionDeCuentasWC)

4 个答案:

答案 0 :(得分:3)

我有一个自定义NSTextField子类,它会覆盖-becomeFirstResponder-resignFirstResponder。其-cellView属性要求符合声明-textDidBecome/ResignFirstResponder:(NSTextField *)sender的协议,但它足以为您提供一般概念。可以轻松修改它以发布您的控制器可以注册为观察者的通知。我希望这会有所帮助。

- (BOOL)becomeFirstResponder
{
    BOOL status = [super becomeFirstResponder];
    if (status)
        [self.cellView textFieldDidBecomeFirstResponder:self];
    return status;
}

- (BOOL)resignFirstResponder
{
    BOOL status = [super resignFirstResponder];
    if (status)
        [self.cellView textFieldDidResignFirstResponder:self];
    return status;
}

答案 1 :(得分:3)

我想我已经钉了它。我尝试将NSTextFiled子类化为覆盖becomeFirstResponder()resignFirstResponder(),但是一旦我点击它,becomeFirstResponder()就会被调用,之后会调用resignFirstResponder()。咦?但搜索字段仍然处于编辑状态,焦点仍在其中。

我发现,当您点击搜索字段时,搜索字段会成为第一响应者一次,但稍后会在某个时间准备NSText,焦点将移至NSText。< / p>

我发现在准备NSText时,它被设置为self.currentEditor()。问题是,当becomeFirstResponder()来电时,self.currentEditor()尚未设定。所以becomeFirstResponder()不是检测它焦点的方法。

另一方面,当焦点移至NSText时,会调用文本字段resignFirstResponder(),您知道吗? self.currentEditor()已设定。所以,现在是时候告诉它的代表那个文本字段了。

接下来,如何检测搜索字段何时丢失它的焦点。再次,它是关于NSText。然后你需要听NSText委托的方法,如textDidEndEditing(),并确保你让它的超类来处理方法,看看self.currentEditor()是否无效。如果是这种情况,NSText会失去它的焦点并告诉文本字段的代表。

我提供了一个代码,实际上是NSSearchField子类来做同样的事情。同样的原则也适用于NSTextField

protocol ZSearchFieldDelegate: NSTextFieldDelegate {
    func searchFieldDidBecomeFirstResponder(textField: ZSearchField)
    func searchFieldDidResignFirstResponder(textField: ZSearchField)
}


class ZSearchField: NSSearchField, NSTextDelegate {

    var expectingCurrentEditor: Bool = false

    // When you clicked on serach field, it will get becomeFirstResponder(),
    // and preparing NSText and focus will be taken by the NSText.
    // Problem is that self.currentEditor() hasn't been ready yet here.
    // So we have to wait resignFirstResponder() to get call and make sure
    // self.currentEditor() is ready.

    override func becomeFirstResponder() -> Bool {
        let status = super.becomeFirstResponder()
        if let _ = self.delegate as? ZSearchFieldDelegate where status == true {
            expectingCurrentEditor = true
        }
        return status
    }

    // It is pretty strange to detect search field get focused in resignFirstResponder()
    // method.  But otherwise, it is hard to tell if self.currentEditor() is available.
    // Once self.currentEditor() is there, that means the focus is moved from 
    // serach feild to NSText. So, tell it's delegate that the search field got focused.

    override func resignFirstResponder() -> Bool {
        let status = super.resignFirstResponder()
        if let delegate = self.delegate as? ZSearchFieldDelegate where status == true {
            if let _ = self.currentEditor() where expectingCurrentEditor {
                delegate.searchFieldDidBecomeFirstResponder(self)
                // currentEditor.delegate = self
            }
        }
        self.expectingCurrentEditor = false
        return status
    }

    // This method detect whether NSText lost it's focus or not.  Make sure
    // self.currentEditor() is nil, then that means the search field lost its focus,
    // and tell it's delegate that the search field lost its focus.

    override func textDidEndEditing(notification: NSNotification) {
        super.textDidEndEditing(notification)

        if let delegate = self.delegate as? ZSearchFieldDelegate {
            if self.currentEditor() == nil {
                delegate.searchFieldDidResignFirstResponder(self)
            }
        }
    }

}

您需要将NSSerachField更改为ZSearchField,并且您的客户端类必须符合ZSearchFieldDelegate而不是NSTextFieldDelegate。这是一个例子。当用户单击搜索字段时,它会扩展它的宽度,当您单击其他位置时,搜索字段会丢失它的焦点并缩小其宽度,方法是更改​​Interface Builder设置的NSLayoutConstraint值。

class MyViewController: NSViewController, ZSearchFieldDelegate {

    // [snip]

    @IBOutlet weak var searchFieldWidthConstraint: NSLayoutConstraint!

    func searchFieldDidBecomeFirstResponder(textField: ZSearchField) {
        self.searchFieldWidthConstraint.constant = 300
        self.view.layoutSubtreeIfNeeded()
    }

    func searchFieldDidResignFirstResponder(textField: ZSearchField) {
        self.searchFieldWidthConstraint.constant = 100
        self.view.layoutSubtreeIfNeeded()
    }

}

这可能取决于操作系统的行为,我尝试了El Capitan 10.11.4,并且它有效。

代码也可以从Gist复制。 https://gist.github.com/codelynx/aa7a41f5fd8069a3cfa2

答案 2 :(得分:1)

以防万一,与@sam的思想略有不同的是,我们可以观察到NSWindow.firstResponder属性本身,根据文档它是KVO兼容的。然后将其与textFieldtextField.currentEditor()进行比较,以确定该字段是否聚焦。

答案 3 :(得分:0)

我在macrumors forums上找到了以下代码。

  1. 第一个响应者是文本视图(字段编辑器是文本视图)。
  2. 字段编辑器是否存在?
  3. 文本字段是字段编辑器的委托
  4. 似乎有效。

    - (BOOL)isTextFieldInFocus:(NSTextField *)textField
    {
        BOOL inFocus = NO;
    
        inFocus = ([[[textField window] firstResponder] isKindOfClass:[NSTextView class]]
                   && [[textField window] fieldEditor:NO forObject:nil]!=nil
                   && [textField isEqualTo:(id)[(NSTextView *)[[textField window] firstResponder]delegate]]);
    
        return inFocus;
    }