分析{功能分析(打电话、添加联系人、修改联系人),模块分析(联系人展示、详情模块、添加模块)}
拿到一个项目,首先分析项目框架(工程框架)
首先:判断是否是用户第一次安装:(如果是的,那就加载用户引导页面)(如果不是,那就显示用户联系人的主页面)
其次:用户联系人的主页面VC(用一个协助类 addressbookheleper类(做数据处理相关的内容,属于MVC的M层。只处理相关的数据,这里是为VC瘦身)去提供相应的方法 给 controller (ContactListViewController) )这里面有这几个方法:为VC提供tableViewCell某一分区得到行数、分区个数、对应的分区的标题、右侧的索引、是否要删除某分区、是否要删除某一行、移动某分区的某一行、返回对应行的联系人、添加联系人。
#import "AppDelegate.h"#import "ContactListViewController.h"#import "MainNavigationController.h"//程序导航栏是同样的风格,所以这里用公用的类@interface AppDelegate ()@end@implementation AppDelegate-(void)dealloc{ [_window release]; [super dealloc];}- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; //判断是否是第一次安装 //如果是,则指定为引导用户页为第一启动(为 window 的跟视图控制器)否则进入程序的主页面 //设置 根视图控制器 ContactListViewController * mainVC = [[ContactListViewController alloc]initWithStyle:UITableViewStylePlain]; //创建导航控制器 MainNavigationController * navl = [[MainNavigationController alloc]initWithRootViewController:mainVC]; //指定为 window 根视图控制器 self.window.rootViewController = navl; //释放所有权 [mainVC release]; [navl release]; return YES;}
#import#import "Contact.h"@interface AddressBookHelper : NSObject//提供相应的方法,给对应的 controller (ContactListViewController)+ (NSInteger)numberOfSection;//返回分区个数+ (NSInteger)numberOfRowsInSection:(NSInteger)section;//返回对应分区的行数+ (NSString *)titleForHeaderInSection:(NSInteger)section;//返回对应分区的标题+ (NSArray *)sectionIndexTitles;//返回右侧索引的标题#pragma mark---delete+ (BOOL)isNeedToDeleteWholeSection:(NSInteger)section;//是否要删除整个分区+ (void)deleteWholeSection:(NSInteger)section;//删除整个分区+ (void)deleteRowAtIndexPath:(NSIndexPath *)indexPath;//删除一行#pragma mark---move+ (void)moveFromIndexPath:(NSIndexPath *)sourceIndexPath ToIndexPath:(NSIndexPath *)destinationindexPath;//移动某分区某行#pragma mark---返回对应行所需的联系人+ (Contact *)contactAtIndexPath:(NSIndexPath *)indexPath;#pragma mark---添加联系人+(void)addContact:(Contact *)newContact;@end
//// AddressBookHelper.m#import "AddressBookHelper.h"#import/* AddressHelper 类 是通讯录列表的界面的辅助类,帮助(ContactListViewController )做数据处理相关的内容,为 ContactListViewController 瘦身,该类可有可无 属于 MVC 的 M 层的类 M层的类 只处理相关的数据(和数据有关的类都是M层)如:数据解析类 数据请求类 数据本地化 */@interface AddressBookHelper ()//存储外层的大字典@property(nonatomic,retain)NSMutableDictionary * dataSource;//存储拍好序的 key@property(nonatomic,retain)NSMutableArray * sortedKeys;@end@implementation AddressBookHelperstatic AddressBookHelper * helper ;//让生命周期与程序的生命周期一致//单例方法+ (AddressBookHelper *)defaultHelper{ //安全处理保证多线程下是安全的 @synchronized(self){ if (!helper) { helper = [[AddressBookHelper alloc]init]; [helper readDataFromPlist];//获取本地 plist 文件 } } return helper;}//获取本地文件-(void)readDataFromPlist{ //获取文件。获取外层字典的信息 NSString * filePath = [[NSBundle mainBundle] pathForResource:kPlistName ofType:@"plist"]; NSDictionary * dict = [NSDictionary dictionaryWithContentsOfFile:filePath]; self.dataSource = [NSMutableDictionary dictionaryWithCapacity:dict.count]; //2.1遍历外层字典的key for (NSString * key in dict) {// NSArray * group = [dict objectForKey:key]; //2.2通过key 获取联系人分组数据 NSArray * group = dict[key]; //2.3创建一个可变数组 来存放联系人对象 NSMutableArray * perArr = [NSMutableArray arrayWithCapacity:group.count]; //2.4遍历分组数组 获得联系人小字典 for (NSDictionary * dic in group) { //2.5将对象封装到字典 Contact * contact = [[Contact alloc]initWithDic:dic]; //2.6将联系人字典添加到存放对象的数组 [perArr addObject:contact]; [contact release]; } //2.7将存放对象的数组以及key 存放到 大字典里 [self.dataSource setObject:perArr forKey:key];//(可变字典可以这样格式写,不可变的不能这样写) //获得排好序的 key NSArray * sorted = [[_dataSource allKeys]sortedArrayUsingSelector:@selector(compare:)]; self.sortedKeys = [NSMutableArray arrayWithArray:sorted]; }}//提供相应的方法,给对应的 controller (ContactListViewController)//返回分区个数+ (NSInteger)numberOfSection{ return [[self defaultHelper].dataSource count];// [self defaultHelper].sortedKeys.count;}//返回对应分区的行数+ (NSInteger)numberOfRowsInSection:(NSInteger)section{ return [helper.dataSource [helper.sortedKeys[section]] count];}//返回对应分区的标题+ (NSString *)titleForHeaderInSection:(NSInteger)section{ return helper.sortedKeys[section];}//返回右侧索引的标题+ (NSArray *)sectionIndexTitles{ return helper.sortedKeys;}#pragma mark---delete//是否要删除整个分区+ (BOOL)isNeedToDeleteWholeSection:(NSInteger)section{ //获取对应分组的联系人数组 NSArray * group = helper.dataSource[helper.sortedKeys[section]]; //如果该分组只有一元素,就代表只有一个人 if (group.count == 1) { return YES;//则需要删除整个分区 } return NO;//否则不需要删除}//删除整个分区+ (void)deleteWholeSection:(NSInteger)section{ //1.获取对应的 key NSString * key = helper.sortedKeys[section]; //2.从字典中删除 key 的分组数组 [helper.dataSource removeObjectForKey:key]; //3.从key 数组中删除 key [helper.sortedKeys removeObject:key]; /* //从字典删除对应的分组的数组 [helper.dataSource removeObjectForKey:helper.sortedKeys[section]]; //从拍好序的 key 数组中移除 key 值 [helper.sortedKeys removeObjectAtIndex:section]; */}//删除一行+ (void)deleteRowAtIndexPath:(NSIndexPath *)indexPath{ //1.获取对应的分组 NSMutableArray * group = helper.dataSource[helper.sortedKeys[indexPath.section]];//这里要导入 框架 //2.从对应的分组中移除 [group removeObjectAtIndex:indexPath.row];//根据下标索引去删除}#pragma mark---move//移动某分区某行+ (void)moveFromIndexPath:(NSIndexPath *)sourceIndexPath ToIndexPath:(NSIndexPath *)destinationindexPath{ /* //获取对应分组的数组 NSMutableArray * group = helper.dataSource[helper.sortedKeys[sourceIndexPath.section]]; //获取对应的联系人 Contact * per =[[group objectAtIndex:sourceIndexPath.row]retain]; //从对应位置移除 [group removeObjectAtIndex:sourceIndexPath.row]; //插入联系人 [group insertObject:per atIndex:destinationindexPath.row]; */ NSString * key = [helper.sortedKeys objectAtIndex:sourceIndexPath.section]; NSMutableArray * group = [helper.dataSource objectForKey:key]; Contact * per = [[group objectAtIndex:sourceIndexPath.row]retain]; [group removeObjectAtIndex:sourceIndexPath.row]; [group insertObject:per atIndex:destinationindexPath.row]; [per release]; }#pragma mark---返回对应行所需的联系人+ (Contact *)contactAtIndexPath:(NSIndexPath *)indexPath{ //测试的假数据// Contact * per = [[Contact alloc]initWithDic:@{@"name":@"张三",@"gender":@"男",@"phoneNum":@"13523526303",@"photo":@"jia"}];// return per; Contact * per = helper.dataSource[helper.sortedKeys[indexPath.section]][indexPath.row]; return per; //对的写法// NSArray * group = helper.dataSource[helper.sortedKeys[indexPath.section]];// Contact * per = group[indexPath.row];// return per; //对// return helper.dataSource[helper.sortedKeys[indexPath.section]][indexPath.row];}#pragma mark---添加联系人+(void)addContact:(Contact *)newContact{ }-(void)dealloc{ self.dataSource = nil; self.sortedKeys = nil; [super dealloc];}@end
并提供相对应的借口,供VC里的数据处理使用
然后:在联系人的主页面要实现对数据与用户页面的交互功能
#import@interface ContactListViewController : UITableViewController @end、、、、、、、ContactListViewController.m文件//// ContactListViewController.m#import "ContactListViewController.h"#import "AddressBookHelper.h"#import "Contact.h"#import "AddContactViewController.h"#import "MainNavigationController.h"#import "DetailViewController.h"#import "CustomContactCellTabviewCell.h"@interface ContactListViewController () @end@implementation ContactListViewController- (void)viewDidLoad { [super viewDidLoad]; [self customizedNavBar];//配置该页面的导航条 //注册 cell [self.tableView registerClass:[CustomContactCellTabviewCell class] forCellReuseIdentifier:@"reuse"];}#pragma mark---配置该页面的导航条-(void)customizedNavBar{ self.navigationItem.title = @"XXXX通讯录"; self.navigationItem.rightBarButtonItem = self.editButtonItem; UIBarButtonItem * left = [[UIBarButtonItem alloc]initWithImage:[[UIImage imageNamed:@"add_contact"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] style:UIBarButtonItemStylePlain target:self action:@selector(handleAddAction:)]; self.navigationItem.leftBarButtonItem = left; [left release];}#pragma mark---模态进入添加联系人页面-(void)handleAddAction:(UIBarButtonItem *)sender{ AddContactViewController * addContactVC = [[AddContactViewController alloc]init]; MainNavigationController * mainVC = [[MainNavigationController alloc]initWithRootViewController:addContactVC]; mainVC.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal; [self presentViewController:mainVC animated:YES completion:^{ }]; [addContactVC release]; [mainVC release];}#pragma mark--内存警告处理- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; if ([self isViewLoaded] && !self.view.window) { self.view = nil; }}#pragma mark - UITableViewDataSource 两个必须实现方法- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // 通过助手类 返回分区的个数 return [AddressBookHelper numberOfSection];}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // 通过助手类 返回分区的行数 return [AddressBookHelper numberOfRowsInSection:section];}#pragma mark---信息展示- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { CustomContactCellTabviewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"reuse" forIndexPath:indexPath]; //cell 控件的赋值操作 //1. Contact * contact = [AddressBookHelper contactAtIndexPath:indexPath]; //2.// cell.contact = contact; //为系统默认的控件赋值 [cell configureCell:contact]; //视图控制器完成拨打电话 cell.delegate = self; cell.indexPath = indexPath; return cell;}-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ return [AddressBookHelper titleForHeaderInSection:section];}#pragma mark---右侧的索引的标题-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{ return [AddressBookHelper sectionIndexTitles];}#pragma mark--- cell 行高设置-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ return 90;}#pragma mark---tableViewcell 是否可以编辑- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { return YES;}/* */#pragma mark---重写方法去编辑 tableviewcell- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { if ([AddressBookHelper isNeedToDeleteWholeSection:indexPath.section]) { //数据源删除 [AddressBookHelper deleteWholeSection:indexPath.section]; //UI 界面 [tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationLeft]; }else{ //数据源删除 [AddressBookHelper deleteRowAtIndexPath:indexPath]; //UI 界面 [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; } } else if (editingStyle == UITableViewCellEditingStyleInsert) { // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view } }#pragma mark---移动操作- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { //调用助手类 完成对应的移动操作 [AddressBookHelper moveFromIndexPath:fromIndexPath ToIndexPath:toIndexPath];}#pragma mark---移动设置(cell是否可以移动)- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { return YES;}#pragma makr---限制移动的范围,禁止跨区移动-(NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath{ if (sourceIndexPath.section == proposedDestinationIndexPath.section) { return proposedDestinationIndexPath; } return sourceIndexPath;}#pragma mark---点击进入详情页面 (UITableViewDelegate)-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ //push 进入联系人详情页面 DetailViewController * detailVC = [[DetailViewController alloc]init]; //属性传值 [self.navigationController pushViewController:detailVC animated:YES]; [detailVC release];}#pragma mark---MakeACallDelegate 协议方法实现//拨打电话-(void)dial:(NSIndexPath *)indexPath{ Contact * contact = [AddressBookHelper contactAtIndexPath:indexPath]; UIWebView * webview = [[UIWebView alloc]init]; [webview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"tel:%@",contact.phoneNum]]]]; [webview release]; }@end
其中要用到的数据层类 M
Contact.h文件#import@interface Contact : NSObject@property(nonatomic,copy)NSString * name;@property(nonatomic,copy)NSString * gender;@property(nonatomic,copy)NSString * phoneNum;@property(nonatomic,copy)NSString * photo;-(instancetype)initWithDic:(NSDictionary *)dic;@endContact.m 文件#import "Contact.h"@implementation Contact-(instancetype)initWithDic:(NSDictionary *)dic{ self = [super init]; if (self) { //KVC 赋值 [self setValuesForKeysWithDictionary:dic]; } return self;}//对应的KVC 赋值的错误安全处理-(void)setValue:(id)value forUndefinedKey:(NSString *)key{ }-(void)dealloc{ self.name = nil; self.gender = nil; self.phoneNum = nil; self.photo = nil; [super dealloc];}@end
视图类 V
#import@class Contact;//为 callBtn 制订协议,实现拨打电话@protocol MakeACallDelegate -(void)dial:(NSIndexPath *)indexPath;@end@interface CustomContactCellTabviewCell : UITableViewCell@property (nonatomic ,retain) UIImageView *photoView;@property (nonatomic ,retain) UILabel *nameLabel;@property (nonatomic ,retain) UILabel *phoneLabel;@property (nonatomic ,retain) UIButton *callBtn;@property(nonatomic,assign)id delegate;@property(nonatomic,retain)NSIndexPath * indexPath;//存储当前点击 call 的索引@property(nonatomic,retain)Contact * contact;//声明一个联系人对象的属性//为cell 上的控件赋值-(void)configureCell:(Contact *)contact;@end//// CustomContactCellTabviewCell.m#import "CustomContactCellTabviewCell.h"#import "Contact.h"#import "AddressBookHelper.h"@implementation CustomContactCellTabviewCell- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { [self.contentView addSubview:self.photoView]; [self.contentView addSubview:self.nameLabel]; [self.contentView addSubview:self.phoneLabel]; [self.contentView addSubview:self.callBtn]; } return self;}#pragma lazy loading 实现控件的创建- (UIImageView *)photoView { if (!_photoView) { self.photoView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20, 50, 50)]; _photoView.layer.cornerRadius = 5.0; _photoView.layer.masksToBounds = YES; } return [[_photoView retain]autorelease]; }- (UILabel *)nameLabel { if (!_nameLabel) { self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 30, self.frame.size.width / 3, 40)]; } return [[_nameLabel retain]autorelease]; }- (UILabel *)phoneLabel { if (!_phoneLabel) { self.phoneLabel = [[UILabel alloc] initWithFrame:CGRectMake(70 + self.frame.size.width / 3, 30, self.frame.size.width / 2, 40)]; } return [[_phoneLabel retain]autorelease]; }- (UIButton *)callBtn { if (!_callBtn) { self.callBtn = [UIButton buttonWithType:UIButtonTypeSystem]; _callBtn.frame = CGRectMake(self.frame.size.width - 20, 30, 40, 40); //添加触发 [_callBtn addTarget:self action:@selector(handleCall:) forControlEvents:UIControlEventTouchUpInside]; [_callBtn setImage:[UIImage imageNamed:@"action_call"] forState:UIControlStateNormal]; } return _callBtn;}#pragma mark---打电话按钮- (void)handleCall:(UIButton *)sender { if (!_delegate && [_delegate respondsToSelector:@selector(dial:)]) { [_delegate dial:self.indexPath]; }}- (void)dealloc { self.nameLabel = nil; self.photoView = nil; self.phoneLabel = nil; self.callBtn = nil; [super dealloc];}- (void)awakeFromNib { // Initialization code}- (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; // Configure the view for the selected state}//第一种 : 自定义方法实现对 cell 的控件的赋值-(void)configureCell:(Contact *)contact{ self.nameLabel.text = contact.name; self.phoneLabel.text = contact.phoneNum; self.photoView.image = [UIImage imageNamed:contact.photo];}//第二种 :重写 setter 方法 为 cell 上的方法赋值/*-(void)setContact:(Contact *)contact{ if (_contact != contact) { [_contact release]; _contact = [contact retain]; } self.nameLabel.text = contact.name; self.phoneLabel.text = contact.phoneNum; self.photoView.image = [UIImage imageNamed:contact.photo];}*/@end
其他参考: