Cowboy Tech

iOS设计模式-组合

树形结构

  1. 层次嵌套
  2. 外层和内层具有相似的结构
  3. 可以递归的表示

简易的二叉树

Node

@interface Node : NSObject

@property (nonatomic, strong) NSString *nodeName;
@property (nonatomic, strong) Node *leftNode;
@property (nonatomic, strong) Node *rightNode;
+ (instancetype)nodeWithName:(NSString *)nodeName;

@implementation Node
+ (instancetype)nodeWithName:(NSString *)nodeName {

//这里用self,因为它可能会被子类继承
Node *node    = [[[self class] alloc] init];
node.nodeName = nodeName;

return node;
}
@end

ViewController

@interface ViewController ()

@property (nonatomic, strong) Node *rootNode;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

self.rootNode = [Node nodeWithName:@"A"];

// 插入节点
[self insertNodeTree:self.rootNode node:[Node nodeWithName:@"B"]];
[self insertNodeTree:self.rootNode node:[Node nodeWithName:@"C"]];
[self insertNodeTree:self.rootNode node:[Node nodeWithName:@"D"]];
[self insertNodeTree:self.rootNode node:[Node nodeWithName:@"E"]];
[self insertNodeTree:self.rootNode node:[Node nodeWithName:@"F"]];

// 遍历二叉树
[self treeInfomationWithNode:self.rootNode];
}


//往根节点上插入节点

- (void)insertNodeTree:(Node *)tree node:(Node *)node {

if (tree.leftNode == nil) {
    tree.leftNode = node;
    return;
}

if (tree.rightNode == nil) {
    tree.rightNode = node;
    return;
}

[self insertNodeTree:tree.leftNode node:node];
}

// 遍历二叉树
- (void)treeInfomationWithNode:(Node *)node {

if (node.leftNode) {
    [self treeInfomationWithNode:node.leftNode];
}

NSLog(@"%@", node.nodeName);

if (node.rightNode) {
    [self treeInfomationWithNode:node.rightNode];
}
@end

组合模式

  1. 组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
  2. 掌握组合模式的重点是要理解清楚 “部分/整体” 还有 ”单个对象“ 与 “组合对象” 的含义。

Node

@interface Node : NSObject

@property (nonatomic, strong) NSString *nodeName;

//便利构造器
+ (instancetype)nodeWithNodeName:(NSString *)nodeName;

//子节点集合
@property (nonatomic, strong, readonly) NSMutableArray <Node *>  *childNodes;

//添加子节点
- (void)addNode:(Node *)node;

//删除子节点
- (void)removeNode:(Node *)node;

//获取子节点
- (Node *)nodeAtIndex:(NSInteger)index;

//打印Node
- (void)operation;
@end

//------------------------------------------------------------------------
@interface Node ()
@property (nonatomic, strong) NSMutableArray <Node *>  *childNodes;
@end

@implementation Node

- (instancetype)init {
self = [super init];
if (self) {  
    self.childNodes = [NSMutableArray array];
}
return self;
}

+ (instancetype)nodeWithNodeName:(NSString *)nodeName {
Node *node    = [[[self class] alloc] init];
node.nodeName = nodeName;
return node;
}

- (void)addNode:(Node *)node {
[self.childNodes addObject:node];
}

- (void)removeNode:(Node *)node {
[self.childNodes removeObject:node];
}

- (Node *)nodeAtIndex:(NSInteger)index {
if (index >= self.childNodes.count) {     
    return nil;  
} else {
    return self.childNodes[index];
}
}

- (void)operation {
NSLog(@"nodeName --> %@", self.nodeName);
}

- (NSString *)description {
return [NSString stringWithFormat:@"[Node] - %@", self.nodeName];
}
@end

ViewController

@interface ViewController ()
@property (nonatomic, strong) Node  *rootNode;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

// 创建根节点
self.rootNode = [Node nodeWithNodeName:@"A"];

// 创建第一级子节点(A -> B,C,D)
[self.rootNode addNode:[Node nodeWithNodeName:@"B"]];
Node *c = [Node nodeWithNodeName:@"C"];
[self.rootNode addNode:c];
[self.rootNode addNode:[Node nodeWithNodeName:@"D"]];

// 创建第二级子节点(C -> E,F)
[c addNode:[Node nodeWithNodeName:@"E"]];
[c addNode:[Node nodeWithNodeName:@"F"]];

NSLog(@"%@", self.rootNode.childNodes);
NSLog(@"%@", c.childNodes);
}

编写文件夹系统

File

typedef enum : NSUInteger {
kFile,   // 文件
kFolder, // 文件夹   
} EFile;

@interface File : NSObject

//文件夹或者文件的名字,根据EFile类别来区分
@property (nonatomic, strong) NSString *name;

//文件类型
@property (nonatomic) EFile             fileType;

//子文件集合
@property (nonatomic, strong, readonly) NSMutableArray <File *>  *childFiles;

