2016 Week 2

开篇

第二星期开始,先和浩哥去看了美人鱼,就开始每天继续Coding-iOS的学习了。个人感觉这个Coding-iOS项目代码写的真好,开源之后简直就是iOS菜鸡开发者的福音。自己写起来之后,才发现coding速度真的是慢=。=

问题列表

  1. UITextField有关
  2. 自定义UITableViewCell流程
  3. NSUserDefaults基础
  4. dispatch_oncestatic 变量
  5. 创建category
  6. SHA1加密
  7. 2种enum类型
  8. Coding调用API流程
  9. MBProgressHUD基础
  10. keyWindow
  11. 文件操作有关
  12. 账户输入提示框
  13. enumerateObjectsUsingBlock:
  14. 2种UITableViewCell重用方式
  15. NSRange基础
  16. CAShapeLayer基础
  17. 显示/隐藏导航栏
  18. ViewController之间切换卡顿
  19. SDWebImageOptions
  20. category中新增property
  21. 用户名存储整个流程
  22. UIAlertController基础

UITextField有关

介绍几个UITextField的属性和方法

  1. secureTextEntry: 输入的文本不可见,多用于密码输入
  2. clearButtonMode: 清空文本输入的按钮的模式控制,属于UITextFieldViewMode。经常用到的有:UITextFieldViewModeNever, UITextFieldViewModeWhileEditing
  3. 触发事件: 常用的有 开始编辑(UIControlEventEditingDidBegin), 文本变更(UIControlEventEditingChanged), 结束编辑(UIControlEventEditingDidEnd)
  4. keyboardType: 键盘类型,可选的有 UIKeyboardTypeEmailAddress,
    UIKeyboardTypeDefault

自定义UITableViewCell流程

参考这里

NSUserDefaults基础

NSUserDefaults适合存储轻量级的本地数据,非常适合用来存储登录界面的数据(用户名,密码等等)。用法也很简单👇

  1. 存储数据
         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];        // 获取 defaults
         [defaults setObject:loginData forKey:@"loginData"];                      // 存放登录数据
         [defaults synchronize];                                                  // 同步到磁盘,避免数据丢失
    
  2. 读取数据
         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];        // 获取 defaults
         NSDictionary *loginData = [defaults objectForKey:@"loginData"];          // 获取登录数据
    

dispatch_oncestatic 变量

dispatch_once函数只会被执行一次,经常用来创建单例实例,先来看看dispatch_once函数

        void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);

第二个参数是只会被执行一次的代码块,第一个参数用来判断代码块是否被执行。该函数与static变量一同使用,看看具体代码👇

        + (instancetype)sharedManager {
            static Coding_NetAPIManager *manager = nil;
            static dispatch_once_t pred;
            dispatch_once(&pred, ^{
                manager = [[Coding_NetAPIManager alloc] init];
            });

            return manager;
        }

上面的static变量属于类成员,如果没有在.h文件中声明该static变量,那么他就是私有的类成员。类成员不能通过实例对象或是实例方法(-)去访问,只能通过类实例或是类方法(+)访问。

创建category文件

这个很简单啦,xcode -> New File -> Objective-C File -> 填写所需信息 -> Finish
例如,创建一个UIButton+Common的category,应该👇
category

SHA1加密

加密方法如下👇

        - (NSString *)sha1Str {
            const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
            NSData *data = [NSData dataWithBytes:cstr length:self.length];

            uint8_t digest[CC_SHA1_DIGEST_LENGTH];

            CC_SHA1(data.bytes, (CC_LONG)data.length, digest);

            NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];

            for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
                [output appendFormat:@"%02x", digest[i]];
            }

            return output;
        }

注意 该方法写在了NSString的category里面

2种enum类型

指定enum有两种方式:
1.

        typedef enum {
            Get = 0,
            Post,
            Put,
            Delete
        } NetworkMethod;

