源码下载地址:http://download.csdn.net/detail/liu537192/8523613
效果图:
核心代码:
//
// JLLoginViewController.h
// 01-私人通讯录
//
// Created by XinYou on 15-3-13.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import
@interface JLLoginViewController : UIViewController
@end
//
// JLLoginViewController.m
// 01-私人通讯录
//
// Created by XinYou on 15-3-13.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import "JLLoginViewController.h"
#import "MBProgressHUD+MJ.h"
@interface JLLoginViewController ()
/**
* “账号”输入框
*/
@property (weak, nonatomic) IBOutlet UITextField *accountField;
/**
* “密码”输入框
*/
@property (weak, nonatomic) IBOutlet UITextField *pwdField;
/**
* “登录”按钮
*/
@property (weak, nonatomic) IBOutlet UIButton *loginBtn;
/**
* "记住密码"开关
*/
@property (weak, nonatomic) IBOutlet UISwitch *remeberPwdSwitch;
/**
* "自动登录"开关
*/
@property (weak, nonatomic) IBOutlet UISwitch *autoLoginSwitch;
/**
* "记住密码"开关状态改变
*/
- (IBAction)remeberPwdSwitchChange;
/**
* "自动登录"开关状态改变
*/
- (IBAction)autoLoginSwitchChange;
/**
* "登录"按钮被点击
*/
- (IBAction)loginBtnClick;
@end
@implementation JLLoginViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// 监听“账号”输入框的文本改变。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.accountField];
// 监听“密码”输入框的文本改变
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.pwdField];
// 读取上一次的 记住密码、自动登录等个人配置数据
NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
self.accountField.text = [def objectForKey:@"JLAccountKey"];
self.remeberPwdSwitch.on = [def boolForKey:@"JLRemeberPwdKey"];
self.autoLoginSwitch.on = [def boolForKey:@"JLAutoLoginKey"];
// 如果上一次登录时勾选了“记住密码”,才自动填充密码
if (self.remeberPwdSwitch.isOn) {
self.pwdField.text = [def objectForKey:@"JLPwdKey"];
}
// 如果上一次登录时勾选了“自动登录”,才自动登录
if (self.autoLoginSwitch.isOn) {
[self loginBtnClick];
}
}
- (void)dealloc{
// 一定要记得移除对通知的监听
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)textChange{
// 只有当“账号”输入框和“密码”输入框都有文字的时候,“登录”按钮才可用
// if (self.accountField.text.length > 0 && self.pwdField.text.length > 0) {
// self.loginBtn.enabled = YES;
// }else{
// self.loginBtn.enabled = NO;
// }
// 以上代码可以简写成:
self.loginBtn.enabled = (self.accountField.text.length && self.pwdField.text.length);
}
/**
* "记住密码"开关状态改变
*/
- (IBAction)remeberPwdSwitchChange {
// 如果“记住密码”开关关闭,那么“自动登录”开关也应该关闭
if (self.remeberPwdSwitch.isOn == NO) {
// self.autoLoginSwitch.on = NO;
// 下面这句代码相比上面这句这代码,有一个动画的过程,用户体验更好
[self.autoLoginSwitch setOn:NO animated:YES];
}
}
/**
* "自动登录"开关状态改变
*/
- (IBAction)autoLoginSwitchChange {
// 如果“自动登录”开关打开,那么“记住密码”开关也应该打开
if (self.autoLoginSwitch.isOn == YES) {
// self.remeberPwdSwitch.on = YES;
[self.remeberPwdSwitch setOn:YES animated:YES];
}
}
/**
* "登录"按钮被点击
*/
- (IBAction)loginBtnClick {
if (![self.accountField.text isEqualToString:@"kobe"]) {
// 提示用户"账号不存在"
[MBProgressHUD showError:@"账号不存在"];
return;
}
if (![self.pwdField.text isEqualToString:@"123"]) {
// 提示用户"密码错误"
[MBProgressHUD showError:@"密码错误"];
return;
}
// 显示一个遮盖
[MBProgressHUD showMessage:@"玩命加载中..."];
// 模拟网络请求,1秒钟后跳转
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 移除遮盖
[MBProgressHUD hideHUD];
[self performSegueWithIdentifier:@"loginToContact" sender:nil];
// 存储 记住密码、自动登录等个人配置数据
NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
// 存储是否记住密码
[def setBool:self.remeberPwdSwitch.isOn forKey:@"JLRemeberPwdKey"];
// 存储是否自动登录
[def setBool:self.autoLoginSwitch.isOn forKey:@"JLAutoLoginKey"];
// 存储账号
[def setObject:self.accountField.text forKey:@"JLAccountKey"];
// 存储密码
[def setObject:self.pwdField.text forKey:@"JLPwdKey"];
[def synchronize];// 立即同步到文件
});
}
@end
//
// JLContactViewController.h
// 01-私人通讯录
//
// Created by XinYou on 15-3-13.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import
@interface JLContactViewController : UITableViewController
@end
//
// JLContactViewController.m
// 01-私人通讯录
//
// Created by XinYou on 15-3-13.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import "JLContactViewController.h"
#import "JLContact.h"
#import "JLAddViewController.h"
#import "JLContactsCell.h"
#import "JLEditViewController.h"
#define JLContactsDataFilePath [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"contacts.data"]
@interface JLContactViewController ()
- (IBAction)loginOut:(UIBarButtonItem *)sender;
@property (nonatomic, strong)NSMutableArray *contactsArray;
@end
@implementation JLContactViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// 设置tableView没有分隔线
// 因为默认是有分隔线的,而且满屏幕的都是分隔线,我们只想让有数据的cell有分隔线
// 所以先设置tableView没有分隔线,然后在每个cell的底部加一个高度为1的UIView
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
}
- (NSMutableArray *)contactsArray{
if (_contactsArray == nil) {
// 从文件中读取以前保存的联系人的数据
_contactsArray = [NSKeyedUnarchiver unarchiveObjectWithFile:JLContactsDataFilePath];
// 如果文件中没有数据或者文件不存在
if (_contactsArray == nil) {
_contactsArray = [NSMutableArray array];
}
}
return _contactsArray;
}
#pragma mark - UITableView的数据源方法
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.contactsArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
JLContactsCell *cell = [JLContactsCell cellWithTableView:tableView];
cell.contact = self.contactsArray[indexPath.row];
return cell;
}
#pragma mark - UITableView的代理方法
/**
* 如果实现了这个方法,就自动实现了滑动删除的功能
* 点击了删除按钮就会调用
* 提交了一个编辑操作就会调用(操作:删除\添加)
* @param editingStyle 编辑的行为
* @param indexPath 操作的行号
*/
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
if (editingStyle == UITableViewCellEditingStyleDelete) { // 提交的是删除操作
// 1.删除模型数据
[self.contactsArray removeObjectAtIndex:indexPath.row];
// 2.刷新表格
// 局部刷新某些行
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];
// 3.归档
[NSKeyedArchiver archiveRootObject:self.contactsArray toFile:JLContactsDataFilePath];
}
}
/**
* 左上角的"注销"按钮
*/
- (IBAction)loginOut:(UIBarButtonItem *)sender {
// 弹框
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"确定要注销?" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:@"确定" otherButtonTitles:nil, nil];
[sheet showInView:self.view];
}
#pragma mark -UIActionSheet的代理方法
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex != 0) return;
[self.navigationController popViewControllerAnimated:YES];
}
#pragma mark -执行跳转之前会调用
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
id vc = segue.destinationViewController;
if ([vc isKindOfClass:[JLAddViewController class]]) {
// 设置JLAddViewController的代理
JLAddViewController *addVc = vc;
addVc.delegate = self;
} else if ([vc isKindOfClass:[JLEditViewController class]]){
JLEditViewController *editVc = vc;
// 获取当前选中了哪一行
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
editVc.contact = self.contactsArray[indexPath.row];
editVc.delegate = self;
}
}
#pragma mark -JLAddViewController的代理方法
- (void)addViewController:(JLAddViewController *)addViewController didAddContact:(JLContact *)contact{
// 添加模型数据
[self.contactsArray addObject:contact];
// 刷新列表
[self.tableView reloadData];
// 归档(存储)数组
[NSKeyedArchiver archiveRootObject:self.contactsArray toFile:JLContactsDataFilePath];
}
#pragma mark -JLEditViewController的代理方法
- (void)editViewController:(JLEditViewController *)editVc didSaveContact:(JLContact *)contact{
[self.tableView reloadData];
// 归档(存储)数组
[NSKeyedArchiver archiveRootObject:self.contactsArray toFile:JLContactsDataFilePath];
}
@end
//
// JLAddViewController.h
// 01-私人通讯录
//
// Created by XinYou on 15-3-13.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import
@class JLAddViewController, JLContact;
@protocol JLAddViewControllerDelegate
@optional
- (void)addViewController:(JLAddViewController *)addViewController didAddContact:(JLContact *)contact;
@end
@interface JLAddViewController : UIViewController
@property (nonatomic, weak)id
delegate;
@end
//
// JLAddViewController.m
// 01-私人通讯录
//
// Created by XinYou on 15-3-13.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import "JLAddViewController.h"
#import "JLContact.h"
@interface JLAddViewController ()
/**
* "姓名"输入框
*/
@property (weak, nonatomic) IBOutlet UITextField *nameField;
/**
* "电话"输入框
*/
@property (weak, nonatomic) IBOutlet UITextField *telNumberField;
/**
* "添加"按钮
*/
@property (weak, nonatomic) IBOutlet UIButton *addBtn;
/**
* "添加"按钮被点击
*/
- (IBAction)addBtnClick;
@end
@implementation JLAddViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// 监听“姓名”输入框的文本改变。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.nameField];
// 监听“电话”输入框的文本改变
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.telNumberField];
// 下面这句代码应该放到viewDidAppear方法中
// [self.nameField becomeFirstResponder];
}
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
// 让“姓名”输入框成为第一响应者,在view完全显示的时候自动弹出键盘。
// 如果把这句代码放到viewDidLoad方法中,会出现view还未完全显示,键盘却已经弹出
[self.nameField becomeFirstResponder];
}
- (void)dealloc{
// 一定要记得移除对通知的监听
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)textChange{
// 只有当“姓名”输入框和“电话”输入框都有文字的时候,“添加”按钮才可用
self.addBtn.enabled = (self.nameField.text.length && self.telNumberField.text.length);
}
/**
* "添加"按钮被点击
*/
- (IBAction)addBtnClick {
// 关闭当前控制器
[self.navigationController popViewControllerAnimated:YES];
// 通过代理传递数据给上一个控制器
if ([self.delegate respondsToSelector:@selector(addViewController:didAddContact:)]) {
JLContact *contact = [[JLContact alloc] init];
contact.name = self.nameField.text;
contact.telNumber = self.telNumberField.text;
[self.delegate addViewController:self didAddContact:contact];
}
}
@end
//
// JLEditViewController.h
// 01-私人通讯录
//
// Created by XinYou on 15-3-14.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import
@class JLContact, JLEditViewController;
@protocol JLEditViewControllerDelegate
@optional
- (void)editViewController:(JLEditViewController *)editVc didSaveContact:(JLContact *)contact;
@end
@interface JLEditViewController : UIViewController
@property (nonatomic, strong)JLContact *contact;
@property (nonatomic, weak)id
delegate;
@end
//
// JLEditViewController.m
// 01-私人通讯录
//
// Created by XinYou on 15-3-14.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import "JLEditViewController.h"
#import "JLContact.h"
@interface JLEditViewController ()
/**
* "姓名"输入框
*/
@property (weak, nonatomic) IBOutlet UITextField *nameField;
/**
* "电话"输入框
*/
@property (weak, nonatomic) IBOutlet UITextField *telNumField;
/**
* "保存"按钮
*/
@property (weak, nonatomic) IBOutlet UIButton *saveBtn;
/**
* 导航条右上角的“编辑”按钮
*/
@property (weak, nonatomic) IBOutlet UIBarButtonItem *editBtn;
/**
* "保存"按钮被点击
*/
- (IBAction)saveBtnClick;
/**
* 导航条右上角的“编辑”按钮被点击
*/
- (IBAction)editBtnClick:(UIBarButtonItem *)sender;
@end
@implementation JLEditViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// 为什么是在这里设置控件显示的内容而不是在setContact方法中设置呢?
// 在JLContactViewController的prepareForSegue方法中我们调用了JLEditViewController的setContact方法
// 但是prepareForSegue是控制器跳转之前执行的方法,也就是说此时还未跳转到JLEditViewController,而控制器的view是延时加载的。
// 也就是说在JLContactViewController的prepareForSegue中调用JLEditViewController的setContact方法的时候,JLEditViewController的view都还没有存在,
// 如果view都不存在,那么view中的子控件自然不存在,所以在setContact方法中设置子控件的内容是没有意义的。
self.nameField.text = self.contact.name;
self.telNumField.text = self.contact.telNumber;
//
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.nameField];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.telNumField];
}
//- (void)setContact:(JLContact *)contact{
// self.nameField.text = self.contact.name;
// self.telNumField.text = self.contact.telNumber;
//}
- (void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)textChange{
self.saveBtn.enabled = (self.nameField.text.length && self.telNumField.text.length);
}
- (IBAction)saveBtnClick {
// 移除当前控制器
[self.navigationController popViewControllerAnimated:YES];
if ([self.delegate respondsToSelector:@selector(editViewController:didSaveContact:)]) {
// 更新模型数据
self.contact.name = self.nameField.text;
self.contact.telNumber = self.telNumField.text;
// 通知代理
[self.delegate editViewController:self didSaveContact:self.contact];
}
}
- (IBAction)editBtnClick:(UIBarButtonItem *)sender {
if (self.nameField.enabled) {// 输入框可用,表示当前处于编辑状态,导航条右上角的按钮显示的是“取消”
self.nameField.enabled = NO;
self.telNumField.enabled = NO;
self.editBtn.title = @"编辑";
self.saveBtn.hidden = YES;
// 隐藏键盘
[self.view endEditing:YES];
// 用户点击“取消”,意思是取消了编辑,这时需要还原数据
self.nameField.text = self.contact.name;
self.telNumField.text = self.contact.telNumber;
}else{//输入框不可用,表示当前处于查看状态,导航条右上角的按钮显示的是“编辑”
self.nameField.enabled = YES;
self.telNumField.enabled = YES;
self.editBtn.title = @"取消";
self.saveBtn.hidden = NO;
// 一般情况下,编辑联系人的时候修改的是电话号码
// 这里让“电话”输入框成为第一响应者,弹出键盘
[self.telNumField becomeFirstResponder];
}
}
@end
//
// JLContact.h
// 01-私人通讯录
//
// Created by XinYou on 15-3-13.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import
@interface JLContact : NSObject
@property (nonatomic, copy)NSString *name;
@property (nonatomic, copy)NSString *telNumber;
@end
//
// JLContact.m
// 01-私人通讯录
//
// Created by XinYou on 15-3-13.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import "JLContact.h"
@implementation JLContact
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeObject:self.telNumber forKey:@"telNumber"];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.telNumber = [aDecoder decodeObjectForKey:@"telNumber"];
}
return self;
}
@end
//
// JLContactsCell.h
// 01-私人通讯录
//
// Created by XinYou on 15-3-14.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import
@class JLContact;
@interface JLContactsCell : UITableViewCell
+ (instancetype)cellWithTableView:(UITableView *)tableView;
@property (nonatomic, strong)JLContact *contact;
@end
//
// JLContactsCell.m
// 01-私人通讯录
//
// Created by XinYou on 15-3-14.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import "JLContactsCell.h"
#import "JLContact.h"
@interface JLContactsCell()
/**
* cell之间的分隔线
*/
@property (nonatomic, weak)UIView *divider;
@end
@implementation JLContactsCell
+ (instancetype)cellWithTableView:(UITableView *)tableView{
static NSString *ID = @"contact";
// 先从缓存池中取,如果缓存池中没有可循环利用的cell,先去storyboard中找到合适的cell
// cell是从storyboard中创建出来的
JLContactsCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
return cell;
}
- (void)setContact:(JLContact *)contact{
// 固定写法
_contact = contact;
self.textLabel.text = contact.name;
self.detailTextLabel.text = contact.telNumber;
}
/**
* 如果cell是通过storyboard或者xib创建的,就不可能会调用这个方法来初始化cell
* 如果cell是通过手写代码创建,才会调用这个方法来初始化cell
*/
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
/**
* 如果cell是通过storyboard或者xib创建的,就会调用这个方法来初始化cell
* 这个方法的作用类似于init方法
*/
- (void)awakeFromNib
{
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor blackColor];
view.alpha = 0.2;
[self.contentView addSubview:view];
self.divider = view;
}
/**
* 在这个方法中设置子控件的frame,因为awakeFromNib(或者init)方法中获取到的cell的frame是不正确的。
*/
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat dividerX = 10;
CGFloat dividerH = 1;
CGFloat dividerY = self.frame.size.height - dividerH;
CGFloat dividerW = self.frame.size.width;
self.divider.frame = CGRectMake(dividerX, dividerY, dividerW, dividerH);
}
@end