繁体   English   中英

如何使自定义视图中的NSTextField成为第一响应者?

[英]How do you make an NSTextField in a custom View be the first responder?

据我了解,将文本字段设为第一响应者意味着您可以在文本字段中输入文本,而不必先单击文本字段。

我制作了一个简单的应用程序来演示该问题。 这些是文件:

//
//  AppDelegate.m
//  MyFirstResponderApp
//

#import "AppDelegate.h"
#import "MyViewController.h"

@interface AppDelegate ()

@property (weak) IBOutlet NSWindow *window;
@property(strong) MyViewController* viewCtrl;

@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application

    MyViewController* myViewCtrl = [[MyViewController alloc]
                                   initWithNibName:@"MyView" bundle:nil];

    [self setViewCtrl:myViewCtrl];

    [[self window] setContentSize:[[myViewCtrl view] bounds].size];
    [[self window] setContentView:[myViewCtrl view]];



}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // Insert code here to tear down your application
}

@end

...

//
//  MyViewController.h
//  MyFirstResponderApp
//

#import <Cocoa/Cocoa.h>

@interface MyViewController : NSViewController

@property IBOutlet NSTextField* textField;

@end

...

//
//  MyViewController.m
//  MyFirstResponderApp
//

#import "MyViewController.h"

@interface MyViewController ()

@end

@implementation MyViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do view setup here.

    [[self textField] becomeFirstResponder];  //I tried here...
}

//And overriding initWithNibName:bundle: and setting it here:
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {

    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        [[self textField] becomeFirstResponder];
    }

    return self;
}

@end

MyView.xib的文件所有者是MyViewController,这是文件所有者的出口:

在此处输入图片说明

回答答案:

1)这对我有用:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application

    MyViewController* myViewCtrl = [[MyViewController alloc]
                                   initWithNibName:@"MyView" bundle:nil];

    [self setViewCtrl:myViewCtrl];

    [[self window] setContentSize:[[myViewCtrl view] bounds].size];
    [[self window] setContentView:[myViewCtrl view]];


    if ([[myViewCtrl textField] acceptsFirstResponder]) {
        [[[myViewCtrl textField] window] makeFirstResponder:[myViewCtrl textField]];

    }

}

3)是的,我正在尝试为窗口的初始显示做第一个响应者舞蹈,但这对我不起作用:

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application

    MyViewController* myViewCtrl = [[MyViewController alloc]
                                   initWithNibName:@"MyView" bundle:nil];

    [self setViewCtrl:myViewCtrl];

    [[self window] setContentSize:[[myViewCtrl view] bounds].size];
    [[self window] setContentView:[myViewCtrl view]];

    /*
    if ([myViewCtrl acceptsFirstResponder]) {
        [[[myViewCtrl textField] window] makeFirstResponder:[myViewCtrl textField]];

    }
    */

    [[self window] setInitialFirstResponder:[myViewCtrl textField]];



}

窗口显示正常,但是我必须单击文本字段才能输入文本。 如果我使用makeFirstResponder:则文本字段的运行方式如下:

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application

    MyViewController* myViewCtrl = [[MyViewController alloc]
                                   initWithNibName:@"MyView" bundle:nil];

    [self setViewCtrl:myViewCtrl];

    [[self window] setContentSize:[[myViewCtrl view] bounds].size];
    [[self window] setContentView:[myViewCtrl view]];

    /*
    if ([[myViewCtrl textField] acceptsFirstResponder]) {
        [[[myViewCtrl textField] window] makeFirstResponder:[myViewCtrl textField]];

    }
    */

    [[self window] makeFirstResponder:[myViewCtrl textField]];



}

我在Apple Developer的事件处理基础知识的“设置第一响应者”部分中找到了该建议。

对答案下评论的回应:

在我的应用程序中,如果我选择MainMenu.xib然后取消选中“ Visible at Launch ,则下面的代码将成功地在“查看第一响应者”中显示文本字段:

//
//  AppDelegate.m
//  MyFirstResponderApp
//

#import "AppDelegate.h"
#import "MyViewController.h"

@interface AppDelegate ()

@property(weak)IBOutlet NSWindow* window;
@property(strong) MyViewController* viewCtrl;

@end

@implementation AppDelegate



- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application

    MyViewController* myViewCtrl = [[MyViewController alloc]
                                    initWithNibName:@"MyView" bundle:nil];


    [self setViewCtrl:myViewCtrl];

    [[self window] setContentSize:[[myViewCtrl view] bounds].size];
    [[self window] setContentView:[myViewCtrl view]];

    [[self window] setInitialFirstResponder:[myViewCtrl textField] ];
    [self.window makeKeyAndOrderFront:self];

}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // Insert code here to tear down your application
}

@end

如果我创建一个WindowController(以及一个.xib,并删除MainMenu.xib中的窗口),那么对我来说组织这样的配置/初始化是最有意义的:

//
//  AppDelegate.m
//  MyFirstResponderApp
//

