今天要完成的这个示例,有两个 Entity:StudentEntity 与 ClassEntity,各自有一个名为 name 的Attribute 其中 StudentEntity 通过一个名为 inClass 的 relationship 与 ClassEntity关联,而 ClassEntity 也有一个名为 students 的 relationship 与 Entity:StudentEntity 关联,这是一个一对多的关系。此外 ClassEntity 还有一个名为 monitor 的 relationship 关联到 StudentEntity,代表该班的班长。


代码下载:点击下载

最终的效果图如下:

下面我们一步一步来完成这个示例:
1,创建工程:
创建一个 Cocoa Application,工程名为:MacCoreData,并勾选 Create Document-Based Application 和 Use Core Data,在这里要用到 Core Data 和 Document 工程模版,以简化代码的编写。

2,分类文件:
在 MacCoreData 下新建 Src 和 Res 两个 Group,并将 MyDocument.h 和 MyDocument 拖到 Src 下,将其他 xib 和 xcdatamodeld 拖到 Res 中。将文件分类是个好习惯,尤其是对大项目来说。

3,创建 Entity:
在工程中,我们可以看到名为 MyDocument.xcdatamodeld 的文件,其后缀表明这是一个 core data model文件,框架就是读取该模型文件生成模型的。下面我们选中这个文件,向其中添加两个实体。点击下方的 Add Entity 增加两个新 Entity: ClassEntity 和 StudentEntity。

向 StudentEntity 中添加名为 name 的 string 类型的 Attribute,并设置其 Default Value 为学生甲,去除 Optional 前勾选状态;
向 ClassEntity 中添加名为 name 的 string 类型的 Attribute,并设置其 Default Value 为XX班,去除 Optional 前勾选状态;
选项 Optional 是表示该  Attribute 可选与否的,在这里 name 都是必须的。

向 StudentEntity 中添加名为 inClass 指向 ClassEntity 的 Relationship,其 Inverse 栏要等 ClassEntity 添加了反向关系才能选择,后面回提到;
向 ClassEntity 中添加名为 students 指向 StudentEntity 的 Relationship,其 Inverse 栏选择 inClass,表明这是一个双向关系,勾选 To-Many Relationship,因为一个班级可以有多名学生,这是一对多关系。设定之后,我们可以可以将 StudentEntity 的 inClass 关系的 Inverse 设置为 students了。
再向 ClassEntity 中添加名为 monitor 指向 StudentEntity 的 Relationship,表示该班的班长。

4,生成 NSManagedObject 类:
选中 StudentEntity,然后点击菜单 File-> New -> New file…,添加 Core Data -> NSManagerObject subclass, XCode 就会自动为我们生成 StudentEntity.h 和 StudentEntity.m 文件,记得将这两个文件拖放到 Src Group 下。下面我们来看看这两个文件中有什么内容:

StudentEntity.h

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@class ClassEntity;

@interface StudentEntity : NSManagedObject {
@private
}
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) ClassEntity * inClass;

@end

StudentEntity.m

#import "StudentEntity.h"
#import "ClassEntity.h"

@implementation StudentEntity
@dynamic name;
@dynamic inClass;

@end

在前面手动代码的示例中,我们是自己编写 Run NSManagedObject的代码,而现在,XCode 已经根据模型文件的描述,自动为我们生成了,方便吧。有时候自动生成的代码不一定满足我们的需要,我们就得对代码进行修改,比如对 ClassEntity 来说,班长只能是其 students 中的一员,如果我们在 students 中移除了班长那个学生,那么该班级的班长就应该置空。
选中 ClassEntity,重复上面的步骤,自动生成 ClassEntity.h 和 ClassEntity.m,下面我们根据需求来修改 ClassEntity.m。
在 - (void)removeStudentsObject:(StudentEntity *)value 的开头添加如下代码:

    if (value == [self monitor])
        [self setMonitor:nil];

在 - (void)removeStudents:(NSSet *)value 的开头添加如下代码:

    if ([value containsObject:[self monitor]])
        [self setMonitor:nil];

这样当我们在 students 中删除一个学生时,就会检测该学生是不是班长,如果是,就将该班的班长置空。

5,下面来生成 UI 界面:
在这里,我们是通过切换 view 的方法来显现学生与班级两个界面,因此我们需要主界面,班级以及学生共三个界面。向 MyDocument.xib 中添加如下一个 popup button 和一个 NSBox。并删除 popup 控件中的 menu item,因为我们要通过代码来添加班级,学生项的。

