UINavigationController是一个导航控制器,它用来组织有层次关系的视图,在UINavigationController中子控制器以栈的形式存储,只有在栈顶的控制器能够显示在界面中,一旦一个子控制器出栈则会被销毁。UINavigationController默认也不会显示任何视图(这个控制器自身的UIView不会显示),它必须有一个根控制器rootViewController,而且这个根控制器不会像其他子控制器一样被销毁。
导航栏的内容由栈顶控制器的navigationItem属性决定
UINavigationItem有以下属性影响着导航栏的内容
左上角的返回按钮
@property(nonatomic,retain) UIBarButtonItem *backBarButtonItem;
中间的标题视图
@property(nonatomic,retain) UIView *titleView;
中间的标题文字
@property(nonatomic,copy) NSString *title;
左上角的视图
@property(nonatomic,retain) UIBarButtonItem *leftBarButtonItem;
UIBarButtonItem *rightBarButtonItem 右上角的视图
@property(nonatomic,retain) UIBarButtonItem *rightBarButtonItem;
导航栏的内容由栈顶控制器的navigationItem属性决定
UINavigationItem有以下属性影响着导航栏的内容
左上角的返回按钮
@property(nonatomic,retain) UIBarButtonItem *backBarButtonItem;
中间的标题视图
@property(nonatomic,retain) UIView *titleView;
中间的标题文字
@property(nonatomic,copy) NSString *title;
左上角的视图
@property(nonatomic,retain) UIBarButtonItem *leftBarButtonItem;
UIBarButtonItem *rightBarButtonItem 右上角的视图
@property(nonatomic,retain) UIBarButtonItem *rightBarButtonItem;
1.当文本框现实的内容改变时 调用通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.accountField];
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
2.让登陆按钮在文本框无信息的时候无效化
/**
* 文本框的文字发生改变的时候调用
*/
- (void)textChange
{
self.loginBtn.enabled = (self.accountField.text.length && self.pwdField.text.length);
}
/**
* 记住密码开关的状态改变就会调用
*/
- (IBAction)rmbPwdChange {
// 取消了记住密码
if (self.rmbPwdSwitch.isOn == NO) {
[self.autoLoginSwitch setOn:NO animated:YES];
}
}
/**
* 自动登录的状态改变就会调用
*/
- (IBAction)autoLoginChange {
if (self.autoLoginSwitch.isOn) {
[self.rmbPwdSwitch setOn:YES animated:YES];
}
}
3.登陆的确认 Storyboard上每一根用来界面跳转的线,都是一个UIStoryboardSegue对象(简称Segue)
每一个Segue对象,都有3个属性
唯一标识
@property (nonatomic, readonly) NSString *identifier;
来源控制器
@property (nonatomic, readonly) id sourceViewController;
目标控制器
@property (nonatomic, readonly) id destinationViewController;
- (IBAction)login { // 帐号:mj 密码:123
if (![self.accountField.text isEqualToString:@"mj"]) {
// 帐号不存在
[MBProgressHUD showError:@"帐号不存在"];
return;
}
if (![self.pwdField.text isEqualToString:@"123"]) {
// 密码错误
[MBProgressHUD showError:@"密码错误"];
return;
}
// 显示一个蒙版(遮盖)
[MBProgressHUD showMessage:@"哥正在帮你登录中...."];
// 发送网络请求
// 模拟(2秒后执行跳转)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 移除遮盖
[MBProgressHUD hideHUD];
// 跳转 -- 执行login2contacts这个segue
[self performSegueWithIdentifier:@"login2contacts" sender:nil];
});
}
注销界面 当你点击注销的时候下方弹框(uiactionsheet)提示是否注销
用pop方法返回到上一控制器
- (IBAction)logout:(id)sender {
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"确定要注销?" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:@"确定" otherButtonTitles:nil, nil];
[sheet showInView:self.view];
}
#pragma mark - actionsheet的代理方法
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex != 0) return;
[self.navigationController popViewControllerAnimated:YES];
}
添加界面
—————————————————————代理可以解除耦合性—————————————————————————
设置代理将数据传给上一个控制器 这里小提示还有个就是创建一个数据模型将要写的数据都封装在里面
@class MJAddViewController ,MJContact;
@protocol MJAddViewControllerDelegate <NSObject>
@optional
-(void)addViewController:(MJAddViewController *)addVC didAddContact:(MJContact *)contact;
@end
@interface MJAddViewController : UIViewController
@property (nonatomic, weak) id<MJAddViewControllerDelegate> delegate;
#import <Foundation/Foundation.h>
@interface MJContact : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *phone;
@end
传递模型数据给上一个控制器
- (IBAction)add {
// 1.关闭当前控制器
[self.navigationController popViewControllerAnimated:YES];
if ([self.delegate respondsToSelector:@selector(addViewController:didAddContact:)]) {
MJContact *contact = [[MJContact alloc] init];
contact.name = self.nameField.text;
contact.phone = self.phoneField.text;
[self.delegate addViewController:self didAddContact:contact];
}
}
在上一个控制器中创建模型
- (NSMutableArray *)contacts
{
if (_contacts == nil) {
_contacts = [NSMutableArray array];
}
return _contacts;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 1.创建cell
static NSString *ID = @"contact";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:ID];
}
// // 2.设置cell的数据
MJContact *contact = self.contacts[indexPath.row];
cell.textLabel.text = contact.name;
cell.detailTextLabel.text = contact.phone;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
设置代理 利用代理把上一个数据传递到当前的控制器
@class MJContact,MJAddViewController;
@protocol MJAddViewControllerdelagate<NSObject>
@optional
- (void)addViewController:(MJAddViewController *)addVc didAddContact:(MJContact *)contact;
@end
@interface MJAddViewController : UIViewController
@property(nonatomic ,weak)id <MJAddViewControllerdelagate> delegate;
@end
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// 设置下一个控制器(添加联系人的控制器)的代理
MJAddViewController *addVc = segue.destinationViewController;
addVc.delegate = self;
}
#pragma mark - MJAddViewController的代理方法
- (void)addViewController:(MJAddViewController *)addVc didAddContact:(MJContact *)contact
{
// 1.添加模型数据
[self.contacts addObject:contact];
// 2.刷新表格
[self.tableView reloadData];
}
添加完之后,增加修改界面
点击联系人那行进入修改界面。这里涉及到数据的顺传
在联系人控制器中,在准备跳转的时候 将模型传出
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
id vc = segue.destinationViewController ;
MJEditViewController *edit = vc;
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
edit.contact = self.contacts[path.row];
}
}
在编辑的控制器里面didload中拿到数据模型。
self.nameField.text = self.contact.name;
self.phoneField.text = self.contact.phone;
编辑按钮 点击可以编辑 字变成取消 如果点了取消文本不变
- (IBAction)edit:(UIBarButtonItem *)sender {
if (self.nameField.enabled) { // 点击的是"取消"
self.nameField.enabled = NO;
self.phoneField.enabled = NO;
[self.view endEditing:YES];
self.saveBtn.hidden = YES;
sender.title = @"编辑";
// 还原回原来的数据
self.nameField.text = self.contact.name;
self.phoneField.text = self.contact.phone;
} else { // 点击的是"编辑"
self.nameField.enabled = YES;
self.phoneField.enabled = YES;
[self.phoneField becomeFirstResponder];
self.saveBtn.hidden = NO;
sender.title = @"取消";
}
}
点击保存之后跳回原来的界面 又是设置代理 简单带过 主要提醒的是 这次从联系人控制器跳转的时候会有2根线 所以要判断下一个控制器是哪一个
#import <UIKit/UIKit.h>
@class MJContact,MJEditViewController;
@protocol MJEditViewControllerdelegate <NSObject>
@optional
-(void)editViewController:(MJEditViewController *)editController DidSaveContact:(MJContact *)contact;
@end
@interface MJEditViewController : UIViewController
@property (nonatomic, strong) MJContact *contact;
@property (nonatomic ,weak)id <MJEditViewControllerdelegate> delegate;
@end
- (IBAction)save {
[self.navigationController popViewControllerAnimated:YES];
if ([self.delegate respondsToSelector:@selector(editViewController:DidSaveContact:)]) {
// MJContact *contact = [[MJContact alloc]init];
self.contact.name = self.nameField.text;
self.contact.phone = self.phoneField.text;
[self.delegate editViewController:self DidSaveContact:self.contact];
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
id vc = segue.destinationViewController ;
if ([vc isKindOfClass:[MJAddViewController class]]) {
MJAddViewController *add = vc;
add.delegate = self;
} else if ([vc isKindOfClass:[MJEditViewController class]] )
{
MJEditViewController *edit = vc;
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
edit.contact = self.contacts[path.row];
edit.delegate = self;
}
}
-(void)editViewController:(MJEditViewController *)editController DidSaveContact:(MJContact *)contact
{
// [self.contacts addObject:contact];
[self.tableView reloadData];
}
设置tableview 的分割线样式无
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
自定义分割线 顺便将控制器解放
/**
* 如果cell是通过storyboard或者xib创建的,就会调用这个方法来初始化cell
* 这个方法的作用类似于init方法
*/
- (void)awakeFromNib
{
// Initialization code
UIView *divider = [[UIView alloc] init];
divider.backgroundColor = [UIColor blackColor];
divider.alpha = 0.2;
[self.contentView addSubview:divider];
self.divider = divider;
}
/**
* 在这个方法中设置子控件的frame
*/
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat dividerX = 0;
CGFloat dividerH = 1;
CGFloat dividerY = self.frame.size.height - dividerH;
CGFloat dividerW = self.frame.size.width;
self.divider.frame = CGRectMake(dividerX, dividerY, dividerW, dividerH);
}
- (void)setContact:(MJContact *)contact
{
_contact = contact;
self.textLabel.text = contact.name;
self.detailTextLabel.text = contact.phone;
}
添加删除 这是tableviewdelegate自带的功能 只要实现协议的方法 就可以往左滑动出现删除
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.contacts removeObjectAtIndex:indexPath.row];
[self.tableView reloadData];
}
}
数据的归档
获取文件的路径
// 文件路径
#define MJContactsFilepath [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"contacts.data"]
[NSKeyedArchiver archiveRootObject:self.contacts toFile:MJContactsFilepath ];
这里要注意的是 归档的时候 要先在模型中遵循<NSCoding> 然后将要读取的和写入的属性 写成变成key
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.name forKey:@"name" ];
[aCoder encodeObject:self.phone forKey:@"phone"];
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init]) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.phone = [aDecoder decodeObjectForKey:@"phone"];
}
return self;
}
归档写在数据的增加,修改和删除的时候。