#import "ContactListController.h"
#import "FMDatabase.h" // 第三方数据库操作类
#import "ContactCell.h"
#import "DetailViewController.h"
#import "AddViewController.h"
@interface ContactListController () <DetailViewControllerDelegate,AddViewControllerDelegate>
@property (nonatomic, retain) FMDatabase *db; // 存储数据库操作类对象
@property (nonatomic, retain) NSMutableArray *dataSource; // 存储所有联系人信息
@end
@implementation ContactListController
// 懒加载, 给数组开辟空间
- (NSMutableArray *)dataSource {
if (!_dataSource) {
self.dataSource = [NSMutableArray arrayWithCapacity:1];
}
return [[_dataSource retain] autorelease];
}
- (void)viewDidLoad {
[super viewDidLoad];
// 1.创建数据库 -- 找到数据库文件
[self createDataBase];
// 2. 创建表 -- 存储数据
[self createTableInDataBase];
// 3. 读取数据
[self selectDataFromDataBase];
// Edit 按钮
// self.navigationItem.rightBarButtonItem
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
#warning Incomplete method implementation.
// Return the number of rows in the section.
return self.dataSource.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ContactCell *cell = [tableView dequeueReusableCellWithIdentifier:@"reuse" forIndexPath:indexPath];
// 从数组中取出Model对象
Person *per = self.dataSource[indexPath.row];
// 给cell 赋值
[cell setValueForCellWithPerson:per];
return cell;
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
// 提交编辑操作
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// 1. 修改数据源
// 移除数据库的数据
Person *per = self.dataSource[indexPath.row];
[self deleteDataFromDataBase:per.cID];
// 从数组中移除
[self.dataSource removeObjectAtIndex:indexPath.row];
// 2. 修改界面
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
}
#pragma mark -- Navigation
// 当通过storyboard 完成界面跳转时, 触发, 通过该方法传值
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"add"]) {
// 添加联系人界面
AddViewController *addVC = [segue.destinationViewController viewControllers][0];
addVC.delegate = self;
}else {
// 列表界面进入详情界面
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender]; // sender 为选中的cell
DetailViewController *detailVC = segue.destinationViewController;
detailVC.valuePer = self.dataSource[indexPath.row];
detailVC.delegate = self;
}
}
#pragma mark -- delegate method
// 详情界面 代理方法 -- 修改联系人
- (void)updatePerson:(Person *)per {
[self updateDataBaseWithPerson:per];
}
// 添加联系人界面
- (void)addPerson:(Person *)per
{
// 1. 添加到数组
[self.dataSource addObject:per];
// 2. 将数据插入到数据库
[self insertDataInDataBase:per];
[self.tableView reloadData];
}
// view 将要展示时, 刷新界面
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.tableView reloadData];
}
#pragma mark -- handle DataBase
// 创建数据库
- (void)createDataBase {
// db -- 数据库操作类对象 -- 指向本地的数据库
self.db = [FMDatabase databaseWithPath:[self getDataBasePath]];
}
// 创建表 -- 表用来存储数据 -- 一条数据就是一个完整的联系人信息
- (void)createTableInDataBase {
// 1. 打开数据库
BOOL isOpen = [self.db open];
if (!isOpen) {
return;
}
// 2. 通过SQL语句操作数据库 -- 创建表
// executeUpdate : 除了查询指令, 其他都用这个方法
BOOL isSuccess = [self.db executeUpdate:@"create table if not exists Person(per_cID integer primary key autoincrement, per_name text, per_gender text, per_age text, per_phone text, per_image blob)"]; // blob 二进制数据
NSLog(@"%@", isSuccess ? @"winner" : @"loser");
// 3. 关闭数据库
[self.db close];
}
// 往数据库中插入一条新的数据
- (void)insertDataInDataBase:(Person *)per {
// 1. 打开数据库
BOOL isOpen = [self.db open];
if (!isOpen) {
return;
}
// 2. 通过SQL语句操作数据库 -- 插入一条新的数据
// executeUpdate : 除了查询指令, 其他都用这个方法
BOOL isSuccess = [self.db executeUpdate:@"insert into Person(per_name, per_gender, per_age, per_phone, per_image) values(?, ?, ?, ?, ?)", per.name, per.gender, per.age, per.phone, per.image];
NSLog(@"%@", isSuccess ? @"insertDataInDataBase winner" : @"insertDataInDataBase loser");
// 3. 关闭数据库
[self.db close];
}
// 更新一条数据
- (void)updateDataBaseWithPerson:(Person *)per {
// 1. 打开数据库
BOOL isOpen = [self.db open];
if (!isOpen) {
return;
}
// 2. 通过SQL语句操作数据库 -- 更新数据
BOOL isSuccess = [self.db executeUpdate:@"update Person set per_name = ?, per_gender = ?, per_age = ?, per_phone = ?, per_image = ?,where per_cID = ?", per.name, per.age, per.phone, per.image, @(per.cID)];
NSLog(@"%@", isSuccess ? @"update winner" : @" update loser");
// 3. 关闭数据库
[self.db close];
}
/**
更新的思路: 点击cell时, 将对应的联系人对象, 传到(属性传值) 详情界面, 当详情界面修改完信息之后, 通过代理回调, 将修改后的 Person对象传到列表中, 进行数据的更新.
*/
// 读取数据 -- 查询数据库
- (void)selectDataFromDataBase {
// 1. 打开数据库
BOOL isOpen = [self.db open];
if (!isOpen) {
return;
}
// 2. 通过SQL语句操作数据库 -- 查询所有的数据
FMResultSet *result = [self.db executeQuery:@"select * from Person"];
while ([result next]) {
// 读取一条数据的每个字段的值
NSInteger ID = [result intForColumn:@"per_cID"];
NSString *name = [result stringForColumn:@"per_name"];
NSString *gender = [result stringForColumn:@"per_gender"];
NSString *phone = [result stringForColumn:@"per_phone"];
NSString *age = [result stringForColumn:@"per_age"];
NSData *data = [result dataForColumn:@"per_image"];
UIImage *image = [UIImage imageWithData:data];
// 将读取到的数据封装成Person对象, 存储到数组中
Person *per = [Person personWithName:name gender:gender phone:phone age:age image:image cID:ID];
[self.dataSource addObject:per];
}
// 3. 关闭数据库
[self.db close];
}
// 获取数据库文件路径
- (NSString *)getDataBasePath {
// 1. 获取Documents 文件夹
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
// 2. 拼接上数据库文件路径
return [documentsPath stringByAppendingPathComponent:@"LO.sqlite"];
}
// 从数据库中移除一条数据
- (void)deleteDataFromDataBase:(NSInteger)cID {
// 1. 打开数据库
BOOL isOpen = [self.db open];
if (!isOpen) {
return;
}
// 2. 通过SQL语句操作数据库 -- 删除一条数据
BOOL isSuccess = [self.db executeUpdate:@"delete from Person where per_cID = ?", cID];
NSLog(@"%@", isSuccess ? @"success" : @"failure");
// 3. 关闭
[self.db close];
}
- (void)dealloc {
self.dataSource = nil;
self.db = nil;
[super dealloc];
}
@end
#import <UIKit/UIKit.h>
#import "Person.h"
// 代理 一
@protocol DetailViewControllerDelegate <NSObject>
- (void)updatePerson:(Person *)per;
@end
@interface DetailViewController : UIViewController
// 代理 二
@property (nonatomic, assign)id<DetailViewControllerDelegate> delegate;
@property (nonatomic, retain)Person *valuePer; // 属性传值
@end
#import "DetailViewController.h"
@interface DetailViewController () <UIActionSheetDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate>
@property (retain, nonatomic) IBOutlet UITextField *nameText;
@property (retain, nonatomic) IBOutlet UITextField *genderText;
@property (retain, nonatomic) IBOutlet UITextField *ageText;
@property (retain, nonatomic) IBOutlet UITextField *phoneNumber;
@property (retain, nonatomic) IBOutlet UIImageView *iconView;
@end
@implementation DetailViewController
// 轻拍手势
- (IBAction)handleTap:(UITapGestureRecognizer *)sender {UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"选择图像" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:@"拍照" otherButtonTitles:@"从相册选择", nil];
[sheet showInView:self.view];
[sheet release];
}
#pragma mark -- imagePickerControllerDelegate
//获取图片后触发
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
self.iconView.image = [info objectForKey:UIImagePickerControllerOriginalImage];
[picker dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - handle Photo
//action sheet 响应事件
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
//创建对象
UIImagePickerController *imagePicker = [[[UIImagePickerController alloc] init] autorelease];
//相片来源:1.photoAlum 2.camera 3.photoLibrary
//指定相片来源
switch (buttonIndex) {
case 0: //拍照
//判断是否支持相机
if ([UIImagePickerController isSourceTypeAvailable:(UIImagePickerControllerSourceTypeCamera)]) {
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:@"不支持相机拍照,请重新选择" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:nil, nil];
[alert show];
[alert release];
return;
}
break;
case 1:
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; //从相册选择
break;
case 2:
return; //取消
default:
break;
}
//跳转模式
imagePicker.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
//指定代理
imagePicker.delegate = self;
//弹出视图
[self presentViewController:imagePicker animated:YES completion:nil];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// 给自身控件赋值
self.iconView.image = self.valuePer.image;
self.nameText.text = self.valuePer.name;
self.phoneNumber.text = self.valuePer.phone;
self.genderText.text = self.valuePer.gender;
self.ageText.text = self.valuePer.age;
// Edit 按钮
self.navigationItem.rightBarButtonItem = self.editButtonItem;
// 控制 控件的交互
[self depenedUserInteration:NO];
}
// 点击Edit 触发
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
// 控制自身控件交互的方法 -- 自定义的
[self depenedUserInteration:editing];
if (!editing) { // 如果此时点击 done 要保存数据
[self save];
}
}
// 控制自身控件交互
- (void)depenedUserInteration:(BOOL)editing {
self.iconView.userInteractionEnabled = editing;
self.nameText.enabled = editing;
self.nameText.enabled = editing;
self.ageText.enabled = editing;
self.phoneNumber.enabled = editing;
}
// 保存更改之后的数据
- (void)save {
// 1. 将内存中的valuePer 数据进行更改
self.valuePer.image = self.iconView.image;
self.valuePer.name = self.nameText.text;
self.valuePer.phone = self.phoneNumber.text;
self.valuePer.age = self.ageText.text;
self.valuePer.gender = self.genderText.text;
// 2. 让前一个界面更改数据库中的数据
// 代理 六
if ([self.delegate respondsToSelector:@selector(updatePerson:)]) {
[self.delegate updatePerson:self.valuePer];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
- (void)dealloc {
self.valuePer = nil;
[_nameText release];
[_genderText release];
[_ageText release];
[_phoneNumber release];
[_iconView release];
[super dealloc];
}
@end
#import <UIKit/UIKit.h>
#import "Person.h"
// 代理 一
@protocol AddViewControllerDelegate <NSObject>
- (void)addPerson:(Person *)per;
@end
@interface AddViewController : UIViewController
@property (nonatomic, assign)id<AddViewControllerDelegate> delegate; // 定义delegate属性 , 存储代理对象
@end
#import "AddViewController.h"
@interface AddViewController () <UINavigationControllerDelegate, UIActionSheetDelegate, UIImagePickerControllerDelegate, AddViewControllerDelegate>
@property (retain, nonatomic) IBOutlet UITextField *nameTextFiled;
@property (retain, nonatomic) IBOutlet UITextField *genderTextFiled;
@property (retain, nonatomic) IBOutlet UITextField *ageTextFiled;
@property (retain, nonatomic) IBOutlet UITextField *phoneNumber;
@property (retain, nonatomic) IBOutlet UIImageView *iconView;
@end
@implementation AddViewController
// 轻拍手势
- (IBAction)handleTap:(UITapGestureRecognizer *)sender {UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"选择图像" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:@"拍照" otherButtonTitles:@"从相册选择", nil];
[sheet showInView:self.view];
[sheet release];
}
#pragma mark -- imagePickerControllerDelegate
//获取图片后触发
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
self.iconView.image = [info objectForKey:UIImagePickerControllerOriginalImage];
[picker dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - handle Photo
//action sheet 响应事件
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
//创建对象
UIImagePickerController *imagePicker = [[[UIImagePickerController alloc] init] autorelease];
//相片来源:1.photoAlum 2.camera 3.photoLibrary
//指定相片来源
switch (buttonIndex) {
case 0: //拍照
//判断是否支持相机
if ([UIImagePickerController isSourceTypeAvailable:(UIImagePickerControllerSourceTypeCamera)]) {
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:@"不支持相机拍照,请重新选择" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:nil, nil];
[alert show];
[alert release];
return;
}
break;
case 1:
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; //从相册选择
break;
case 2:
return; //取消
default:
break;
}
//跳转模式
imagePicker.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
//指定代理
imagePicker.delegate = self;
//弹出视图
[self presentViewController:imagePicker animated:YES completion:nil];
}
// 返回按钮
- (IBAction)backBtn:(UIBarButtonItem *)sender {
// 如果姓名, 联系方式为空, 直接返回上一界面
if (!self.nameTextFiled.text.length || !self.phoneNumber.text.length) {
[self dismissViewControllerAnimated:YES completion:nil];
}
// 如果不为空, 代理传值
if ([self.delegate respondsToSelector:@selector(addPerson:)]) {
Person *per = [Person personWithName:self.nameTextFiled.text gender:self.genderTextFiled.text phone:self.phoneNumber.text age:self.ageTextFiled.text image:self.iconView.image cID:0];
[self.delegate addPerson:per];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
- (void)dealloc {
[_nameTextFiled release];
[_genderTextFiled release];
[_ageTextFiled release];
[_phoneNumber release];
[_iconView release];
[super dealloc];
}
@end