[Cocoa]深入淺出Cocoa之Core Data(4)- 使用綁定
深入淺出 Cocoa 之 Core Data(4)- 使用綁定
羅朝輝 (http://www.rzrgm.cn/kesalin/)
前面講解了 Core Data 的框架,并完全手動編寫代碼演示了 Core Data 的運作過程。下面我們來演示如何結合 XCode 強大的可視化編輯以及 Cocoa 鍵值編碼,綁定機制來使用 Core Data。有了上面提到的哪些利器,在這個示例中,我們無需編寫 NSManagedObjectModel 代碼,也無需編寫 NSManagedObjectContext,工程模版在背后為我們做了這些事情。
今天要完成的這個示例,有兩個 Entity:StudentEntity 與 ClassEntity,各自有一個名為 name 的 Attribute。其中 StudentEntity 通過一個名為 inClass 的 relationship 與 ClassEntity關聯,而 ClassEntity 也有一個名為 students 的 relationship 與 StudentEntity 關聯,這是一個一對多的關系。此外 ClassEntity 還有一個名為 monitor 的 relationship 關聯到 StudentEntity,表示該班的班長。
代碼下載:點此下載
=========================================================================
7,創建 NSArrayController,關聯對象
現在回到 xib 中來,選中 StudentView.xib,設置StudentView 的 File's Owner 的類為 StudentViewController;使用 Control-Drag 將 File's Owner 的 view 指向 custom view。
向其中拖入兩個 NSArrayController:ClassPopup 和 Students。
設置 ClassPopup 的 Object Controller Mode 為 Entity Name,實體名為:ClassEntity,并勾選 Prepare Content。
設置 Students 的 Object Controller Mode 為 Entity Name,實體名為:StudentEntity,并勾選 Prepare Content。

上面的這些操作,ClassPopup ArrayController 管理 ClassEntity 的數據,Students ArrayController 管理 StudentEntity 的數據,后面我們就要將控件與這些 ArrayController 綁定起來。下面我們將這兩個 NSArrayController 的 ManagedObjectContext 參數與 ManagedViewController(File's Owner) 中的 managedObjectContext 綁定起來,這樣 NSDocuments 的 NSManagedObjectContext 就作用到的 ArrayController 中來了。下面只演示了 ClassPopup,請自行完成 Students 的綁定:

前面我們在 ManagedViewController 創建了一個 IBOutlet contentArrayController,現在是將它關聯的時候了,使用 Control-Drag 將 File's Owner 的 contentArrayController 關聯到 Students。
重復上面的過程,選中 ClassView.xib,將 File's Owner 的類為 ClassViewController,并將其 view 指向 custom view。
向其中拖入三個 NSArrayController:Classes,MonitorPopup 和 Students。
設置 Classes 的 Object Controller Mode 為 Entity Name,實體名為:ClassEntity,并勾選 Prepare Content。
將 Classes 的 ManagedObjectContext 參數與 ManagedViewController(File's Owner) 中的 managedObjectContext 綁定起來。
注意:這里沒有對 MonitorPopup 和 Students 進行修改。
使用 Control-Drag 將 File's Owner 的 contentArrayController 關聯到 Classes。
將 Students 和 MonitorPopup 的 Content set 綁定到 Classes 的 Model key path: students,表示這兩個 ArrayController 是管理對應 ClassEntity 的 students 的數據。
至此,模型, ArrayController 都準備好了,下面我們將控件綁定到這些對象上。上面已經夠繁瑣的了,下面我們得更加仔細,很容易出錯的。
選中 StudentView.xib,展開 Custom View 中的 TableView,直到我們看到名稱和班級兩個 Table Column。
選中名稱列,將其 value 綁定到 Students,model key path 為:name,表明第一列顯示學生的名稱;
選擇班級列,注意這一列是popup button cell,
將其 Content 綁定到 ClassPopup;
將其 ContentValues 綁定到 ClassPopup,model key path 為:name,表明第二列的選項為班級的名稱;
將其 Selected Object 綁定到 Students,model key path 為:inClass;表明將學生添加為選中班級的一員;
選中 + button,使用 Control+Drag將其托拽到 Students 上,選擇 add: 動作關聯;
選中 - button,使用 Control+Drag將其托拽到 Students 上,選擇 remove: 動作關聯;
選中 - button,將其 Eanbled 綁定到 Students, ctroller key 為:canRemove;
以上操作是將添加,刪除學生的操作直接與 Students ArrayController 綁定,無需編寫一點兒代碼!

選中 ClassView.xib
展開 Custom View 中的班級表,,直到我們看到班級 Table Column:選擇班級列,將其 value 綁定到 Classes,model key path 為:name,表明這一列顯示班級的名稱;
選中 Box,將其 Title 綁定到 Classed,model key path 為:name,并設置下方的 No Selection Placeholder 為:No Selection,Null Placeholder 為:Unnamed Class。 表明 box 顯示的信息為選中班級的信息,如果沒有選中任何班級,則顯示 No Selection。
展開 Box
選中 Pop up button
將其 Content 綁定到 MonitorPopup;
將其 ContentValues 綁定到 MonitorPopup,model key path 為:name,表明其選項為班級中的學生的名稱;
將其 Selected Object 綁定到 Classes,model key path 為:monitor;表明將選中的學生當作該班級的班長;
展開學生 tabel view,直到我們看到學生這個 Table Column。
選擇學生列,將其 Value 綁定到 Students,Model key path 為:name,表明學生列表顯示該班級中所有學生的名稱。
選中 + button,使用 Control+Drag 將其托拽到 Classes 上,選擇 add: 動作關聯;
選中 - button,使用 Control+Drag 將其托拽到 Classes 上,選擇 remove: 動作關聯;
選中 - button,將其 Eanbled 綁定到 Classes, ctroller key 為:canRemove;
以上操作是將添加,刪除班級的操作直接與 Classes ArrayController 綁定。

至此,綁定也大功告成,如果你的程序運行不正確,多半是這地方的關聯與綁定錯了,請回到這部分,仔細檢查每一項。
8,顯示,切換 view。
現在到了設置主界面的時候,修改 MyDocument.h 中的代碼如下:
#import <Cocoa/Cocoa.h>
@class ManagedViewController;
@interface MyDocument : NSPersistentDocument {
@private
NSBox * box;
NSPopUpButton * popup;
NSMutableArray *viewControllers;
NSInteger currentIndex;
}
@property (nonatomic, retain) IBOutlet NSBox * box;
@property (nonatomic, retain) IBOutlet NSPopUpButton * popup;
- (IBAction) changeViewController:(id)sender;
- (void) displayViewController:(ManagedViewController *)mvc;
@end
#import "MyDocument.h"
#import "ClassViewController.h"
#import "StudentViewController.h"
@implementation MyDocument
@synthesize popup;
@synthesize box;
- (id)init
{
self = [super init];
if (self) {
// create view controllers
//
viewControllers = [[NSMutableArray alloc] init];
ManagedViewController * mvc;
mvc = [[ClassViewController alloc] init];
[mvc setManagedObjectContext:[self managedObjectContext]];
[viewControllers addObject:mvc];
[mvc release];
mvc = [[StudentViewController alloc] init];
[mvc setManagedObjectContext:[self managedObjectContext]];
[viewControllers addObject:mvc];
[mvc release];
}
return self;
}
- (void) dealloc
{
self.box = nil;
self.popup = nil;
[viewControllers release];
[super dealloc];
}
- (NSString *)windowNibName
{
// Override returning the nib file name of the document
// If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
return @"MyDocument";
}
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
[super windowControllerDidLoadNib:aController];
// init popup
//
NSMenu *menu = [popup menu];
NSInteger itemCount = [viewControllers count];
for (NSInteger i = 0; i < itemCount; i++) {
NSViewController *vc = [viewControllers objectAtIndex:i];
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[vc title]
action:@selector(changeViewController:)
keyEquivalent:@""];
[item setTag:i];
[menu addItem:item];
[item release];
}
// display the first controller
//
currentIndex = 0;
[self displayViewController:[viewControllers objectAtIndex:currentIndex]];
[popup selectItemAtIndex:currentIndex];
}
#pragma mark -
#pragma mark Change Views
- (IBAction) changeViewController:(id)sender
{
NSInteger tag = [sender tag];
if (tag == currentIndex) {
return;
}
currentIndex = tag;
ManagedViewController *mvc = [viewControllers objectAtIndex:currentIndex];
[self displayViewController:mvc];
}
- (void) displayViewController:(ManagedViewController *)mvc
{
NSWindow *window = [box window];
BOOL ended = [window makeFirstResponder:window];
if (!ended) {
NSBeep();
return;
}
NSView *mvcView = [mvc view];
// Adjust window's size and position
//
NSSize currentSize = [[box contentView] frame].size;
NSSize newSize = [mvcView frame].size;
float deltaWidth = newSize.width - currentSize.width;
float deltaHeight = newSize.height - currentSize.height;
NSRect windowFrame = [window frame];
windowFrame.size.width += deltaWidth;
windowFrame.size.height += deltaHeight;
windowFrame.origin.y -= deltaHeight;
[box setContentView:nil];
[window setFrame:windowFrame display:YES animate:YES];
[box setContentView:mvcView];
// add viewController to the responder-chain
//
[mvcView setNextResponder:mvc];
[mvc setNextResponder:box];
}
@end

然后,使用 Control+Drag,將 File's Owner的 popup 和 popup button相聯,box 與 box相聯,并將 popup button 的 action 設置為 File's Owner 的 - (IBAction) changeViewController:(id)sender。
至此,所有的工作都完成了。編譯運行程序,如果不出意外的話,我們應該可以添加學生,班級,并設置學生的班級,班級的班長等信息了。
浙公網安備 33010602011771號