然后在 Res 中添加两个新 Empty xib 文件:StudentView.xib 和 ClassView.xib,分别向这两个 xib 文件中拖入一个 Custom View,然后在这个 view 添加相关控件构成 UI。记得设置 ClassView 中两个 tableView 的列数为 1,拖入一个 PopupButtonCell 到 StudentView 中班级那一列。效果如下:

6,添加 ViewController:
下面我们创建 ViewController 来在程序中转载 xib 文件,显示和切换 view。为了便于切换 view,我们创建一个继承自 NSViewController 的名为:ManagedViewController的类(记得不要创建该类对应的 xib 文件!创建一个 NSObject子类,然后修改其父类为 NSViewController),然后让 StudentViewController 和 ClassViewController 从它继承。

ManagedViewController 类的代码如下:

ManagedViewController.h

#import <Cocoa/Cocoa.h>

@interface ManagedViewController : NSViewController {
@private
    NSManagedObjectContext * managedObjectContext;
    NSArrayController * contentArrayController;
}

@property (nonatomic, retain) NSManagedObjectContext * managedObjectContext;
@property (nonatomic, retain) IBOutlet NSArrayController *contentArrayController;

@end


ManagedViewController.h
#import "ManagedViewController.h"

@implementation ManagedViewController

@synthesize managedObjectContext;
@synthesize contentArrayController;

- (void)dealloc
{
    self.contentArrayController = nil;
    self.managedObjectContext = nil;

[super dealloc];
}

// deal with "Delete" key event.
//
- (void) keyDown:(NSEvent *)theEvent
{
    if (contentArrayController) {
        if ([theEvent keyCode] == 51) {
            [contentArrayController remove:nil];
        }
        else {
            [super keyDown:theEvent];
        }
    }
    else {
        [super keyDown:theEvent];
    }
}

@end

在上面代码中,我们有一个 NSManagedObjectContext * managedObjectContext 指针,它指向 MyDocument 框架中的NSManagedObjectContext对象,后面我们会说到,至于 NSArrayController * contentArrayController,它是一个 IBOutlet,将与xib 中创建的 NSArrayController关联,后面也会说到。在这里引入 contentArrayController 是为了让 delete 能够删除记录。
ClassViewController 类的代码如下:

ClassViewController.h

#import "ManagedViewController.h"

@interface ClassViewController : ManagedViewController {
@private
}

@end

ClassViewController.m

#import "ClassViewController.h"

@implementation ClassViewController

- (id)init
{
    self = [super initWithNibName:@"ClassView" bundle:nil];
    if (self) {
        [self setTitle:@"班级"];
    }
    
    return self;
}

- (void)dealloc
{
    [super dealloc];
}

@end

StudentViewController 类的代码如下:

StudentViewController.h

#import "ManagedViewController.h"

@interface StudentViewController : ManagedViewController {
@private
}

@end

StudentViewController.m

#import "StudentViewController.h"

@implementation StudentViewController

- (id)init
{
    self = [super initWithNibName:@"StudentView" bundle:nil];
    if (self) {
        [self setTitle:@"学生"];
    }
    
    return self;
}

- (void)dealloc
{
    [super dealloc];
}

@end

在这两个子类中,我们在 init 方法中载入 xib 文件,然后设置其 title。

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


修改 MyDocument.m  中的代码如下:

#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


在 MyDocument 中,我们创建了两个 ManagedViewController,并将 managedObjectContext 传入其中。这两个ViewController分别代表班级与学生两个界面,然后通过 popup button 的选择在他们之间切换显示;在 displayViewController 中,我们还根据当前界面的大小来调整主界面的大小。这需要我们设置主界面中 box 的自动大小,打开 MyDocument.xib,作如下设置:


然后,使用 Control+Drag,将 File's Owner的 popup 和 popup button相联,box 与 box相联,并将 popup button 的 action 设置为 File's Owner 的 - (IBAction) changeViewController:(id)sender。

至此,所有的工作都完成了。编译运行程序,如果不出意外的话,我们应该可以添加学生,班级,并设置学生的班级,班级的班长等信息了。

转载于:https://blog.51cto.com/4682895/959996

