zoukankan      html  css  js  c++  java
  • 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 文件

    其他参考:参考

  • 相关阅读:
    Java n个线程轮流打印数字的问题
    【我所认知的BIOS】—&gt; uEFI AHCI Driver(6) AtaAtapiPassThruSupported的局部变量们
    设备树学习之(二)点灯【转】
    设备树学习之(一)GPIO中断【转】
    S5PV210开发板 VGA测试【转】
    Linux VGA驱动移植实验【转】
    略过天涯 深入浅出VGA和DVI接口【转】
    基于FPGA的VGA可移植模块终极设计【转】
    字符串函数---strcmp()与strncmp()详解及实现【转】
    关于内存中栈和堆的区别(非数据结构中的堆和栈,区别)
  • 原文地址:https://www.cnblogs.com/benpaobadaniu/p/4806198.html
Copyright © 2011-2022 走看看