2.

        typedef NS_ENUM(NSInteger, IllegalContentType) {
            IllegalContentTypeTweet = 0,
            IllegalContentTypeTopic,
            IllegalContentTypeProject,
            IllegalContentTypeWebsite
        };

Coding调用API流程

参考这里

MBProgressHUD基础

MBProgressHUD是一个用来显示提示信息的第三方库,效果图如下👇
MBProgressHUD demo

事例代码:

        - (void)viewDidLoad {
            [super viewDidLoad];
            // Do any additional setup after loading the view, typically from a nib.
            MBProgressHUD  *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
            hud.mode = MBProgressHUDModeText;
            hud.detailsLabelFont = [UIFont boldSystemFontOfSize:15];
            hud.detailsLabelText = @"tip from vanney9.com";
            hud.margin = 10;
            hud.removeFromSuperViewOnHide = YES;
            [hud hide:YES afterDelay:2.0f];
        }

keyWindow

        [UIApplication sharedApplication].keyWindow

APP主界面

文件操作有关

3个与文件操作相关的方法

  1. [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    该方法返回该APP的Documents目录,在真机上显示的结果如下:
         /var/mobile/Containers/Data/Application/7DB19AAA-B9E7-40D8-AAFA-C5BA2FA91CF0/Documents
    
    在模拟机上显示的结果如下:
         /Users/vanney/Library/Developer/CoreSimulator/Devices/FF2B6585-B815-4762-AC40-268D51742C85/data/Containers/Data/Application/82FEAB7E-9245-4BA8-88ED-A559034BA5F0/Documents
    
  2. [NSMutableDictionary dictionaryWithContentsOfFile:@”file name”];
    该方法读取@"file name"这个文件中的内容(注意:该文件应该存放键值对,一般XML文件),并返回由该键值对组成的NSMutableDictionary
  3. [loginDataList writeToFile:@”file name” atomically:YES];
    该方法中的loginDataList是一个NSMutableDictionary,这个方法将键值对写入@"file name"这个文件中

账户输入提示框

效果如下图👇
easeinput demo
实现该效果的步骤如下:

  1. 创建提示的UITableView
    显示提示的白色框框实际上是一个UITableView,UITableView的内容根据当前已经输入的内容进行改变,若没有输入到@符号,就从之前输入过的账户中搜索;若已经输入了@符号,就显示不同的邮箱后缀。
  2. 将上一步创建的UITableView添加到输入账号,密码的UITableView中,并将提示框UITableView的位置定在账号的cell下面。
  3. 至于两个UITableView的交互:当账号的cell中的UITextField内容发生变化时,调用提示框UITableView的一个block,传入当前UITextField的文本,对提示的内容进行实时的变换。

enumerateObjectsUsingBlock:

enumerateObjectsUsingBlock:方法和for-in类似,具体代码如下👇

        NSDictionary *dict = @{@"a": @"1", @"b": @"2"};
        [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            NSLog(@"key: %@, value: %@", key, obj);
        }];

推荐使用for-in方法,快。

2种UITableViewCell重用方式

UITableViewCell有两种dequeueReuse方法:

  1. - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier;
    该方法是不用事先注册Cell,但是它可能返回nil;所以当返回nil时,需要重新init。示例代码如下👇
         static NSString *CellIdentifier = @"Cell";
         UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
         if (cell == nil) {
             cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
         }
    
  2. - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath;
    该方法在iOS6中取代了第一种方法,它可以保证一定会返回UITableViewCell,但是在初始化UITableView时,需要进行UITableViewCell的注册,如下👇
         [_tableView registerClass:[CustomCell class] forCellReuseIdentifier:@"Cell"];
    
    于是在重用UITableViewCell,不需要判断是否为nil,代码如下👇
         CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    

NSRange基础

NSRange是一个结构体,其定义如下👇

        typedef struct _NSRange {
            NSUInteger location;
            NSUInteger length;
        } NSRange;