#import "AppDelegate.h"
#import "MyViewController.h"
#import "MyWindowController.h"

@interface AppDelegate ()

@property(strong) MyWindowController* windowCtrl;

@end

@implementation AppDelegate



- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application

    [self setWindowCtrl:[[MyWindowController alloc]
                         initWithWindowNibName:@"MyWindow"]];

}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // Insert code here to tear down your application
}

@end

然后通过覆盖WindowController中的initWithWindowNibName设置视图:

//
//  MyWindowController.h
//  MyFirstResponderApp
//

#import <Cocoa/Cocoa.h>

@interface MyWindowController : NSWindowController

-(instancetype)initWithWindowNibName:(NSString *)windowNibName;

@end

...

//
//  MyWindowController.m
//  MyFirstResponderApp
//


#import "MyWindowController.h"
#import "MyViewController.h"

@interface MyWindowController ()

@property(strong) MyViewController* viewCtrl;

@end

@implementation MyWindowController

-(id)initWithWindowNibName:(NSString *)windowNibName {

    if (self = [super initWithWindowNibName:windowNibName]) {
        MyViewController* myViewCtrl = [[MyViewController alloc]
                                        initWithNibName:@"MyView" bundle:nil];


        [self setViewCtrl:myViewCtrl];

        [[self window] setContentSize:[[myViewCtrl view] bounds].size];
        [[self window] setContentView:[myViewCtrl view]];

        [[self window] setInitialFirstResponder:[myViewCtrl textField] ];

        [self showWindow:self]; //I had to uncheck 'Visible at Launch' for the WindowController's window in the Attributes inspector


    }

    return self;
}
- (void)windowDidLoad {
    [super windowDidLoad];

    // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.


}

@end

...

//
//  MyViewController.h
//  MyFirstResponderApp
//

#import <Cocoa/Cocoa.h>

@interface MyViewController : NSViewController

@property(weak) IBOutlet NSTextField* textField;

@end

有两件事:

  1. 您永远-becomeFirstResponder自己调用-becomeFirstResponder (除非通过覆盖调用super)。 它说对文档

    切勿直接调用此方法。

    该方法是系统通知对象即将成为第一响应者并为其提供拒绝的机会。 它实际上并没有使对象成为第一响应者。

    使视图成为第一响应者的方法是,首先检查它是否将接受该状态,然后告诉窗口使其成为第一响应者:

     if ([self.textField acceptsFirstResponder]) [self.textField.window makeFirstResponder:self.textField]; 
  2. 您当前正在尝试在包含视图的视图控制器的初始化期间使文本字段成为第一响应者。 这还为时过早。 此时,视图不能在窗口中。 因此,它还不能成为窗口的第一响应者。 将视图添加到窗口后,应执行此操作。

    该作业理应属于窗口控制器,而不是视图控制器。 毕竟,只有窗口控制器具有必要的概述,才能决定其(可能有多个)视图中的哪个应该是第一响应者。 您不会在此简单应用程序中使用单独的窗口控制器,因此责任将落到AppDelegate类上。 (根据您发布的代码,它实际上是充当窗口的控制器。)

  3. 如果这是在显示窗口之前发生的,则应考虑设置窗口的initialFirstResponder而不是强制视图立即成为第一响应者。 首次显示时,该窗口会将其initialFirstResponder第一响应者。

    最好保留-makeFirstResponder:以便在显示窗口之后以编程方式更改第一个响应者。

在初始化过程中立即设置“第一响应者”可以忽略。 特别是对于窗口控制器以外的附加到View Controller的UI组件。 viewWillAppear()和viewDidAppear()之间可能会有延迟。

可以使用window的initialFirstResponder属性或makeFirstResponder()方法完成,例如...

window?.initialFirstResponder = yourVC.yourTextField
//OR
window?.makeFirstResponder(yourVC.yourTextField)

(对不起,我的大脑和手已经淘汰了Objective-C)

仅供参考,在某些地方可以通过多种方式访问​​window属性。 在您的自定义VC中,它将是:

self.yourTextField.window?
//OR
NSApplication.sharedApplication().mainWindow?

在您的自定义WindowController中:

self.window?
//or
NSApplication.sharedApplication().mainWindow?

在您的Appdelegate.swif中:

window? //if you use that default property in the template.

//if you have your custom Window Controller instance 'mainWindowController' :
self.mainWindowController.window?
//if you have a custom VC loaded to custom WC, well, :
self.mainWindowController.yourVC.window?
//OR just use 
NSApplication.sharedApplication().mainWindow?

但是就像我说的那样,如果您立即进行设置,可以在lu割期间将其忽略。 因此,请在延迟后进行设置:

window?.performSelector(#selector(window?.makeFirstResponder(_:)),
                                  withObject: .yourTextField,
                                  afterDelay:0.5)

根据放置位置的不同,“窗口? 和“ .yourTextField”部分应该不同。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM