iOS实战项目开发:通讯录
- 页面跳转
- MBProgressHUD第三方类库
- 数据存储
- UITableView深入理解
- 代理(代理传值)
storyBoard实现页面跳转布局
Paste_Image.png- 删除系统的VC storyBoard中的xib 拖拽自己的xib并 添加导航控制器 item等等 最后创建VC进行关联
实现输入框的监听
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self addobser];
}
- (void)addobser{
// 注册通知 添加观察者
/*
observer:给self添加 的意思是针对本控制器
selector:调用的方法
name:表示对哪一个属性观察
object:表示要观察的对象
*/
// 添加用户名输入框观察者
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.nameField];
// 添加密码输入框观察者
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.passwordField];
}```
pragma mark - 文本框变化后调用的方法
-
(void)textChange{
// if (self.nameField.text.length && self.passwordField.text.length) {
// self.loginBtn.enabled = YES;
// }else{
// self.loginBtn.enabled = NO;
// }
// 等价于 三目运算符
self.loginBtn.enabled = (self.nameField.text.length && self.passwordField.text.length);
} -
(void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}```
效果:
login.gif实现Segue的传值 和 跳转
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #78492a}p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #008400}p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo}p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #703daa}p.p5 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; min-height: 21.0px}p.p6 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #3d1d81}span.s1 {font-variant-ligatures: no-common-ligatures}span.s2 {font: 18.0px 'Heiti SC Light'; font-variant-ligatures: no-common-ligatures}span.s3 {font-variant-ligatures: no-common-ligatures; color: #bb2ca2}span.s4 {font-variant-ligatures: no-common-ligatures; color: #703daa}span.s5 {font-variant-ligatures: no-common-ligatures; color: #000000}span.s6 {font-variant-ligatures: no-common-ligatures; color: #3d1d81}span.s7 {font-variant-ligatures: no-common-ligatures; color: #d12f1b}span.s8 {font: 18.0px 'Heiti SC Light'; font-variant-ligatures: no-common-ligatures; color: #d12f1b}span.s9 {font-variant-ligatures: no-common-ligatures; color: #4f8187}
#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.
//1:取得目标控制器
UIViewController *contactVC = segue.destinationViewController;
// 2:设置标题(传值)
contactVC.title = [NSString stringWithFormat:@"%@的联系人列表",self.nameField.text];
}
#pragma mark - 当用户点击之后 实现传值和跳转工作
- (IBAction)loginOnclick:(id)sender {
// 执行跳转
[self performSegueWithIdentifier:@"LoginToContact" sender:nil];
}```
效果
![segus.gif](http:https://img.haomeiwen.com/i189984/1597cea29f203b52.gif?imageMogr2/auto-orient/strip)
###UIAlertViewController与添加页面布局
- (IBAction)exitAction:(id)sender {
UIAlertController *alertC = [UIAlertController alertControllerWithTitle:@"要注销吗" message:@"真的要注销吗?" preferredStyle:UIAlertControllerStyleActionSheet];
// 添加取消按钮
[alertC addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
// 添加确定按钮
[alertC addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
[self.navigationController popViewControllerAnimated:NO];
}]];
// 弹出
[self presentViewController:alertC animated:YES completion:nil];
}```
exit.gif添加功能与传值
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #d12f1b}p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; min-height: 21.0px}p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #4f8187}p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo}p.p5 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #bb2ca2}span.s1 {font-variant-ligatures: no-common-ligatures; color: #78492a}span.s2 {font-variant-ligatures: no-common-ligatures}span.s3 {font-variant-ligatures: no-common-ligatures; color: #bb2ca2}span.s4 {font-variant-ligatures: no-common-ligatures; color: #000000}span.s5 {font-variant-ligatures: no-common-ligatures; color: #703daa}span.s6 {font-variant-ligatures: no-common-ligatures; color: #4f8187}
#import <UIKit/UIKit.h>
@class AddTableViewController,ContactModel;
@protocol AddVCDelegate <NSObject>
@optional
- (void)addContact:(AddTableViewController *)addVC didAddContact:(ContactModel *)model;
@end
@interface AddTableViewController : UIViewController
@property (nonatomic, weak) id<AddVCDelegate> delegate;
@end```
import "AddTableViewController.h"
import "ContactModel.h"
@interface AddTableViewController ()
@property (weak, nonatomic) IBOutlet UITextField *phoneField;
@property (weak, nonatomic) IBOutlet UITextField *passwordField;
@property (weak, nonatomic) IBOutlet UIButton *addContactButton; // 添加联系人按钮
@end
@implementation AddTableViewController
-
(IBAction)backAction:(id)sender {
} -
(void)viewDidLoad {
[super viewDidLoad];// 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;// 添加观察者
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.phoneField];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.passwordField];
} -
(void)textChange{
self.addContactButton.enabled = (self.phoneField.text.length && self.passwordField.text.length);
}
pragma mark - 视图已经出现 让输入框成为第一响应者
-
(void)viewDidAppear:(BOOL)animated{
[self.phoneField becomeFirstResponder];
} -
(IBAction)addAction {
// 关闭当前控制器
[self.navigationController popViewControllerAnimated:YES];
// 代理传值
if ([self.delegate respondsToSelector:@selector(addContact:didAddContact:)]) {
ContactModel *contactModel = [[ContactModel alloc] init];
contactModel.name = self.phoneField.text;
contactModel.phone = self.passwordField.text;
[self.delegate addContact:self didAddContact:contactModel];
}
} -
(void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end```
联系人控制器 获取并刷新数据
- 设置代理 遵守代理 实现代理方法 中 获取数据 刷新TableView 并存储数据
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #78492a}p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; min-height: 21.0px}p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #008400}p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo}p.p5 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #703daa}p.p6 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px 'Heiti SC Light'; color: #008400}p.p7 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #4f8187}p.p8 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #3d1d81}span.s1 {font-variant-ligatures: no-common-ligatures}span.s2 {font-variant-ligatures: no-common-ligatures; color: #bb2ca2}span.s3 {font-variant-ligatures: no-common-ligatures; color: #703daa}span.s4 {font-variant-ligatures: no-common-ligatures; color: #000000}span.s5 {font: 18.0px 'Heiti SC Light'; font-variant-ligatures: no-common-ligatures}span.s6 {font: 18.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #000000}span.s7 {font: 18.0px Menlo; font-variant-ligatures: no-common-ligatures}span.s8 {font-variant-ligatures: no-common-ligatures; color: #3d1d81}span.s9 {font-variant-ligatures: no-common-ligatures; color: #4f8187}span.s10 {font-variant-ligatures: no-common-ligatures; color: #008400}span.s11 {font: 18.0px 'Heiti SC Light'; font-variant-ligatures: no-common-ligatures; color: #008400}
#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.
// 拿到目的控制器
id vc = segue.destinationViewController;
// 判断是哪一个控制器
if ([vc isKindOfClass:[AddTableViewController class]]) {
// 如果是跳转到添加联系人控制器 设置代理
AddTableViewController *addVc = vc;
addVc.delegate = self;
}else if ([vc isKindOfClass:[EditTableViewController class]]){
EditTableViewController *editVC = vc;
// 取得选中的那一行
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
// 数据传入
editVC.contactModel = self.contactArr[path.row];
// 设置代理
editVC.delegate = self;
}
// 拿到控制器
// AddTableViewController *addVC = segue.destinationViewController;
// 设置代理
// addVC.delegate = self;
}```
pragma mark - addContactDelegate
- (void)addContact:(AddTableViewController *)addVC didAddContact:(ContactModel *)model{
// 1:把传过来的数据模型给我们的数组
[self.contactArr addObject:model];
// 2:刷新表示图
[self.tableView reloadData];
// 3:对象归档
[NSKeyedArchiver archiveRootObject:self.contactArr toFile:ContactFilePath];
}```
数据归档存储
-
用户名 密码 记住密码 用NSUserDefults存储
-
编写key 存储于读取
// 数据存储
#define User_Name_Key @"name"
#define PWD_key @"password"
#define RemPwdKry @"rem_pwd"```
- 读取数据
-
(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.// 读取上次的配置
NSUserDefaults *defults = [NSUserDefaults standardUserDefaults];
self.nameField.text = [defults valueForKey:User_Name_Key];
self.passwordField.text = [defults valueForKey:PWD_key];
self.remberSwitch.on = [defults boolForKey:RemPwdKry];
// 如果开关打开 密码框则自动存入数据
if (self.remberSwitch.isOn) {
self.passwordField.text = [defults valueForKey:PWD_key];
self.loginBtn.enabled = YES;
}// 监听输入框
[self addobser];
}``` -
存储数据并同步数据
#pragma mark - 当用户点击之后 实现传值和跳转工作
- (IBAction)loginOnclick:(id)sender {
if (![self.nameField.text isEqualToString:@"jafar"]) {
[MBProgressHUD showError:@"账号不存在"];
return;
}
if (![self.passwordField.text isEqualToString:@"jyf2015."]){
[MBProgressHUD showError:@"密码错误"];
return;
}
// 显示蒙版
[MBProgressHUD showMessage:@"正在登录"];
// 模拟两秒时间,结束跳转
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 移除遮盖
[MBProgressHUD hideHUD];
// 执行跳转
[self performSegueWithIdentifier:@"LoginToContact" sender:nil];
});
// 存储用户名和密码
NSUserDefaults *userDefult = [NSUserDefaults standardUserDefaults];
[userDefult setValue:self.nameField.text forKey:User_Name_Key];
[userDefult setValue:self.passwordField.text forKey:PWD_key];
[userDefult setBool:self.remberSwitch.isOn forKey:RemPwdKry];
[userDefult synchronize]; // 同步
}```
![save.gif](http:https://img.haomeiwen.com/i189984/4943bea5e80ec6d4.gif?imageMogr2/auto-orient/strip)
####数据模型的存储方式
- 归档 遵守归档协议 编写路径宏定义 存储 取出 展示 可以存储数组类型哦
import <Foundation/Foundation.h>
@interface ContactModel : NSObject<NSCoding>
@property(nonatomic, copy) NSString * name;
@property(nonatomic, copy) NSString * phone;
@end```
#import "ContactModel.h"
@implementation ContactModel
/*
将某个对象写入文件是会调用
在这个方法中说清楚哪些属性需要存储
*/
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeObject:self.phone forKey:@"phone"];
}
/*
解析对象的时候会调用这个方法
需要解析哪些属性
*/
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.phone = [aDecoder decodeObjectForKey:@"phone"];
}
return self;
}
@end```
//路径
define ContactFilePath [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"contacts.data"]```
- 数组获取的时候先从本地来获取
@interface ContactTableViewController ()<AddVCDelegate,EditTVCDelegate>
@property (nonatomic, strong) NSMutableArray *contactArr; // 联系人数组
@end
@implementation ContactTableViewController
#pragma mark - 懒加载
- (NSMutableArray *)contactArr{
if (!_contactArr) {
// 数据解码
_contactArr = [NSKeyedUnarchiver unarchiveObjectWithFile:ContactFilePath];
if (_contactArr == nil) {
_contactArr = [NSMutableArray array];
}
}
return _contactArr;
}```
- 存储 这里注意 不管是添加联系人还是编辑修改之后的数据都要重新归档
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #78492a}p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo}p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px 'Heiti SC Light'; color: #008400}p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #008400}p.p5 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #3d1d81}p.p6 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; min-height: 21.0px}span.s1 {font-variant-ligatures: no-common-ligatures}span.s2 {font-variant-ligatures: no-common-ligatures; color: #bb2ca2}span.s3 {font-variant-ligatures: no-common-ligatures; color: #4f8187}span.s4 {font: 18.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #000000}span.s5 {font: 18.0px Menlo; font-variant-ligatures: no-common-ligatures}span.s6 {font-variant-ligatures: no-common-ligatures; color: #3d1d81}span.s7 {font-variant-ligatures: no-common-ligatures; color: #000000}span.s8 {font: 18.0px 'Heiti SC Light'; font-variant-ligatures: no-common-ligatures}span.s9 {font-variant-ligatures: no-common-ligatures; color: #703daa}span.s10 {font-variant-ligatures: no-common-ligatures; color: #78492a}
pragma mark - addContactDelegate
- (void)addContact:(AddTableViewController *)addVC didAddContact:(ContactModel *)model{
// 1:把传过来的数据模型给我们的数组
[self.contactArr addObject:model];
// 2:刷新表示图
[self.tableView reloadData];
// 3:对象归档
[NSKeyedArchiver archiveRootObject:self.contactArr toFile:ContactFilePath];
}
pragma mark - editContactDelegate
- (void)editVC:(EditTableViewController *)editVC contactModel:(ContactModel *)model{
[self.tableView reloadData];
[NSKeyedArchiver archiveRootObject:self.contactArr toFile:ContactFilePath];
}```
数据编辑方面的逻辑
- 代理传值 编辑前后输入框enabled属性的判断 主控制器获取之后 刷新与存储逻辑
#import <UIKit/UIKit.h>
@class ContactModel,EditTableViewController;
@protocol EditTVCDelegate <NSObject>
@optional
- (void)editVC:(EditTableViewController *)editVC contactModel:(ContactModel *)model;
@end
@interface EditTableViewController : UIViewController
@property (nonatomic, strong) ContactModel *contactModel;
@property (nonatomic, weak) id<EditTVCDelegate> delegate;
@end```
pragma mark - 编辑按钮
- (IBAction)editAction:(UIBarButtonItem *)sender {
// 如果是编辑状态 结束编辑
if (self.name.enabled) {
self.name.enabled = NO;
self.phone.enabled = NO;
[self.view endEditing:YES];
self.saveBtn.hidden = YES;
sender.title = @"编辑";
// 还原回原来的数据
self.name.text = self.contactModel.name;
self.phone.text = self.contactModel.phone;
}else{
self.name.enabled = YES;
self.phone.enabled = YES;
[self.view endEditing:YES];
self.saveBtn.hidden = NO;
sender.title = @"取消";
}
}
pragma mark - 保存按钮
-
(IBAction)save:(id)sender {
// 保存 并且传递数据
[self.navigationController popViewControllerAnimated:YES];
if ([self.delegate respondsToSelector:@selector(editVC:contactModel:)]) {
// 更新数据模型
self.contactModel.name = self.name.text;
self.contactModel.phone = self.phone.text;
[self.delegate editVC:self contactModel:self.contactModel];
}
} -
(void)viewDidLoad {
[super viewDidLoad];// 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;// 赋值
self.name.text = self.contactModel.name;
self.phone.text = self.contactModel.phone;// 文本框验证
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textCheck) name:UITextFieldTextDidChangeNotification object:self.name];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textCheck) name:UITextFieldTextDidChangeNotification object:self.phone];
}
pragma mark - 文本验证后调用的方法
-
(void)textCheck{
self.saveBtn.enabled = (self.name.text.length && self.phone.text.length);
} -
(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.
}
*/
@end```
主控制器的刷新
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #78492a}p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo}p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #3d1d81}p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; min-height: 21.0px}p.p5 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #008400}p.p6 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #703daa}p.p7 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px 'Heiti SC Light'; color: #008400}p.p8 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #4f8187}span.s1 {font-variant-ligatures: no-common-ligatures}span.s2 {font-variant-ligatures: no-common-ligatures; color: #bb2ca2}span.s3 {font-variant-ligatures: no-common-ligatures; color: #4f8187}span.s4 {font-variant-ligatures: no-common-ligatures; color: #000000}span.s5 {font-variant-ligatures: no-common-ligatures; color: #703daa}span.s6 {font-variant-ligatures: no-common-ligatures; color: #78492a}span.s7 {font: 18.0px 'Heiti SC Light'; font-variant-ligatures: no-common-ligatures}span.s8 {font: 18.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #000000}span.s9 {font: 18.0px Menlo; font-variant-ligatures: no-common-ligatures}span.s10 {font-variant-ligatures: no-common-ligatures; color: #3d1d81}span.s11 {font-variant-ligatures: no-common-ligatures; color: #008400}span.s12 {font: 18.0px 'Heiti SC Light'; font-variant-ligatures: no-common-ligatures; color: #008400}
#pragma mark - editContactDelegate
- (void)editVC:(EditTableViewController *)editVC contactModel:(ContactModel *)model{
[self.tableView reloadData];
[NSKeyedArchiver archiveRootObject:self.contactArr toFile:ContactFilePath];
}
#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.
// 拿到目的控制器
id vc = segue.destinationViewController;
// 判断是哪一个控制器
if ([vc isKindOfClass:[AddTableViewController class]]) {
// 如果是跳转到添加联系人控制器 设置代理
AddTableViewController *addVc = vc;
addVc.delegate = self;
}else if ([vc isKindOfClass:[EditTableViewController class]]){
EditTableViewController *editVC = vc;
// 取得选中的那一行
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
// 数据传入
editVC.contactModel = self.contactArr[path.row];
// 设置代理
editVC.delegate = self;
}
// 拿到控制器
// AddTableViewController *addVC = segue.destinationViewController;
// 设置代理
// addVC.delegate = self;
}```
####删除逻辑 注意数据的更新 和 视图的更新
pragma mark - 调用代理 实现删除功能
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// 删除数据模型
[self.contactArr removeObjectAtIndex:indexPath.row];
// 刷新视图
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];
// 归档
[NSKeyedArchiver archiveRootObject:self.contactArr toFile:ContactFilePath];
}
}```