//添加文件
- (void)addFile:(File *)file;

//便利构造器
+ (instancetype)fileWithFileType:(EFile)fileType fileName:(NSString *)name;
@end

-------------------------------------------------------------

@interface File ()
@property (nonatomic, strong) NSMutableArray <File *>  *childFiles;
@end

@implementation File

- (instancetype)init {
self = [super init];
if (self) {
    self.childFiles = [NSMutableArray array];
}
return self;
}

- (void)addFile:(File *)file {
NSParameterAssert(file);
[self.childFiles addObject:file];
}

+ (instancetype)fileWithFileType:(EFile)fileType fileName:(NSString *)name {
File *file    = [[[self class] alloc] init];
file.fileType = fileType;
file.name     = name;
return file;
}

@end

FileCell

@interface FileCell : UITableViewCell

@property (nonatomic, weak) id                data;
@property (nonatomic, weak) NSIndexPath      *indexPath;
@property (nonatomic, weak) UITableView      *tableView;
@property (nonatomic, weak) UIViewController *controller;

- (void)loadContent;

@end
---------------------------------------------------------------------
@interface FileCell ()

@property (nonatomic, strong) UIImageView  *fileImageView;
@property (nonatomic, strong) UIImageView  *folderImageView;
@property (nonatomic, strong) UILabel      *iconNameLabel;
@property (nonatomic, strong) UIButton     *button;
@property (nonatomic, strong) UILabel      *nameLabel;

@end

@implementation FileCell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {

if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {

    self.selectionStyle = UITableViewCellSelectionStyleNone;

    [self buildView];
}

return self;
}

- (void)buildView {

UIView *line         = [[UIView alloc] initWithFrame:CGRectMake(0, 79.5f, 500, 0.5f)];
line.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.25f];
[self addSubview:line];

self.fileImageView   = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"File"]];
self.folderImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Folder"]];
self.fileImageView.center   = CGPointMake(40, 50);
self.folderImageView.center = CGPointMake(40, 50);
[self addSubview:self.folderImageView];
[self addSubview:self.fileImageView];

self.iconNameLabel               = [[UILabel alloc] initWithFrame:CGRectMake(0, 13, 80, 20)];
self.iconNameLabel.font          = [UIFont fontWithName:@"AppleSDGothicNeo-Light" size:12.f];
self.iconNameLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:self.iconNameLabel];

self.nameLabel      = [[UILabel alloc] initWithFrame:CGRectMake(80, 10, 200, 60)];
self.nameLabel.font = [UIFont fontWithName:@"Avenir-Book" size:12.f];
[self addSubview:self.nameLabel];

self.button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 500, 80)];
[self.button addTarget:self
                action:@selector(buttonEvent)
      forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.button];
}    

- (void)loadContent {

File *file = self.data; 
self.nameLabel.text = file.name;
if (file.fileType == kFolder) { 
    [self changeToFolderState];
} else if (file.fileType == kFile) {
    [self changeToFileState];
}
}

//切换到文件夹状态
- (void)changeToFolderState {
self.fileImageView.hidden    = YES;
self.folderImageView.hidden  = NO;
self.iconNameLabel.textColor = [UIColor blackColor];
self.nameLabel.textColor     = [UIColor blackColor];
self.iconNameLabel.text      = @"Folder";
}

//切换到文件状态
- (void)changeToFileState {
self.fileImageView.hidden    = NO;
self.folderImageView.hidden  = YES;
self.iconNameLabel.textColor = [UIColor grayColor];
self.nameLabel.textColor     = [UIColor grayColor];
self.iconNameLabel.text      = @"File";
}

- (void)buttonEvent {
File *file = self.data;
if (file.fileType == kFolder) {    
    FileViewController *fvc = [[FileViewController alloc] init];
    fvc.rootFile            = file;
    [self.controller.navigationController pushViewController:fvc
                                                    animated:YES];
}
}
@end

FileViewController

@interface FileViewController : UIViewController

@property (nonatomic, strong) File  *rootFile;

@end

-----------------------------------------------------------------

@interface FileViewController () <UITableViewDelegate, UITableViewDataSource>

@property (nonatomic, strong) UITableView *tableView;

@end

@implementation FileViewController

- (void)viewDidLoad {

[super viewDidLoad];

self.title = self.rootFile.name;

[self initTableView];
}

#pragma mark - tableView相关
- (void)initTableView {

self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];

self.tableView.delegate       = self;
self.tableView.dataSource     = self;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

[self.tableView registerClass:[FileCell class] forCellReuseIdentifier:@"fileCell"];

[self.view addSubview:self.tableView];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

return self.rootFile.childFiles.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

FileCell *cell  = [tableView dequeueReusableCellWithIdentifier:@"fileCell"];
cell.indexPath  = indexPath;
cell.tableView  = tableView;
cell.controller = self;