深入浅出 Cocoa 之 Core Data(3)- 使用绑定相关推荐

  1. [Cocoa]深入浅出Cocoa之Core Data(4)- 使用绑定

    深入浅出 Cocoa 之 Core Data(4)- 使用绑定 罗朝辉 (http://www.cnblogs.com/kesalin/) 本文遵循"署名-非商业用途-保持一致"创 ...

  2. [Cocoa]深入浅出 Cocoa 之 Core Data(1)- 框架详解

    深入浅出 Cocoa 之 Core Data(1)- 框架详解 罗朝辉(http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 Core data 是 Cocoa 中处 ...

  3. [Cocoa]深入浅出 Cocoa 之 Core Data(2)- 手动编写代码

    深入浅出 Cocoa 之 Core Data(2)- 代码示例 罗朝辉(http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 前面 详细讲解了 Core Data 的 ...

  4. [Cocoa]深入浅出Cocoa之Core Data(2)- 手动编写代码

    深入浅出Cocoa之Core Data(2)- 手动编写代码 罗朝辉 (http://www.cnblogs.com/kesalin/) 本文遵循"署名-非商业用途-保持一致"创作 ...

  5. Core Data(4)- 使用绑定

    深入浅出 Cocoa 之 Core Data(4)- 使用绑定 罗朝辉(http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 前面讲解了 Core Data 的框架, ...

  6. Core Data(3)- 使用绑定

    深入浅出 Cocoa 之 Core Data(3)- 使用绑定 罗朝辉(http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 前面讲解了 Core Data 的框架, ...

  7. [Cocoa]深入浅出Cocoa系列

    深入浅出Cocoa系列 罗朝辉 (http://www.cnblogs.com/kesalin/) 本文遵循"署名-非商业用途-保持一致"创作公用协议 这是本人在研究 Cocoa  ...

  8. Core Data 编程指南

    一.技术概览 1. Core Data 功能初窥 对于处理诸如对象生命周期管理.对象图管理等日常任务,Core Data框架提供了广泛且自动化的解决方案.它有以下特性. (注:对象图-Object g ...

  9. [深入浅出Cocoa]iOS网络编程之Socket

    一,iOS网络编程层次模型 在前文<深入浅出Cocoa之Bonjour网络编程>中我介绍了如何在Mac系统下进行 Bonjour 编程,在那篇文章中也介绍过 Cocoa 中网络编程层次结构 ...

  10. [Cocoa]深入浅出Cocoa之Bonjour网络编程

    深入浅出Cocoa之Bonjour网络编程 罗朝辉 (http://www.cnblogs.com/kesalin/) 本文遵循"署名-非商业用途-保持一致"创作公用协议 本文高度 ...

最新文章

  1. 线性表的链式表示——双链表
  2. linux 命令行简介
  3. xheditor的使用方法
  4. VC网络通信API概览
  5. 数数题(计数类 DP)做题记录
  6. 机器学习作业班_python神经网络搭建
  7. 简明python教程:数据结构
  8. 发布Oracle存储过程包c#代码生成工具(CodeRobot)
  9. 腾讯会议共享屏幕 共享PPT视频声音
  10. Word删除表格后空白页的方法
  11. 用gin+xorm+docker编写Online Judge后端
  12. ARM架构(RISC)和x86架构(CISC)以及传统与移动CPU/GPU厂商
  13. 电场强度 高斯定理 习题
  14. 运维 技术支持 人在香港 硬件维护 经验
  15. 2.HTML元素、属性、标题
  16. 职业科普丨PMP是什么呢?
  17. C# VBA 提取word中图片方法
  18. libev中ev_loop结构体中宏定义的理解
  19. NTT笔记和多项式全家桶
  20. 笔记--js开发规范

热门文章

  1. UML?类图中聚合和组合的区别
  2. MacOS 迅速上手 Makefile 编译 C / C++ 工程
  3. 485.最大连续1的个数(力扣leetcode) 博主可答疑该问题
  4. android多地图切换,Android的谷歌地图卫星切换
  5. wince 德赛187a__艾默生CT M8x0-03200066A
  6. HTML5 语义元素
  7. Could not get unknown property 'packageForR' for task ':app:processDebugReso
  8. HTTP协议详解(经典)
  9. win8内置管理员用户无法激活此应用
  10. 30多个最有用的Web开发框架