其中location表示起始位置,length表示长度。

CAShapeLayer基础

CAShapeLayer继承自CALayer,它表示一个形状,有一个path属性用来描述该形状,然后可以对这个形状进行描边,填充等操作。示例代码如下👇

        CAShapeLayer *layer = [CAShapeLayer layer];
        // 创建path, 给layer添加path, 释放path
        CGMutablePathRef pathRef = CGPathCreateMutable();
        CGRect bounds = CGRectInset(cell.bounds, 0, 0);
        CGPathAddRect(pathRef, nil, bounds);
        layer.path = pathRef;
        CFRelease(pathRef);
        // 对layer进行填充操作
        layer.fillColor = [UIColor colorWithWhite:1.0 alpha:0.8].CGColor;
        // 添加layer
        UIView *bgView = [[UIView alloc] initWithFrame:bounds];
        [bgView.layer addSublayer:layer];

显示/隐藏导航栏

使用下列方法实现导航栏的显示或者隐藏

        - (void)viewWillAppear:(BOOL)animated {
            [super viewWillAppear:animated];
            // 隐藏导航栏,若Hidden设为NO,则显示导航栏
            [self.navigationController setNavigationBarHidden:YES animated:YES];
        }

ViewController之间切换卡顿

可能的原因:没有设置ViewController的backgroundColor=。=

SDWebImageOptions

        typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
            SDWebImageRetryFailed = 1 << 0,//下载失败了会再次尝试下载
            WebImageLowPriority = 1 << 1,//当UIScrollView等正在滚动时,延迟下载图片(放置scrollView滚动卡)
            SDWebImageCacheMemoryOnly = 1 << 2,//只缓存到内存中
            SDWebImageProgressiveDownload = 1 << 3,// 图片会边下边显示
            SDWebImageRefreshCached = 1 << 4,//将硬盘缓存交给系统自带的NSURLCache去处理
            SDWebImageContinueInBackground = 1 << 5,//后台下载
            SDWebImageHandleCookies = 1 << 6,// 通过设置NSMutableURLRequest.HTTPShouldHandleCookies = YES来处理存储在NSHTTPCookieStore中的cookie
            SDWebImageAllowInvalidSSLCertificates = 1 << 7,// 允许不受信任的SSL证书。主要用于测试目的。
            SDWebImageHighPriority = 1 << 8,
            SDWebImageDelayPlaceholder = 1 << 9,
            SDWebImageTransformAnimatedImage = 1 << 10,
        };

category中新增property

正常情况下,在category中是不能新增property的,要按如下步骤进行=。=

  1. 导入头文件
         #import <objc/runtime.h>
    
  2. 新增@property
         @property (nonatomic, strong) UIActivityIndicatorView *buttonIndicatorView;
    
  3. 自定义settergetter

         static char EAButtonIndicatorViewKey;
    
         - (void)setButtonIndicatorView:(UIActivityIndicatorView *)buttonIndicatorView {
             objc_setAssociatedObject(self, &EAButtonIndicatorViewKey, buttonIndicatorView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);  
         }
    
         - (UIActivityIndicatorView *)buttonIndicatorView {
             return objc_getAssociatedObject(self, &EAButtonIndicatorViewKey);
         }
    

用户名存储整个流程

参考这里

UIAlertController基础

从iOS8开始,UIAlertController开始替代UIAlertAction,先来看看UIAlertController的效果图👇
UIAlertController demo
实现代码👇

        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"欢迎注册 Coding,请尽快去邮箱查收邮件并激活账号。如若在收件箱中未看到激活邮件,请留意一下垃圾邮件箱(T_T)。" preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *ok = [UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            [alertController dismissViewControllerAnimated:YES completion:nil];
        }];

        [alertController addAction:ok];
        [self presentViewController:alertController animated:YES completion:nil];

参考

P.S

以上都不是重点,重点是ollie要过立了=。=

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器