我的iOS入门第二课

作者: __西门吹雪__ | 来源:发表于2016-09-13 15:54 被阅读107次

    简介

    承上一篇我的iOS入门第一课,本次要记录的是一年前读raywenderlich的基本教程的第二个app,Checklists.主要功能是用来记录个人的一些提醒事项。如下:

    主要界面.png

    下面主要讲我学到了什么:

    一、基本控件
    UITableView, UITableView, UITableView!

    重点知识:

    • 导航控制器的概念 UINavigationController
    • 控制器之间的消息回传 delegate
    • UITableView的delegate、dataSource
    • 操作tableView的cell,删除cell,增加cell
      • 删除cell要做两件事情,一是删除数据,二是删除tableView上的cell
     -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
       [self.dataModel.lists removeObjectAtIndex:indexPath.row];
       
       [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:(UITableViewRowAnimationAutomatic)];
    }
    
    • 动态增加cell,用storyboard的话首先要把tableViewCell布局放好,然后在控制器创建引用,接着就可以用代码操作cell了
    在storyboard布局一个隐藏的cell.png
    @property (weak, nonatomic) IBOutlet UITableViewCell *datePickerCell;
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        if (section == 1 && _datePickerVisible) {
            return 3;
        }else {
            return [super tableView:tableView numberOfRowsInSection:section];
        }
    }
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        if (indexPath.section == 1 && indexPath.row == 2) {
            return self.datePickerCell;
        }else {
            return [super tableView:tableView cellForRowAtIndexPath:indexPath];
        }
    }
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        if (indexPath.section == 1 && indexPath.row == 2) {
            return 217;
        }else {
            return [super tableView:tableView heightForRowAtIndexPath:indexPath];
        }
    }
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        [self.textField resignFirstResponder];
        
        if (indexPath.section == 1 && indexPath.row == 1) {
            if (!self.datePickerVisible) {
                [self showDatePicker];
            }else {
                [self hideDatePicker];
            }
        }
    }
    - (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath {
        if (indexPath.section == 1 && indexPath.row == 2) {
            indexPath = [NSIndexPath indexPathForRow:0 inSection:indexPath.section];
        }
        return [super tableView:tableView indentationLevelForRowAtIndexPath:indexPath];
    }
    - (void)showDatePicker {
        self.datePickerVisible = YES;
        
        NSIndexPath *indexPathDateRow = [NSIndexPath indexPathForRow:1 inSection:1];
        NSIndexPath *indexPathDatePicker = [NSIndexPath indexPathForRow:2 inSection:1];
        
        UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPathDateRow];
        cell.detailTextLabel.textColor = cell.detailTextLabel.tintColor;
        
        [self.tableView beginUpdates];
        [self.tableView insertRowsAtIndexPaths:@[indexPathDatePicker] withRowAnimation:(UITableViewRowAnimationFade)];
        [self.tableView reloadRowsAtIndexPaths:@[indexPathDateRow] withRowAnimation:(UITableViewRowAnimationNone)];
        [self.tableView endUpdates];
        
        [self.datePicker setDate:self.dueDate animated:NO];
    }
    - (void)hideDatePicker {
        if (self.datePickerVisible) {
            self.datePickerVisible = NO;
            
            NSIndexPath *indexPathDateRow = [NSIndexPath indexPathForRow:1 inSection:1];
            NSIndexPath *indexPathDatePicker = [NSIndexPath indexPathForRow:2 inSection:1];
            
            UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPathDateRow];
            cell.detailTextLabel.textColor = [[UIColor alloc] initWithWhite:0 alpha:0.5];
            
            [self.tableView beginUpdates];
            [self.tableView reloadRowsAtIndexPaths:@[indexPathDateRow] withRowAnimation:(UITableViewRowAnimationNone)];
            [self.tableView deleteRowsAtIndexPaths:@[indexPathDatePicker] withRowAnimation:(UITableViewRowAnimationFade)];
            [self.tableView endUpdates];
        }
    }
    

    不得不说动态增加一个cell需要做很多事情。。。

    • textField的delegate, 当用户有输入时把done按钮变为可用
    -(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
        NSString *oldString = textField.text;
        NSString *newString = [oldString stringByReplacingCharactersInRange:range withString:string];
        self.doneBarButton.enabled = newString.length > 0;
        
        return YES;
    }
    
    • 本地通知UILocalNotification
      • 第一次时需要获取本地通知权限
    UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:nil];
            [[UIApplication sharedApplication] registerUserNotificationSettings:setting];
    
    • 设置本地通知
    -(void)scheduleNotification {
        UILocalNotification *existingNotification = [self notificationForThisItem];
        if (existingNotification) {
            [[UIApplication sharedApplication] cancelLocalNotification:existingNotification];
        }
        
        if (_shouldRemind && [_dueDate compare:[NSDate new]] != NSOrderedAscending ) {
            UILocalNotification *notification = [UILocalNotification new];
            notification.fireDate = _dueDate;
            notification.timeZone = [NSTimeZone defaultTimeZone];
            notification.alertBody = _text;
            notification.soundName = UILocalNotificationDefaultSoundName;
            notification.userInfo = @{@"ItemID": @(_itemID)};
            
            [[UIApplication sharedApplication] scheduleLocalNotification:notification];
        }
    }
    -(UILocalNotification *)notificationForThisItem {
        NSArray *notifications = [UIApplication sharedApplication].scheduledLocalNotifications;
        for (UILocalNotification *notification in notifications) {
            NSInteger itemID = (NSInteger)notification.userInfo[@"ItemID"];
            if (itemID == self.itemID) {
                return notification;
            }
        }
        return nil;
    }
    
    • 发通知的对象销毁时取消通知
    -(void)dealloc {
        if ([self notificationForThisItem]) {
            [[UIApplication sharedApplication] cancelLocalNotification:[self notificationForThisItem]];
        }
    }
    
    • 本地存储,使用对象归档为plist文件做本地存储
      • 必须给模型实现NSCoding协议,实现两个方法
     -(instancetype)initWithCoder:(NSCoder *)aDecoder {
        if (self = [super init]) {
            _name = [aDecoder decodeObjectForKey:@"Name"];
            _iconName = [aDecoder decodeObjectForKey:@"IconName"];
            _items = [aDecoder decodeObjectForKey:@"Items"];
        }
        return self;
    }
    -(void)encodeWithCoder:(NSCoder *)aCoder {
        [aCoder encodeObject:_name forKey:@"Name"];
        [aCoder encodeObject:_iconName forKey:@"IconName"];
        [aCoder encodeObject:_items forKey:@"Items"];
    }
    
    • 归档到Document文件夹中
    -(void)saveChecklists {
        NSMutableData *data = [NSMutableData data];
        NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
        [archiver encodeObject:_lists forKey:@"Checklists"];
        [archiver finishEncoding];
        [data writeToFile:[self dataFilePath] atomically:YES];
    }
    -(NSString *)documentsDirectory {
        return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    }
    -(NSString *)dataFilePath {
        return [[self documentsDirectory] stringByAppendingPathComponent:@"Checklists.plist"];
    }
    
    • 程序重新启动时在init方法调用loadChecklists初始化模型加载本地数据
    -(void)loadChecklists {
        NSString *path = [self dataFilePath];
        if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
            NSData *data = [NSData dataWithContentsOfFile:path];
            NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
            _lists = [unarchiver decodeObjectForKey:@"Checklists"];
            [unarchiver finishDecoding];
            [self sortChecklists]; //排序
        }
    }
    
    • 在AppDelegate中初始化DataModel, 并在代理方法中保存数据
    -(void)applicationDidEnterBackground:(UIApplication *)application {    
        [self saveData];
    }
    -(void)applicationWillTerminate:(UIApplication *)application {
        [self saveData];
    }
    -(void)saveData {
        [self.dataModel saveChecklists];
    }
    

    总结

    此app设计的目的只有一个,就是熟悉tableView的操作,在往后开发的任何一个app里都离不开这个最基本的控件,但现实开发中更多涉及到的是处理动态cell,与xib组合,不同的cell和内容,tableView的header和footer,下拉刷新,如何优化tableView,动态计算cell的高度,MVVM等。革命尚未完成,同志仍需努力。
    源代码传送门

    卧薪藏胆,三千越甲可吞吴。

    相关文章

      网友评论

        本文标题:我的iOS入门第二课

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