Problems setting FirstResponder in Cocoa Mac OSX

2019-03-31 05:02发布

问题:

Im working on a tiny app just to learn cocoa, and Im having a hard time with setting FirstResponder to some NSTextFields.

When the view opens, I want the first NSTextField, clientNumber, to be selected, so I trigger [clientNumber becomeFirstResponder] at the end of my loadView method, but it does not select the text field. But when I click the button that fires the (IBAction)addToArray method, it selects the right text field. Further do I have another text field, it should contain integers only, so I have a crude verification for it. When the content is not a int, I get a beep, just like it should, but it does not select the text field.

Here is my code:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
      NSLog(@"initWithNibName");
    }
    return self;
}

- (void)viewWillLoad {
    NSLog(@"viewWillLoad");
}

- (void)viewDidLoad {
    NSLog(@"viewDidLoad");
    [self initNewInvoice];    
}

- (void)loadView {
    NSLog(@"loadView");
    [self viewWillLoad];
    [super loadView];
    [self viewDidLoad];
    FakturaAppDelegate *appDelegate = (FakturaAppDelegate *)[[NSApplication sharedApplication] delegate]; 
    [appDelegate.window makeFirstResponder:self.clientNumber]; 
}

- (void)awakeFromNib
{
    NSLog(@"awakeFromNib");
}

- (IBAction)saveInvoice:(id)sender
{
    FakturaAppDelegate *appDelegate = (FakturaAppDelegate *)[[NSApplication sharedApplication] delegate]; 
    [appDelegate.window makeFirstResponder:self.invoiceCredit]; 
}

- (IBAction)addToArray:(id)sender
{
    [clientNumber setStringValue: @"Toodeloo"];
    FakturaAppDelegate *appDelegate = (FakturaAppDelegate *)[[NSApplication sharedApplication] delegate]; 
    [appDelegate.window makeFirstResponder:self.clientNumber];  
}

- (IBAction)updateCreditDays:(id)sender
{
    if(invoiceCredit.intValue){
        [self updateCredit];
    }else{
        NSBeep();
        FakturaAppDelegate *appDelegate = (FakturaAppDelegate *)[[NSApplication sharedApplication] delegate]; 
    [appDelegate.window makeFirstResponder:self.invoiceCredit]; 
    }
}

I really hope someone can help me out here.

EDIT:

I got it all wrong, thanks for the pointers, but when I fix the code, using this: FakturaAppDelegate *appDelegate = (FakturaAppDelegate *)[[NSApplication sharedApplication] delegate]; [appDelegate.window makeFirstResponder:self.invoiceCredit]; instead of becomeFirstResponder. But it still doesnt work.

回答1:

You are making this much harder than it needs to be.

In Interface Builder, set the initialFirstResponder outlet of your window to point to your text field.

That's it.

If you absolutely must do it programmatically, use:

[window setInitialFirstResponder:yourTextField];

Remember, if you're fighting with Cocoa to do something that seems like it should be simple, then you're probably doing it wrong.



回答2:

Override viewDidAppear, and within said method, call the NSTextField's becomeFirstResponder method.

Note that in my own work, viewWillAppear has proven to be too early for said call, whether using the NSTextField's becomeFirstResponder method or the NSWindow's makeFirstResponder method.



回答3:

The trouble is that self.view.window is null in the first view controller's viewDidLoad method until after applicationDidFinishLaunching exits. So, becomeFirstResponder doesn't work in viewDidLoad. Try delaying the setting of the first responder, like so:

[_yourTextField performSelector:@selector(becomeFirstResponder) withObject:nil afterDelay:0.1];



回答4:

You should never try to set first responder in viewDidLoad method because window is still nil at that point. Use viewWillAppear instead:

- (void)viewWillAppear
{
    [super viewWillAppear];
    if (!_started) {
        _started = YES;
        [self.view.window makeFirstResponder:_yourView];
    }
}

Apple also once said that you should never call becomeFirstResponder directly. Use [yourWindow makeFirstResponder:yourView]; instead.



回答5:

Just read the documentation:

becomeFirstResponder

Notifies the receiver that it’s about to become first responder in its NSWindow.

[...]

Use the NSWindow makeFirstResponder: method, not this method, to make an object the first responder. Never invoke this method directly.