// 传入节点File
cell.data       = self.rootFile.childFiles[indexPath.row];

[cell loadContent];

return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

return 80.f;
}
@end

ViewController

@interface ViewController ()<UITableViewDelegate, UITableViewDataSource>
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) File        *root;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.title = @"Root";

// 创建根节点
self.root = [File fileWithFileType:kFolder fileName:@"root"];

// 创建第一级文件
File *folder_A_1 = [File fileWithFileType:kFolder fileName:@"Folder-A-1"];
File *file_A_2   = [File fileWithFileType:kFile fileName:@"File-A-2"];
File *file_A_3   = [File fileWithFileType:kFile fileName:@"File-A-3"];
File *file_A_4   = [File fileWithFileType:kFile fileName:@"File-A-4"];

// 创建第二级文件
File *folder_B_1 = [File fileWithFileType:kFolder fileName:@"Folder-B-1"];
File *file_B_2   = [File fileWithFileType:kFile fileName:@"File-B-2"];
File *file_B_3   = [File fileWithFileType:kFile fileName:@"File-B-3"];
File *folder_B_2 = [File fileWithFileType:kFolder fileName:@"Folder-B-2"];

// 创建第三级文件
File *folder_C_1 = [File fileWithFileType:kFolder fileName:@"Folder-C-1"];
File *file_C_1   = [File fileWithFileType:kFile fileName:@"File-C-1"];
File *file_C_2   = [File fileWithFileType:kFile fileName:@"File-C-2"];

[self.root addFile:folder_A_1];
[self.root addFile:file_A_2];
[self.root addFile:file_A_3];
[self.root addFile:file_A_4];

[folder_A_1 addFile:folder_B_1];
[folder_A_1 addFile:file_B_2];
[folder_A_1 addFile:file_B_3];
[folder_A_1 addFile:folder_B_2];

[folder_B_1 addFile:folder_C_1];
[folder_B_1 addFile:file_C_1];
[folder_B_2 addFile:file_C_2];

[self initTableView];
}

#pragma mark - tableView相关
- (void)initTableView {

self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];

self.tableView.delegate       = self;
self.tableView.dataSource     = self;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

[self.tableView registerClass:[FileCell class] forCellReuseIdentifier:@"fileCell"];

[self.view addSubview:self.tableView];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

return self.root.childFiles.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

FileCell *cell  = [tableView dequeueReusableCellWithIdentifier:@"fileCell"];
cell.indexPath  = indexPath;
cell.tableView  = tableView;
cell.controller = self;

// 传入节点File
cell.data       = self.root.childFiles[indexPath.row];

[cell loadContent];

return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

return 80.f;
}

@end

Node类里含有Node的属性

Node.h

@property (nonatomic, strong) Node *leftNode;

Node.h

@property (nonatomic, strong) NSMutableArray <Node *>  *childNodes;

便利构造器里调用alloc init方法

+ (instancetype)nodeWithNodeName:(NSString *)nodeName {

//使用self class以便子类继承

Node *node    = [[[self class] alloc] init];
node.nodeName = nodeName;
return node;
}

Override init method

- (instancetype)init { 
self = [super init];
if (self) {
    self.childNodes = [NSMutableArray array];
}
return self;
}

调用description显示对象的打印结果

- (NSString *)description {
return [NSString stringWithFormat:@"[Node] - %@", self.nodeName];
}

枚举类型的属性和参数

typedef enum : NSUInteger {  
kFile,   // 文件
kFolder, // 文件夹  
} EFile;

@property (nonatomic) EFile             fileType;

+ (instancetype)fileWithFileType:(EFile)fileType fileName:(NSString *)name;

表视图间隔灰线

UIView *line         = [[UIView alloc] initWithFrame:CGRectMake(0, 79.5f, 500, 0.5f)];
line.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.25f];
[self addSubview:line];

以中心定位某视图

self.fileImageView.center   = CGPointMake(40, 50);
self.folderImageView.center = CGPointMake(40, 50);

视图导航栏的标签设定

- (void)viewDidLoad {
[super viewDidLoad];

self.title = self.rootFile.name; 

[self initTableView];
}

代码创建tableView

- (void)initTableView {

self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];

self.tableView.delegate       = self;
self.tableView.dataSource     = self;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

[self.tableView registerClass:[FileCell class] forCellReuseIdentifier:@"fileCell"];

[self.view addSubview:self.tableView];
}

代码创建tableViewCell

FileCell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
    self.selectionStyle = UITableViewCellSelectionStyleNone;      
    [self buildView];
}   
return self;
}

ViewController

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
FileCell *cell  = [tableView dequeueReusableCellWithIdentifier:@"fileCell"];
cell.indexPath  = indexPath;
cell.tableView  = tableView;
cell.controller = self;   
// 传入节点File
cell.data       = self.root.childFiles[indexPath.row];

[cell loadContent];

return cell;
}

[Self class]

File *file    = [[[self class] alloc] init];