UI:用UITableView制作通讯录的关键代码

分析{功能分析(打电话、添加联系人、修改联系人),模块分析(联系人展示、详情模块、添加模块)}

拿到一个项目,首先分析项目框架(工程框架)

首先:判断是否是用户第一次安装:(如果是的,那就加载用户引导页面)(如果不是,那就显示用户联系人的主页面)

其次:用户联系人的主页面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;
}
View Code Appdelegate.m文件
#import <Foundation/Foundation.h>
#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
View Code 方法的声明
//
//  AddressBookHelper.m

#import "AddressBookHelper.h"
#import <UIKit/UIKit.h>

/*
 AddressHelper 类 是通讯录列表的界面的辅助类,帮助(ContactListViewController )做数据处理相关的内容,为  ContactListViewController 瘦身,该类可有可无 属于 MVC 的 M 层的类
 M层的类  只处理相关的数据(和数据有关的类都是M层)如:数据解析类 数据请求类 数据本地化
 */
@interface AddressBookHelper ()
//存储外层的大字典
@property(nonatomic,retain)NSMutableDictionary * dataSource;
//存储拍好序的 key
@property(nonatomic,retain)NSMutableArray * sortedKeys;

@end

@implementation AddressBookHelper
static 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]];//这里要导入 <UIKit/UIKit.h>框架
    //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
View Code 方法的实现

并提供相对应的借口,供VC里的数据处理使用

然后:在联系人的主页面要实现对数据与用户页面的交互功能

#import <UIKit/UIKit.h>

@interface ContactListViewController : UITableViewController<UITableViewDelegate>

@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 ()<MakeACallDelegate>

@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
View Code ContactListViewController.m & .h文件

其中要用到的数据层类 M

Contact.h文件

#import <Foundation/Foundation.h>

@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;
@end


Contact.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
View Code contact.h & .m 文件

视图类 V

#import <UIKit/UIKit.h>
@class Contact;

//为 callBtn 制订协议,实现拨打电话
@protocol MakeACallDelegate <NSObject>

-(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<MakeACallDelegate>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
View Code contactContactCelltableCell.h & .m 文件

其他参考:参考

原文地址:https://www.cnblogs.com/benpaobadaniu/p/4806198.html