TableView的点击处理
- 要想监听TableView的点击,就需要实现TableView的代理,实现didSelectRowAtIndexPath方法
通过代码的方式搭建编辑界面
- 首先在storyboard中添加新的控制器视图,并添加identifier标识
- 然后在要进行点击跳转的TableView中实现代理方法,从而对cell的点击进行监听
- 实现代理的didSelectRowAtIndexPath方法
#pragma mark - tableView代理方法
// 点击cell的时候调用
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// 加载storyboard
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
// 创建编辑控制器
XMGEditViewController *editVc = [storyboard instantiateViewControllerWithIdentifier:@"edit"];
// 给控制器传递模型数据
editVc.contact = self.contacts[indexPath.row];
// 跳转到编辑界面
[self.navigationController pushViewController:editVc animated:YES];
}
- 注意:控制器之间的传值,一定要注意控制器的子控件有没有加载,一定要在子控价加载完成的时候才去给子控件赋值,否则程序运行时就会产生错误,一般在viewDidLoad方法中给控件赋值,或者在其之后的生命周期的方法中进行赋值
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// 设置导航条的标题
self.title = @"查看/编辑界面";
// 设置导航条右边的按钮
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"编辑" style:UIBarButtonItemStyleDone target:self action:@selector(edit:)];
// 给文本框
_nameField.text = _contact.name;
_phoneField.text = _contact.phone;
// 给文本框添加监听器,及时监听文本框内容的改变
[_nameField addTarget:self action:@selector(textChange) forControlEvents:UIControlEventEditingChanged];
[_phoneField addTarget:self action:@selector(textChange) forControlEvents:UIControlEventEditingChanged];
// 判断下保存按钮能否点击
[self textChange];
}
// 任一一个文本框的内容改变都会调用
- (void)textChange
{
_saveBtn.enabled = _nameField.text.length && _phoneField.text.length;
}
保存功能
- 点击保存的时候应该修改数据模型,并返回到上一个控制器
- 点击取消的时候,应该还原数据,未修改的数据仍然在模型数据中
// 还原数据
_nameField.text = _contact.name;
_phoneField.text = _contact.phone;
block讲解
- 声明block的时候要使用strong修饰,并且block不是对象,所以在声明的时候不能加*
- 自定义block类型
// name为block的类型别名,params为block中需要的参数
// block的类型名应该以类名开头,后面加上Block
typedef void(^name)(params);
@property (nonatomic, strong) MyBlock myBlock1;
- 使用inlineBlock快速定义block
// inline
// blockName:block变量名
returnType(^blockName)(parameterTypes) = ^(parameters) {
statements
};
- block的作用:跟函数和方法很像,其实就是用来保存一段代码,等到恰当的时候再去使用
- 什么时候使用block
- 逆传:用block来传值
- 处理网络请求的时候经常使用block封装代码,因为请求网络数据有延迟,所以需要先将展示到控件的代码保存到block中,等请求到数据的时候直接调用block
- 通讯录block的使用
- 点击保存,通知联系人刷新表格,可以使用代理,也可以使用block
- block相当于小弟,代理相当于打电话
- 使用block先把刷新表格的代码保存起来,等用户点击保存按钮的时候,调用block
通讯录中使用block进行逆传
- 编辑联系人后保存
- 首先在编辑联系人的控制器EditViewController.h)中,定义block的类型别名,并声明一个该类型的block
// 定义block类型别名
typedef void(^XMGEditViewControllerBlock)();
@property (nonatomic, strong)XMGEditViewControllerBlock block;
- 在编辑联系人的控制器(EditViewController.m)中,需要使用block的地方调用block即可
- 在联系人控制器(ContactViewController.m)中,实现block中的代码
- 添加联系人后保存
- 大概步骤和上述类似,区别就是此处需要传递数据模型,所以在定义block的时候添加相应参数即可
iOS应用数据存储的常用方式
- XML属性列表(plist)归档
- Preference(偏好设置)
- NSKeyedArchiver归档(NSCoding)
- SQLite3
- Core Data
Plist存储
-
Plist存储的注意事项:不能存储自定义对象,否则无法保存要生成的plist文件
-
Plist可以存放数组(NSArray)和字典(NSDictionary),如何判断一个对象能不能使用plist存储,就看看有没有writeToFile的方法
-
使用Plist存储的步骤:
- 获取应用的文件夹(文件沙盒)
// 获取应用的文件夹(应用沙盒) // NSString *homePath = NSHomeDirectory(); // 获取temp // NSTemporaryDirectory(); // 获取Cache文件路径 // NSSearchPathDirectory:搜索的目录 // NSSearchPathDomainMask:搜索范围 NSUserDomainMask:表示在用户的手机上查找 // expandTilde 是否展开全路径,如果没有展开,应用的沙盒路径就是~ // 存储一定要要展开路径 NSString *cachePaht = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; // 拼接文件名 NSString *filePath = [cachePaht stringByAppendingPathComponent:@"personArr.plist"]; NSLog(@"%@",cachePaht); // File:文件的全路径 [arr writeToFile:filePath atomically:YES];
-
使用Plist读取的步骤:
- 获取要读取文件的全路径
NSString *cachePaht = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; // 拼接文件名 NSString *filePath = [cachePaht stringByAppendingPathComponent:@"arr.plist"];
- 将plist文件转换成对应的数组
NSArray *arr = [NSArray arrayWithContentsOfFile:filePath];
Preference偏好设置存储
- 优点:
- 不需要关心文件名,默认为“项目名.plist”
- 可以快速做键值对存储
- 底层:就是封装了一个字典
- 举例:
// 保存数据
- (IBAction)save:(id)sender {
// NSUserDefaults为单例,即程序运行中只分配一块内存
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:@"xmg" forKey:@"account"];
[userDefaults setObject:@"123" forKey:@"pwd"];
[userDefaults setBool:YES forKey:@"rmbPwd"];
// 在iOS7之前,默认不会马上把跟硬盘同步
// 同步
[userDefaults synchronize];
}
// 读取数据
- (IBAction)read:(id)sender {
NSString *pwd = [[NSUserDefaults standardUserDefaults] objectForKey:@"pwd"];
NSLog(@"%@",pwd);
}
storyboard中快速复制控件的方法:command + d
自定义对象归档
- 首先创建一个对象,然后拿到要保存文件的全路径,调用archiveRootObjcet方法将对象保存到文件中
- (IBAction)save:(id)sender {
Person *p = [[Person alloc] init];
p.age = 18;
// 获取cache
NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
// 获取文件的全路径
NSString *filePath = [cachePath stringByAppendingPathComponent:@"person.data"];
// 把自定义对象归档
[NSKeyedArchiver archiveRootObject:p toFile:filePath];
}
- 如果一个自定义对象想要归档,必须遵守NSCoding协议,实现协议的encodeWithCoder:方法
// 什么时候调用:自定义对象归档的时候
// 作用:用来描述当前对象里面的哪些属性需要归档
- (void)encodeWithCoder:(NSCoder *)aCoder
{
// name
[aCoder encodeObject:_name forKey:@"name"];
// age
[aCoder encodeInt:_age forKey:@"age"];
}
自定义对象解档
- 首先要获取要解档文件的全路径,然后调用unarchiveObjectWithFile方法
- (IBAction)read:(id)sender {
// 获取cache
NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
// 获取文件的全路径
NSString *filePath = [cachePath stringByAppendingPathComponent:@"person.data"];
// 解档
Person *p = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"%d",p.age);
}
- 要解档文件为一个对象,需要实现initWithCoder方法
// 什么时候调用:解档对象的时候调用
// 作用:用来描述当前对象里面的哪些属性需要解档
// initWithCoder:就是用来解析文件的
- (id)initWithCoder:(NSCoder *)aDecoder
{
// super:NSObject
#warning 什么时候需要调用initWithCoder
if (self = [super init]) {
// 注意:一定要给成员变量赋值
// name
_name = [aDecoder decodeObjectForKey:@"name"];
// age
_age = [aDecoder decodeIntForKey:@"age"];
}
return self;
}
什么时候调用initWithCoder和initWithFrame
-
什么时候调用initWithCoder
- 解析文件的时候都会调用这个方法
// 解析文件都会调用这个方法,通过xib或storyboard的方式等 - (id)initWithCoder:(NSCoder *)aDecoder { // 只要父类遵守了NSCoding,就调用initWithCoder,UIView是遵守该协议的 // 先初始化父类 if (self = [super initWithCoder:aDecoder]) { NSLog(@"%s",__func__); } return self; }
-
什么时候调用initWithFrame
- 通过代码初始化的时候,会调用init方法,底层就会调用initWithFrame方法
// 通过代码初始化的时候,调用init方法,底层就会调用initWithFrame - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { NSLog(@"%s",__func__); } return self; }
主流框架的搭建
- 在iOS7之后,导航控制器下所有UIScrollView顶部都会添加额外的滚动区域(64)
- 注意:如果使用静态单元格,tableView的组数和行数的总数已经固定死,不能超过静态单元格
- 使用代码隐藏TabBar
self.hidesBottomBarWhenPushed = YES;
网友评论