我按照advice here关于如何为我项目的单一窗口设置MainWindowController: NSWindowController
。我使用Cocoa类创建.h / .m文件,然后选中了Also create .xib for User Interface
选项。结果,Xcode自动将一个窗口(我将其重命名为MainWindow.xib)连接到我的MainWidowController。
接下来,我删除了默认MainMenu.xib文件中的窗口(在Interface Builder中我选择了窗口图标,然后我点击了删除键)。之后,我能够成功构建我的项目,MainWindow.xib
中的控制器窗口正确显示,上面有几个按钮。
然后我尝试将NSTableView添加到我的MainWindowController窗口。在Xcode中,我将NSTableView的必需delegate
和datasource
出口拖到File's Owner
,这是我的MainWindowController,我在MainWindowController.m中实现了我认为可以创建NSTableView的方法显示我的数据:
- tableView:viewForTableColumn:row:
- numberOfRowsInTableView:
现在,当我构建项目时,我没有收到任何错误,但数据并没有出现在NSTableView中。
我的代码如下。欢迎任何提示!
//
// AppDelegate.h
// TableViews1
//
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
@end
...
//
// AppDelegate.m
// TableViews1
//
@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@property (strong) MainWindowController* mainWindowCtrl;
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[self setMainWindowCtrl:[[MainWindowController alloc] init] ];
[[self mainWindowCtrl] showWindow:nil];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
@end
...
//
// MainWindowController.h
// TableViews1
//
#import <Cocoa/Cocoa.h>
@interface MainWindowController : NSWindowController
@end
...
//
// MainWindowController.m
// TableViews1
//
#import "MainWindowController.h"
#import "Employee.h"
@interface MainWindowController () <NSTableViewDataSource, NSTableViewDelegate>
@property (strong) NSMutableArray* employees;
@property (weak) IBOutlet NSTableView* tableView;
@end
@implementation MainWindowController
- (NSView*)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row {
Employee* empl = [[self employees] objectAtIndex:row];
NSString* columnIdentifier = [tableColumn identifier];
//The column identifiers are "firstName" and "lastName", which match my property names.
//You set a column's identifier by repeatedly clicking on the TableView until only
//one of the columns is highlighted, then select the Identity Inspector and change the column's 'Identifier' field.
NSString* emplInfo = [empl valueForKey:columnIdentifier]; //Taking advantage of Key-Value coding
NSTableCellView *cellView =
[tableView makeViewWithIdentifier:columnIdentifier
owner:self];
NSLog(@"The Table view is asking for employee: %@", [empl firstName]);
[[cellView textField] setStringValue:emplInfo];
return cellView;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [[self employees] count];
}
- (void)windowDidLoad {
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
Employee* e1 = [[Employee alloc] initWithFirstName:@"Joe" lastName:@"Blow"];
Employee* e2 = [[Employee alloc] initWithFirstName:@"Jane" lastName:@"Doe"];
[self setEmployees:[NSMutableArray arrayWithObjects:e1, e2, nil]];
//Test to see if the employees array was populated correctly:
Employee* e = [[self employees] objectAtIndex:0];
NSLog(@"Here is the first employee: %@", [e firstName]);
//I see the output: "Here is the first employee: Joe"
}
- (id)init {
return [super initWithWindowNibName:@"MainWindow"];
}
- (id)initWithWindowNibName:(NSString *)windowNibName {
NSLog(@"Clients cannot call -[%@ initWithWindowNibName] directly!",
[self class]
);
[self doesNotRecognizeSelector:_cmd];
return nil;
}
@end
...
//
// Employees.h
// TableViews1
#import <Foundation/Foundation.h>
@interface Employee : NSObject
@property NSString* firstName;
@property NSString* lastName;
- initWithFirstName:(NSString*)first lastName:(NSString*)last;
@end
...
//
// Employees.m
// TableViews1
//
#import "Employee.h"
@implementation Employee
- (id)initWithFirstName:(NSString *)first lastName:(NSString *)last {
if (self = [super init]) {
_firstName = first; //I read that you shouldn't use the accessors in init methods.
_lastName = last;
}
return self;
}
@end
文件所有者(= MainWindowController)连接:
NSTableView连接:
对评论的回应:
以下为什么在[self tableView] reloadData]
末尾调用-windowDidLoad
,如评论中所述,不起作用:
我的_tableView
实例变量 - 由我在MainWindowController.m中的@property声明创建 - 并没有指向任何东西;因此呼吁:
[[self tableView] reloadData]
我认为相当于调用:
[nil reloadData]
没有做任何事情。
我从未在_tableView
方法中为-init
实例变量分配任何内容,也没有通过在Interface Builder中的某处拖动插座来为其分配值。为了解决这个问题,我选择了Project Navigator(左侧窗格)中的MainWindow.xib(控制器窗口),然后在中间窗格(Interface Builder)中,我选择了代表文件的多维数据集。所有者(在右侧窗格中选择Identity Inspector显示File的所有者是MainWindowController)。然后在右窗格中,我选择了Connections Inspector,它显示了一个名为tableView
的出口,它是我在MainWindowController.m中声明的IBOutlet变量。
接下来,我将tableView outlet
拖到中间窗格中的TableView上:
这样做会将NSTableView对象分配给由MyWindowControler.m中的@property声明创建的_tableView实例变量:
@property (weak) IBOutlet NSTableView* tableView;
作为实验,我断开了插座,然后注释掉了tableview的@property声明,并且在Connections Inspector中不再出现tableView插座。另外,如果我改变声明:
@property (weak) IBOutlet NSTableView* tableView;
为:
@property (weak) NSTableView* tableView;
...然后tableView出口不会出现在Connections Inspector中。那个实验回答了我关于是否应该将属性声明为IBOutlet的几个问题:如果你需要将Interface Builder中的一个对象分配给你的一个变量,那么将变量声明为IBOutlet。
此后,在[self tableView] reloadData]
结束时调用-windowDidLoad
成功填充TableView。但是,我没有看到任何调用reloadData的教程,甚至Apple's guide都没有这样做。
所以,我仍然对于调用-reloadData
是否是黑客还是正确的做事方式感到困惑。
如果没有它,你的桌面视图就会对你无比无聊 期望它甚至不用向数据源索取数据。
我假设NSTableView在准备好自己显示时自动查询其数据源,并且我的代码需要能够在那时提供数据。
答案 0 :(得分:1)
我没有看到你在任何地方向你的桌面视图发送-reloadData
。将它粘贴到-windowDidLoad
的末尾将是一个好地方。如果没有它,你的表视图就会对你的期望无能为力,因为它甚至不愿意向数据源询问数据。
据他所知,数据根本没有准备/可用,为什么要尝试呢?更重要的是,当 应该 时,它会尝试吗?考虑到UI可能没有完成加载/连接到出口,或者它的数据源可能处于易受攻击的状态(如dealloc期间/之后的拆除)并且可能导致发送数据源请求,因此在任何时候都可以尝试它是相当粗鲁的。在崩溃等等。
答案 1 :(得分:-1)
两件事:
1,在windowDidLoad中设置employees数组时设置一些断点,而当表首次尝试填充自身并调用numberOfRowsInTableView实现时。如果后者发生在前者之前,那么在创建数组后需要添加reloadData。
第二,我个人总是在我的表中使用NSCell而不是NSViews,所以我总是在表的数据源中实现objectValueForTableColumn。因此,当您使用NSView对象并实现viewForTableColumn时,我不确定您是否需要做一些不同的事情。你有没有使用NSCell的原因?