美文网首页
12-UI进阶(私人通讯录和主流框架)

12-UI进阶(私人通讯录和主流框架)

作者: 木喳喳的夏天 | 来源:发表于2016-03-21 15:09 被阅读102次

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的使用
    1. 点击保存,通知联系人刷新表格,可以使用代理,也可以使用block
    • block相当于小弟,代理相当于打电话
    • 使用block先把刷新表格的代码保存起来,等用户点击保存按钮的时候,调用block

通讯录中使用block进行逆传

  1. 编辑联系人后保存
  • 首先在编辑联系人的控制器EditViewController.h)中,定义block的类型别名,并声明一个该类型的block
// 定义block类型别名
typedef void(^XMGEditViewControllerBlock)();

@property (nonatomic, strong)XMGEditViewControllerBlock block;
  • 在编辑联系人的控制器(EditViewController.m)中,需要使用block的地方调用block即可
  • 在联系人控制器(ContactViewController.m)中,实现block中的代码
  • 添加联系人后保存
    • 大概步骤和上述类似,区别就是此处需要传递数据模型,所以在定义block的时候添加相应参数即可

iOS应用数据存储的常用方式

  1. XML属性列表(plist)归档
  • Preference(偏好设置)
  • NSKeyedArchiver归档(NSCoding)
  • SQLite3
  • Core Data

Plist存储

  • Plist存储的注意事项:不能存储自定义对象,否则无法保存要生成的plist文件

  • Plist可以存放数组(NSArray)和字典(NSDictionary),如何判断一个对象能不能使用plist存储,就看看有没有writeToFile的方法

  • 使用Plist存储的步骤:

    1. 获取应用的文件夹(文件沙盒)
      // 获取应用的文件夹(应用沙盒)
      // 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偏好设置存储

  • 优点:
    1. 不需要关心文件名,默认为“项目名.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;

相关文章

网友评论

      本文标题:12-UI进阶(私人通讯录和主流框架)

      本文链接:https://www.haomeiwen.com/subject/jfmklttx.html