• [iOS基础控件


    A.需求
    1.搭建一个“私人通讯录”Demo
    2.模拟登陆界面
    • 账号
    • 密码
    • 记住密码开关
    • 自动登陆开关
    • 登陆按钮
    3.退出注销
    4.增删改查
    5.恢复数据(取消修改)
     
    这个代码托管了在github: https://github.com/hellovoidworld/ContactBook
    (本来网速不好,打算用国内的csdn甚至京东,发现github有现成的app,不用使用命令行,速度还可以,就用github了,界面也是最漂亮的)
     
    Image
     
    B.基本架构
    1. 5个控制器
    (1)导航控制器 NavigationController
    (2)登陆 UIViewController
    • 输入账号密码
    • 记住密码、自动登录开关
    • 登陆跳转按钮
    (3)联系人列表 TableViewController
    • 注销功能
    • 添加联系人跳转按钮
    (4)添加联系人 UIView
    (5)查看、编辑 UIView
     
    Image
     
    C.实现步骤
    1.搭建
    (1)使用NavigationController作为主控制器
    (2)使用storyboard构建UI
    (3)每个控制器搭配一个相应的类,用以处理各种事件
     
    2.登陆界面
    (1)账号、密码的输入框
    a.使用TextField
    Image(232)
     
     
    b.监听输入事件
    <1> 不能使用addTarget方法,因为addTarget只能监听点击事件,不能监听编辑事件
    <2> 不能使用代理进行事件监听,代理就是控制器,但是只能监听到开始、结束编辑、允许编辑文字等状态
    <3> 使用通知,就能监听到正在编辑的状态了,监听文字的改变
    由通知中心转发账号、密码输入框的文字编辑状态信息到控制器,调用指定方法
     1 /* 登陆按钮监听账号、密码输入框的通知
     2 * 只有当两者都有内容的时候才能激活登陆按钮
     3 */
     4 - (void) loginButtonListening{
     5     // 监听账号输入
     6     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.accountText];
     7    
     8     // 监听密码输入
     9     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.pwdText];
    10 }
    11 
    12 // 记得要在自身被销毁的时候取消消息订阅
    13 - (void)dealloc {
    14     [[NSNotificationCenter defaultCenter] removeObserver:self];
    15 }
    16 
    17 // 登陆按钮监听的触发事件
    18 - (void) textChange {
    19     // 只有当账号、密码不为空的时候,才能使用登陆按钮
    20     self.loginButton.enabled = self.accountText.text.length && self.pwdText.text.length;
    21 }
     
    (2)登陆
    当有账号、密码的时候才能激活登陆按钮
    a.验证账号、密码
    使用“登陆界面”控制器拖线到“联系人列表”界面控制器:使用Manual Segue
    /** 点击登陆 */
    - (IBAction)login {
        // 检测账号
        if (![self.accountText.text isEqualToString:@"hw"]) {
            NSLog(@"账号不存在");
            return;
        }
       
        // 检测密码
        if (![self.pwdText.text isEqualToString:@"123"]) {
            NSLog(@"密码不正确");
            return;
        }
       
        // 根据Segue ID 执行跳转
        [self performSegueWithIdentifier:@"contactList" sender:nil];
    }
     
    Image(233)
     
    b.使用第三方包实现登陆效果
    Image(234)
     
    c.在登陆时,使用一个遮盖禁止用户操作
     1 /** 点击登陆 */
     2 - (IBAction)login {
     3     // 检测账号
     4     if (![self.accountText.text isEqualToString:@"hw"]) {
     5         [MBProgressHUD showError:@"账号不存在"];
     6         return;
     7     }
     8    
     9     // 检测密码
    10     if (![self.pwdText.text isEqualToString:@"123"]) {
    11         [MBProgressHUD showError:@"密码错误"];
    12         return;
    13     }
    14    
    15     // 登陆中,遮盖屏幕,禁止用户进行其他操作
    16     [MBProgressHUD showMessage:@"正在使劲登录中..."];
    17    
    18     // 模拟登陆过程,延迟跳转
    19     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    20         // 移除遮盖
    21         [MBProgressHUD hideHUD];
    22        
    23         // 根据Segue ID 执行跳转
    24         [self performSegueWithIdentifier:@"contactList" sender:nil];
    25     });
    26 }
     
     
    Image(235)
     
    d. 使用performSegueWithIndentifier:方法执行指定Segue
    1         // 根据Segue ID 执行跳转
    2         [self performSegueWithIdentifier:@"contactList" sender:nil];
     
    (3)记住密码 & 自动登陆
    a.联动
    <1> 取消记住密码,自动取消自动登陆
    <2> 勾选了自动登陆,自动勾选记住密码
    (使用storyboard拖线也可以完成)
     1 /** 点击记住密码 */
     2 - (IBAction)keepPwdSwitch {
     3     if (!self.keepPwd.isOn) {
     4         [self.autoLogin setOn:NO];
     5     }
     6 }
     7 
     8 /** 点击自动登陆 */
     9 - (IBAction)autoLoginSwitch {
    10     if (self.autoLogin.isOn) {
    11         [self.keepPwd setOn:YES];
    12     }
    13 }
     
    (4)在跳转生效之前,传输数据,设置“联系人列表”标题
    使用来源控制器的“prepareForSegue: sender:”方法(需要自行实现)
    拿到目标控制器,设置数据
     
     
     
    3.联系人列表
    (1)注销
    a.注销按钮及其功能
    Image(236)
     
    b.提醒警告
    使用UIActionSheet控件(这是一个从下往上弹出的底部弹窗控件)
    控制器遵守 UIActionSheetDelegate 协议
     1 /** 点击注销 */
     2 - (IBAction)logout:(UIBarButtonItem *)sender {
     3     UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"确定要注销?" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:@"确定" otherButtonTitles:nil, nil];
     4    
     5     [sheet showInView:self.view];
     6 }
     7 
     8 #pragma mark - ActionSheet delegate function
     9 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
    10     if (buttonIndex != 0) return;
    11    
    12     // 弹出一个栈顶控制器,即本控制器,回到上一页
    13     [self.navigationController popViewControllerAnimated:YES];
    14 }
     
    (2)添加联系人跳转
    a.在右上角添加 “+” 按钮
    3B2B7A98-AA39-453F-9DFE-C17A58BB5226
     
    b.添加一个“添加联系人”控制器和界面
    (下文)
     
    4.添加联系人
    (1)基础搭建
    a.在“联系人列表”界面点击右上角加号跳转,使用自动Segue就可以了
    Image(237)
     
    b.添加姓名、电话输入框
    c."添加"按钮
     
    Image(238)
     
     
    d.设置监听事件(姓名、电话和“添加”按钮的监听、事件处理,类似账号、密码和“登陆”按钮)
     1 - (void)viewDidLoad {
     2     [super viewDidLoad];
     3     // Do any additional setup after loading the view.
     4    
     5     // 设置“添加”按钮的激活状态
     6     self.addButton.enabled = NO;
     7     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.nameText];
     8     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.phoneText];
     9 }
    10  
    11 - (void)dealloc {
    12     // 取消订阅消息监听
    13     [[NSNotificationCenter defaultCenter] removeObserver:self];
    14 }
    15 
    16 /** 姓名、电话文本编辑消息处理 
    17 * 只有当姓名、电话不为空的时候才能使用“添加”按钮
    18 */
    19 - (void) textChange {
    20     self.addButton.enabled = self.nameText.text.length && self.phoneText.text.length;
    21 }
     
    e.进入即自动弹出键盘
    1 // 等界面完全显示完毕,再弹出键盘
    2 - (void)viewDidAppear:(BOOL)animated {
    3     [super viewDidAppear:animated];
    4    
    5     // 将焦点放在“姓名”输入框
    6     [self.nameText becomeFirstResponder];
    7 }
     
    (2)数据返传
    点击“添加”按钮,把数据传回给“联系人列表”
    a.关闭当前控制器(“添加联系人”界面)
    b.传递数据(使用代理)
    设置源控制器(“联系人列表”控制器)为目标控制器(“添加联系人”控制器)的代理
    点击“添加”按钮的时候,使用代理更改“联系人列表”的数据
     
    AddViewController:
     1 /** 点击添加按钮 */
     2 - (IBAction)add {
     3     // 1.关闭当前控制器
     4     [self.navigationController popViewControllerAnimated:YES];
     5    
     6     // 2.使用模型传递数据给上一个控制器(ContactListViewController),使用代理通知数据更新
     7     Contact *contact = [[Contact alloc] init];
     8     contact.name = self.nameText.text;
     9     contact.phone = self.phoneText.text;
    10     [self.delegate addViewController:self didAddedContact:contact];
    11 }
     
    c.“联系人列表”添加数据
    ContactListTableViewController:
    1 #pragma mark - AddViewController delegate function
    2 /** 添加联系人后,更新联系人列表数据 */
    3 - (void)addViewController:(AddViewController *)addViewController didAddedContact:(Contact *)contact {
    4     NSMutableArray *mcontacts = [NSMutableArray arrayWithArray:self.contacts];
    5     [mcontacts addObject:contact];
    6     self.contacts = mcontacts;
    7     [self.tableView reloadData];
    8 }
     
    5.查看/编辑联系人
    (1)拖入一个UIViewController作为“查看/编辑联系人”控制器
    a.姓名、电话的TextField
    b.“保存”按钮
    c."编辑/取消" 按钮
    Image(239)
     
    (2)跳转Segue设置
    a.在“联系人列表”控制器中,指定storyboard的cell指定一个ID
    2ADA46FC-1ABC-46FF-ADE2-F64925598882
     
    b.给上述的cell设置一个自动Segue,指向“查看/编辑联系人”控制器
    Image(240)
     
    c.利用storyboard中已经绑定了ID的cell创建cell
    ContactCell:
    1 + (instancetype) cellWithTableView:(UITableView *) tableView {
    2     // cell ID 要在storyboard中设置好
    3     static NSString *ID = @"contactCell";
    4     // 先从缓存池寻找,如果找不到就从storyboard中创建一个
    5     return [tableView dequeueReusableCellWithIdentifier:ID];
    6 }
     
    d.在“联系人列表”控制器的prepareForSegue方法中,根据目标控制器的类型,区分跳转向“添加联系人”控制器和“编辑联系人”控制器
    ContactListTableViewController:
     1 #pragma mark - Segue相关
     2 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
     3     // 取得目标控制器
     4     id controller = segue.destinationViewController;
     5    
     6     // 判断跳转目标
     7     if ([controller isKindOfClass:[AddViewController class]]) {
     8         // 如果是“添加联系人”
     9         AddViewController *addViewController = controller;
    10         addViewController.delegate = self;
    11     }
    12    
    13     if ([controller isKindOfClass:[EditViewController class]]) {
    14         // 如果是“查看/编辑联系人”
    15         EditViewController *editViewController = controller;
    16        
    17         // 取出数据
    18         NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
    19         editViewController.contact = self.contacts[indexPath.row];
    20        
    21         // 设置代理
    22         editViewController.delegate = self;
    23     }
    24   
    25 }
     
    (3)传输数据
    不能在prepareForSegue中设置目标控制器的控件数据,因为目标控制器的view还没有加载
    在目标控制器的viewDidLoad方法中做
    EditViewController:
     1 - (void)viewDidLoad {
     2     [super viewDidLoad];
     3     // Do any additional setup after loading the view.
     4    
     5     // 加载数据
     6     self.nameText.text = self.contact.name;
     7     self.phoneText.text = self.contact.phone;
     8    
     9     // 设置输入监听
    10     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.nameText];
    11     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:self.phoneText];
    12 }
     
    (4)编辑(右上角加上一个“编辑”按钮)
    a.激活输入框的编辑状态
    b.激活“保存”按钮
    c.弹出键盘
    d.姓名和电话输入框同时为空的时候,保护“保存”按钮
     
    Image(241)
     
    (5)取消编辑(进入“编辑”状态后,将“编辑”按钮转换成“取消”按钮)
    a.改变“取消”按钮为“编辑”按钮
    b.改变输入框为不可编辑
    c.收回键盘
    e.恢复数据
    Image(242)
     
     1 /** 点击“编辑”或"取消” */
     2 - (IBAction)editOrCancel:(UIBarButtonItem *)sender {
     3     // 转变后的状态
     4     BOOL isChangedToEditMode = [sender.title isEqualToString:@"编辑"]? YES:NO;
     5    
     6     if (isChangedToEditMode) {
     7         // 点击"编辑"
     8         // 1.转变title
     9         sender.title = @"取消";
    10        
    11         // 2.转变编辑状态
    12         self.nameText.enabled = YES;
    13         self.phoneText.enabled = YES;
    14         self.saveButton.hidden = NO;
    15        
    16         // 获得输入焦点
    17         [self.nameText becomeFirstResponder];
    18     } else {
    19         // 点击“取消”
    20         // 1.转变title
    21         sender.title = @"编辑";
    22        
    23         // 2.转变编辑状态
    24         self.nameText.enabled = NO;
    25         self.phoneText.enabled = NO;
    26         self.saveButton.hidden = YES;
    27        
    28         // 3.还原数据
    29         self.nameText.text = self.contact.name;
    30         self.phoneText.text = self.contact.phone;
    31        
    32         // 4.退出键盘
    33         [self.view endEditing:YES];
    34     }
    35    
    36 }
    37 
    38 /** 消息监听事件 */
    39 - (void) textChange {
    40     self.saveButton.enabled = self.nameText.text.length && self.phoneText.text.length;
    41 }
     
    (6)保存,返回数据到“联系人列表”
    使用代理,和“添加联系人”同理
    a.关闭页面
    b.传输数据
    <1> 更新模型数据
    <2> 使用代理方法修改模型数据
    c.在“联系人列表”中刷新数据
    1 #pragma mark - EditViewController delegate function
    2 /** “编辑联系人”的“保存”代理方法, 刷新数据 */
    3 - (void)editViewController:(EditViewController *)editViewController didSavedContact:(Contact *)contact {
    4     [self.tableView reloadData];
    5 }
     
    6.自定义分割线
    有数据的cell才显示分割线
    (1)系统自带的分割线实际是一个高度只有1的UIVIew
    (2)设置cell的样式为不带分割线,自行在返回的cell加上分割线
    a.自定义一个集成UITableViewCell的类
    Image(243)
     
    b.在storyboard设置cell的class为自定义的cell类
    Image(244)
     
    c.重写cell的构造方法
    1 + (instancetype) cellWithTableView:(UITableView *) tableView {
    2     // cell ID 要在storyboard中设置好
    3     static NSString *ID = @"contactCell";
    4     // 先从缓存池寻找,如果找不到就从storyboard中创建一个
    5     return [tableView dequeueReusableCellWithIdentifier:ID];
    6 }
     
    注意:
    <1> 从storyboard中创建的cell不会调用initWithStyle: reuseIndentifier:方法,但是也会调用awakeFromNib方法
    ==>
    (1)就算重写initWithStyle: reuseIndentifier:,也不会被调用
    (2)把创建分割线的逻辑放在awakeFromNib方法中
     
    1 /**
    2 * 如果cell是通过storyboard或者xib创建,不会调用此方法
    3 * 如果使用代码创建cell,才会调用这个方法来初始化cell
    4 */
    5 - (instancetype) initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    6     NSLog(@"initWithStyle");
    7     self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    8     return self;
    9 }
     
    <2> 由于在awakeFromNib中cell的frame还没被初始化(例如高度永远是44),所以重写layoutSubviews来设置分割线的frame
     1 /**
     2 * 如果cell是通过storyboard或者xib创建,创建完毕会调用这个方法
     3 * 可以在这里初始化cell
     4 */
     5 - (void) awakeFromNib {
     6     // 自定义一个分割线
     7     UIView *divideLine = [[UIView alloc] init];
     8     [divideLine setBackgroundColor:[UIColor blackColor]];
     9     [divideLine setAlpha:0.2];
    10     [self.contentView addSubview:divideLine];
    11     self.divideLine = divideLine;
    12 }
    13 
    14 /**
    15 * 在此方法中,cell的frame确定被初始化完成,可以再这里初始化其子控件的frame
    16 */
    17 - (void)layoutSubviews {
    18     // 切记调用父类方法!
    19     [super layoutSubviews];
    20    
    21     CGFloat lineHeight = 1;
    22     CGFloat lineWidth = self.frame.size.width;
    23     CGFloat lineX = 0;
    24     CGFloat lineY = self.frame.size.height - 1;
    25    
    26     self.divideLine.frame = CGRectMake(lineX, lineY, lineWidth, lineHeight);
    27 }
     
    7.持久化数据
    有5种方式
    • xml属性列表(plist)归档
    • Preference(偏好设置)
    • NSKeyedArchiver归档
    • SQLite3数据库(关系型数据库)
    • Core Data
     
    (1)“添加联系人”、“编辑联系人” 保存之后
    a.使用plist保存联系人数据(账号、密码也可以用plist模拟实现)
     1 #pragma mark - AddViewController delegate function
     2 /** 添加联系人后,更新联系人列表数据 */
     3 - (void)addViewController:(AddViewController *)addViewController didAddedContact:(Contact *)contact {
     4     NSMutableArray *mcontacts = [NSMutableArray arrayWithArray:self.contacts];
     5     [mcontacts addObject:contact];
     6     self.contacts = mcontacts;
     7    
     8     // 保存数据到plist文件
     9     [self saveContactsDataToFile];
    10    
    11     // 刷新界面数据
    12     [self.tableView reloadData];
    13 }
    14 
    15 #pragma mark - EditViewController delegate function
    16 /** “编辑联系人”的“保存”代理方法, 刷新数据 */
    17 - (void)editViewController:(EditViewController *)editViewController didSavedContact:(Contact *)contact {
    18     // 保存数据
    19     [self saveContactsDataToFile];
    20     // 刷新数据
    21     [self.tableView reloadData];
    22 }
    23 
    24 #pragma mark - 加载、保存、读取数据
    25 /** 保存联系人数据到plist */
    26 - (void) saveContactsDataToFile {
    27     // 沙盒路径
    28     NSString *path = NSHomeDirectory();
    29     // Documents路径
    30     NSString *docPath = [path stringByAppendingPathComponent:@"Documents"];
    31     // plist文件路径
    32     NSString *filePath = [docPath stringByAppendingPathComponent:@"contacts.plist"];
    33    
    34     NSMutableArray *dictArray = [NSMutableArray array];
    35     for (Contact *contact in self.contacts) {
    36         // 将模型转换成字典进行存储
    37         NSDictionary *dict = [Contact dictionaryWithContact:contact];
    38         [dictArray addObject:dict];
    39     }
    40     [dictArray writeToFile:filePath atomically:YES];
    41     NSLog(@"save to : %@", filePath);
    42 }
     
    b.进入“联系人”列表的时候读取数据
     1 /** 加载数据 */
     2 - (NSArray *) contacts {
     3     if (nil == _contacts) {
     4         // 沙盒路径
     5         NSString *path = NSHomeDirectory();
     6         // Documents路径
     7         NSString *docPath = [path stringByAppendingPathComponent:@"Documents"];
     8         // plist文件路径
     9         NSString *filePath = [docPath stringByAppendingPathComponent:@"contacts.plist"];
    10        
    11         // 如果plist文件不存在
    12         NSFileManager *fileManager = [NSFileManager defaultManager];
    13         if (![fileManager fileExistsAtPath:filePath]) {
    14             _contacts = [NSArray array];
    15             return _contacts;
    16         }
    17        
    18         // 如果plist文件存在,开始读取
    19         NSArray *contacts = [NSArray arrayWithContentsOfFile:filePath];
    20         NSMutableArray *mcontacts = [NSMutableArray array];
    21         for (NSDictionary *dict in contacts) {
    22             Contact *contact = [Contact contactWithDictionary:dict];
    23             [mcontacts addObject:contact];
    24         }
    25        
    26         _contacts = mcontacts;
    27     }
    28    
    29     return _contacts;
    30 }
     
    (2)使用preferences自动记住账号、密码
    a.进入“登陆”界面,读取登陆状态数据
     1 /**
     2 * 把初始化代码放在viewDidAppear
     3 * 让view完全呈现之后才进行自动登陆,否则拿不到view,hideHUD的时候会失效
     4 */
     5 - (void)viewDidAppear:(BOOL)animated {
     6     // 设置登陆状态
     7     [self readLoginStatus];
     8    
     9     // 初始化登陆按钮状态
    10     if (!self.accountText.text.length || !self.pwdText.text.length) {
    11         self.loginButton.enabled = NO;
    12     }
    13    
    14     // 登陆按钮开始监听账号、密码输入框
    15     [self loginButtonListening];
    16 }
    17  
    18 /** 读取登陆状态 */
    19 - (void) readLoginStatus {
    20     // 记住密码、自动登录开关状态
    21     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    22     self.keepPwd.on = [defaults boolForKey:@"keepPwd"]? YES:NO; // 防止键值对为nil
    23     self.autoLogin.on = [defaults boolForKey:@"autoLogin"]? YES:NO;
    24    
    25     // 账号、密码
    26     if (self.keepPwd.isOn) {
    27         self.accountText.text = [defaults objectForKey:@"account"];
    28         self.pwdText.text = [defaults objectForKey:@"pwd"];
    29     }
    30    
    31     // 自动登陆
    32     if (self.autoLogin.isOn) {
    33         [self login];
    34     }
    35 }
     
    b.点击“登陆”保存登陆状态
     1 /** 点击登陆 */
     2 - (IBAction)login {
     3     // 检测账号
     4     if (![self.accountText.text isEqualToString:@"hw"]) {
     5         [MBProgressHUD showError:@"账号不存在"];
     6         return;
     7     }
     8    
     9     // 检测密码
    10     if (![self.pwdText.text isEqualToString:@"123"]) {
    11         [MBProgressHUD showError:@"密码错误"];
    12         return;
    13     }
    14    
    15     // 登陆中,遮盖屏幕,禁止用户进行其他操作
    16     [MBProgressHUD showMessage:@"正在使劲登录中..."];
    17    
    18     // 模拟登陆过程,延迟跳转
    19     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    20         // 移除遮盖
    21         [MBProgressHUD hideHUD];
    22        
    23         // 根据Segue ID 执行跳转
    24         [self performSegueWithIdentifier:@"contactList" sender:nil];
    25     });
    26 
    27     // 保存登陆状态
    28     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    29     [defaults setBool:self.keepPwd.isOn forKey:@"keepPwd"];
    30     [defaults setBool:self.autoLogin.isOn forKey:@"autoLogin"];
    31    
    32     // 保存账号、密码信息
    33     if (self.keepPwd.isOn) {
    34         [defaults setObject:self.accountText.text forKey:@"account"];
    35         [defaults setObject:self.pwdText.text forKey:@"pwd"];
    36     }
    37 }
     
    8.“自动登陆”的细节
    (1)模拟登陆的遮盖消除问题
         在登陆控制器中,如果在viewDidLoad中调用登陆方法进行登陆,页面进入到“联系人列表”之后,遮盖的信息并不会隐藏,要在viewDidAppear中调用登陆方法,才能正常运行。
    56376EB7-483B-4B96-8503-3FF64C7E0B4C
     
    (2)在“联系人列表”界面,点击“注销”回到“登陆”界面,依然会触发自动登陆
    解决:设置一个私有变量保存自动登陆的许可状态,在非初次进入登陆界面的情况禁止自动登陆
     
     1 /** 自动登陆标识 */
     2 @property(nonatomic, assign) BOOL stopAutoLogin;
     3  
     4 - (void)viewDidDisappear:(BOOL)animated {
     5     // 当不是初次出现在屏幕上,禁止自动登陆
     6     self.stopAutoLogin = YES;
     7 }
     8  
     9 /** 读取登陆状态 */
    10 - (void) readLoginStatus {
    11     // 记住密码、自动登录开关状态
    12     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    13     self.keepPwd.on = [defaults boolForKey:@"keepPwd"]? YES:NO; // 防止键值对为nil
    14     self.autoLogin.on = [defaults boolForKey:@"autoLogin"]? YES:NO;
    15    
    16     // 账号、密码
    17     if (self.keepPwd.isOn) {
    18         self.accountText.text = [defaults objectForKey:@"account"];
    19         self.pwdText.text = [defaults objectForKey:@"pwd"];
    20     }
    21    
    22     // 自动登陆
    23     if (!self.stopAutoLogin && self.autoLogin.isOn) {
    24         [self login];
    25     }
    26 }
     
    9.删除数据
    在“联系人列表”界面从右往左滑动记录,会弹出“删除”按钮
    只需要实现一个tableView协议方法 commitEditingStyle,只需要实现这个方法就会出现“删除”按钮
     1 /**
     2 * 如果实现了这个方法,就自动实现了滑动删除功能
     3 * 点击了“删除”按钮就会调用
     4 * 实质是提交了一个编辑操作导致调用(删除/添加/编辑)
     5 * @param editingStyle 编辑行为
     6 * @param indexPath 操作行号
     7 */
     8 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
     9     // 如果是删除操作
    10     if (editingStyle == UITableViewCellEditingStyleDelete) {
    11         // 1.删除数据
    12         NSMutableArray *marray = [NSMutableArray arrayWithArray:self.contacts];
    13         [marray removeObjectAtIndex:indexPath.row];
    14         self.contacts = marray;
    15       
    16         // 2.刷新界面
    17 //        [self.tableView reloadData];
    18         // 只删掉需要删除的一行,不必刷新所有数据
    19         // 使用这个方法,必须删除对应的模型数据,数量要对应
    20         [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
    21        
    22         // 3.更改plist数据
    23         [self saveContactsDataToFile];
    24     }
    25 }
     
    Image(245)
     
     
    10.编辑状态
    在“联系人列表”界面,点击一个按钮,使所有联系人cell进入编辑状态
    Image(246)
     
    a.添加一个“垃圾桶”按钮
     1 - (void)viewDidLoad {
     2     [super viewDidLoad];
     3    
     4     // 设置无系统自带分割线
     5     [self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
     6    
     7     // 取出原来在storyboard中创建的“+”按钮
     8     UIBarButtonItem *addItem = self.navigationItem.rightBarButtonItem;
     9    
    10     // 创建一个“垃圾桶”按钮
    11     UIBarButtonItem *deleteItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(deleteCheck)];
    12    
    13     // 都放入到右边的状态栏
    14     self.navigationItem.rightBarButtonItems = @[deleteItem, addItem];
    15 }
     
    b.编辑状态
    1 // 点击状态栏按钮“垃圾桶”,使所有cell进入编辑状态或者退出编辑状态
    2 - (void) deleteCheck {
    3 //    self.tableView.editing = !self.tableView.editing;
    4     [self.tableView setEditing:!self.tableView.editing animated:YES];
    5 }
     
    c.白边编辑状态的功能键
     1 // 改变编辑状态下的编辑按钮
     2 - (UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
     3     if (indexPath.row == 0) {
     4         // 这里改为“添加”符号,需要自定义事件
     5         return UITableViewCellEditingStyleInsert;
     6     } else if (indexPath.row == 1) {
     7         // none就是什么都没有,没有作用
     8         return UITableViewCellEditingStyleNone;
     9     } else {
    10         // 默认就是“删除”
    11         return UITableViewCellEditingStyleDelete;
    12     }
    13 }
     
    Image(247)
     
     
     
  • 相关阅读:
    HTML解决浏览器字体大小12px限制,实现自动适应大小
    Oracle 大最插入数据 一段时间之后变慢问题解决方法
    中间件使用-nginx 中ssl证书的设置
    asp.net core学习:准备asp.net core源码编译环境
    批量修改文件名后缀
    tcpdump 抓所有网卡的包
    mysql数据库备份
    x64架构下Linux系统函数调用
    博客背景美化——动态雪花飘落
    MySQL锁:03.InnoDB行锁
  • 原文地址:https://www.cnblogs.com/hellovoidworld/p/4187880.html
Copyright © 2020-2023  润新知