知识点总结

开发中的知识点总结

textField

使用textField自定义搜索框,textField及leftView等属性
使用leftView 时需要设置model为始终显示
可以设置leftView的frame 调整图片大小
IBInsepectable IBDesignable 的使用

给属性检查器面板中添加属性设置选项
IBDesignalbe 实时显示UI控件,需要讲对应的xib和对应类绑定

增加分类

新建一个swift 文件,

使用extension,重写 get set方法
使用newValue 时需要判断,注意返回值的类型是否匹配
试图切换

一个控制器中加载不同视图,可以在loadView 方法中自定义根视图

loadView 使用注意事项
如果实现loadView 未调用 super loadView ,sb/xib实效
loadView调用时,如果根视图是nil(本质是未调用super loadView),会自动调用loadView 方法初始化根视图,形成递归调用
判断当前登陆标识的状态确定是调用 super loadView 还是 把创建自定义的根视图赋值给 view
VFL 的使用

使用之前需要先 关闭frame 的布局才能手动的添加约束 translatesAutoresizingMaskIntoConstraints = false
基本动画使用

Swift3.0学习(三)

面向对象–必选属性

构造函数时是分段构造,先构造子类,给’必选属性’设置初始值,然后再构造父类,
定义全局变量(属性)时,需要在构造函数中设置初始化变量值
super.init()是一个类构造结束
重写: override 函数重写(覆盖),如果在重写的方法中 没有super, 父类相关的一些操作就不会被执行
重载:函数名相同, 参数的类型 或者参数的个数不同 就形成了函数的重载
注意:重载构造函数,并且父类默认的构造函数 init()不重写, 父类默认的构造函数就不能够被访问,不能够确保必选属性设置初始值,只能调用重载的构造函数
面向对象–可选属性

使用KVC给对象设置值
注意: 在swift中使用KVC 基本数据类型不能声明为可选项,必须设置为必选项给定初始值

KVC实现的流程
遍历字典的键值 给对象发送 setValue: forKey消息
如果key 对象的属性不存在就将消息转发给 setVale: forUndefinedKey:
如果存在就直接设置值
注意:setVale: forUndefinedKey: 默认抛出异常 不能够super,如果super 相当于没有实现此方法

面向对象–便利构造函数

作用:方便快捷的创建对象
场景:
可检查参数是否正确来实例化
实例化控件
特点
必须以self来调用构造函数
指定的构造函数不能被重写,也不能被super调用
便利构造函数可被子类继承
可以构造失败,返回nil
面向对象 – 描述信息

重写 description 方法,返回值是string类型
使用kvc讲对象属性转换为字典
将字典转化为string类型

override var description: String {
        let keys = ["name","age","number","sex"]

        // 对象转为字典
        let dict = self.dictionaryWithValuesForKeys(keys)

        // 每个对象都有描述属性
        return dict.description
    }

面向对象 – 存储属性

面向对象 – 计算属性(readOnly)

只有getter,没有setter
只能取值不能赋值
每次调用都会被执行,消耗cpu
不占内存空间,依赖其他的属性来计算
面向对象 – didset 属性设置监察器

能获取 oldValue 和 newValue 的值
通常用来重写setter方法,实现视图绑定模型
在一个属性的didset中计算其他属性

缺点:消耗内存,被计算的属性占内存
优点:减少了cpu消耗
懒加载

Swift3.0学习(二)

字符串

字符串遍历
字符串字节长度
字符串长度

let string = “我是switf,语言”

// 字符串遍历
for s in string.characters {
print(s)
}

// 字符串的长度
let length = string.characters.count;
print(length)

// 字符串字节长度 汉字对应3个字节,英文是1个字节
let characterLength = string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
print(characterLength)

字符串拼接

let str1 = "你好"
   let str2 = "不好"
   let a = 20

   // 第一种拼接
   print("\(str1)\(str2)\(a)")

   // 第二种拼接
   let str = str1 + str2
   let strA = str1 + String(a)
   print(str)
   print(strA)

截取子串 as 的使用

let subString = str1.substringToIndex("小程生生世世".endIndex);
   print(subString)

   let subStr = str1.substringFromIndex("听说".startIndex)
   print(subStr)

   let range = str1.rangeOfString("小程序")
   let subStr1 = str1.substringWithRange(range!)

   print(subStr1)

   // 截取到某一个位置
   let subStr2 = str1.substringFromIndex(str1.endIndex.advancedBy(-2))
   print(subStr2)

   // 限制截取几个字符
   let subStr3 = str1.substringToIndex(str1.startIndex.advancedBy(5, limit: str1.characters.endIndex))
   print(subStr3)
String 和 NSString 之间的 使用 as 无缝转换
let helloString = "我们一起飞"
(helloString as NSString).substringWithRange(NSMakeRange(2, 3))
数组

可以放不同类型的元素
let不可变数组,var可变数组

// 初始化一个空数组 [String]声明是一个装有字符串的数组类型
   var emptyArray: [String] = [String]()

// 可变数组
   var array = ["哈哈","呵呵","嘿嘿"]
   array.append("咯咯")
   print("array = \(array)")

   // 不可变数组
   let array2 = ["可可","噗噗"]

   // 数组拼接
   let arr = array + array2
   print("arr = \(arr)")

   array += array2
   print("array = \(array)")

数组遍历

// 1. 
   for i in 0 ..< tempArray.count {
       print(tempArray[i])
   }


   // 2. 
   for obj in tempArray {
       print("第二种方法:\(obj)")
   }


   // 3. 快速便利
   for (index,value) in tempArray.enumerate() {
       print("index = \(index), value = \(value)")
   }

字典和OC类似

函数调用

必须在函数声明的下面调用内部函数

可以定义函数的外部参数和内部参数

可以在参数前面加上 ‘# ’ 表明函数的外部参数和内部参数名称一样 swift3.0 以后废弃

无参无返回值
三种写法

func demo9() -> Void {
       print("无参无返回值第一种写法")
   }


   func demo_9() -> () {
       print("无参无返回值第二种写法")
   }

   func demo_99() {
       print("无参无返回值第三种写法")
   }

有参有返回值

func demo10(width a: Int, height b: Int) -> Int {
   return a * b

}

闭包

和OC中block类似,
和在函数中调用一个内部函数原理相同
可当作参数传递
在需要时执行闭包实现回调

// 定义闭包
let closure = {() -> () in
       print("闭包实现")
   }

   // 执行闭包
   closure()

注意循环引用

只有两个对象相互强引用或者三个对象相互强引用形成闭环时才会形成循环引用

weak 和 unsafe_unretained的区别

__weak iOS5.0 推出,当对象被系统回收时,对象的地址会自动指向nil

____unsafe_unretained iOS4.0 推出,当对象被系统回收时,对象的地址不会自动指向nil,会造成野指针访问
解决办法:(官方推荐)weak - strong -dance 解决循环引用,AFNetworking中

尾随闭包

函数的最后一个参数时闭包的时候,函数的参数 ‘()’可以提前关闭,闭包写在 ‘()’后面,当作尾随闭包来使用

swift 和 OC 区别

swift 和 OC语法的快速的对比

XXX.init(xxx) ==> XXX.(xxx)
selector 类型 ==> ‘函数名’
对象方法的调用 是 ‘.’ self是可以省略的
枚举 枚举名 + 枚举值的名 => .枚举值的名
常量 和变量

let 声明常量 var 声明变量
变量/常量的类型是自动推到的
不同类型之间不能够直接运算 需要手动转换数据类型
可选项
‘?’ 表示表示可选项 可能有值 可能为 nil
可选项会自动带上 Optional 字样
可选项不能够直接参与运算 需要强制解包
‘!’ 表示强制解包 获取可选项中具体的值
‘??’ 快速判断可选项是否为nil 如果为 nil 就去取 ‘??’后面的默认值
控制流
if 没有非零即真的概念 必须制定明确的条件
if let 快速赋值 并且判断赋值对象是否为 nil 如果不为nil 就进入分之执行相关逻辑代码
guard let 作用和 if let 相反 好处: 可以减少一层分支嵌套
where 多重判断 注意 和 && || 不一样的
循环

for in
0..<10
0…10
字符串

更加轻量级 更加高效 是 结构体
支持快速遍历
长度
拼接
截取 as
集合 let 声明不可变的集合 var 声明可变的集合

声明 []
声明一个 空的集合
增删改查
合并
遍历
函数

func 函数名称(外部参数1 内部参数1: 类型, 外部参数2 内部参数2: 类型) -> Int {执行的代码}
函数没有返回值的三种方式
函数内部函数
闭包 () -> () 没有参数没有返回值的闭包类型

提前准备好的一段可以执行代码块
可以当做参数传递
在需要的时候执行闭包产生回调的效果
在闭包中使用self 需要考虑循环引用
函数是一种特殊的闭包

block 的循环引用的解除
weak 和 weak关键字作用类似 当属性对象被回收是 会自动指向nil
_
unsafeunretained 和 assgin关键字作用类似 当属性对象被回收是 不会自动指向nil, 会造成野指针访问 weak -strong - dance

Swift 3.0 学习

和OC区别

  • 没有了.h 和 .m 文件,没有main.m 文件
  • 程序入口发生变化 在appdelegate中的 @UIApplicationMain
  • 每行代码可以不写 ‘;’
  • self可以不写,建议不写self,因为闭包中必须要写
  • 真假表示只有 true/ false,没有非0即真的概念
  • 类型自动推到

类型自动推到

提前指定类型

1
2
let float :Double = 9.6 
print(float)

常量和变量

  • let 声明常量,有且只有一次赋值机会
  • var 声明变量
  • 尽量选择使用let如需修改值再使用var 系统会报警告

可选项

  • Optional: 表示一个常量或者是变量可能有值可能为nil,在打印的时候会带有Optional字样
  • ? 表示是一个可选项
  • 可选项不能参与运算
  • ! 表示可选项中一定要有值,可以强制解包,解包时如果没有值会boom

  • ?? 合并空选项 快速判断可选项是否为nil, 如果为nil取后面的默认值,在基本数据类型和字符串使用较多

条件分支

  • if let

快速赋值,判断赋值对象是否为nil,如果不为nil,就赋值进入分支
生成request是url是一个可选项,需要强制解包或者合并空选项

1
2
3
4
let urlString = "http://www.baidu.com"
let url = NSURL(string: urlString)
let request = NSURLRequest(URL: url!)
print(request)
  • 以上代码使用if let实现
1
2
3
4
if let u = NSURL(string: urlString) {
let request = NSURLRequest(URL: u)
print(request)
}
  • where条件语句配合多重判断
1
2
3
4
5
6
if url != nil  {
if url?.host == "www.baidu.com" {
let request = NSURLRequest(URL: url!)
print(request)
}
}
  • 使用if let
1
2
3
4
if let u = url where u.host == "www.baidu.com" {
let request = NSURLRequest(URL: u)
print(request)
}
  • 多重判断
1
2
3
4
if let u = url, s = string {
let request = NSURLRequest(URL: u)
print(request)
}
  • guard let 和 if let 相反如果为nil,可以直接返回
1
2
3
4
5
6
7
let urlString  = "http://www.baidi.com"
let url = NSURL(string: urlString)
guard let u = url else { return }

//程序走到这个地方就表示 u 一定有值
let request = NSURLRequest(URL: u)
print(request)
  • swith语句

    • swift语句中可以case任意类型,
    • 在OC中只能case整数
    • 不需要写break,会自动跳出,
    • 如果需要实现case穿透,需要加入fallthrough语句
    • case语句中临时变量不需要写‘{ }’
    • 可同时case多个值
    • 每句case语句中至少要有一行可执行的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let string = "string1"
switch string {
case "string", "string1":
let str = "stringOne"
print(str)

case "string2":
let str = "stringTWO"
print(str)

case "string3":
print(string)

case "string4":
print(string)
default:
print("empty")
}
  • switch 中同样能够赋值和使用 where 子句
1
2
3
4
5
6
7
8
9
10
11
12
13
let point = CGPoint(x: 10, y: 10)
switch point {
case let p where p.x == 0 && p.y == 0:
print("中心点")
case let p where p.x == 0:
print("Y轴")
case let p where p.y == 0:
print("X轴")
case let p where abs(p.x) == abs(p.y):
print("对角线")
default:
print("其他")
}
  • 循环语句
1
2
3
4
5
6
7
for i in 0 ..< 10 { // 不包含10  循环范围中两边的值格式必须一直(空格问题)
print(i)
}

for _ in 0...5 {// 包含5 _ 表示忽略,不关心,只占位
print("循环语句")
}

OC和JS交互

WebView相关属性

autoResizingMask 是UIView的属性,作用是实现子控件相对于父控件的自动布局

1
2
3
4
5
6
7
8
9
autoResizingMask 是UIViewAutoresizing 默认是 UIViewAutoresizingNone

UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5

各属性解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
UIViewAutoresizingNone
不会随父视图的改变而改变

UIViewAutoresizingFlexibleLeftMargin

自动调整view与父视图左边距,以保证右边距不变

UIViewAutoresizingFlexibleWidth
自动调整view的宽度,保证左边距和右边距不变

UIViewAutoresizingFlexibleRightMargin
自动调整view与父视图右边距,以保证左边距不变

UIViewAutoresizingFlexibleTopMargin
自动调整view与父视图上边距,以保证下边距不变

UIViewAutoresizingFlexibleHeight
自动调整view的高度,以保证上边距和下边距不变

UIViewAutoresizingFlexibleBottomMargin
自动调整view与父视图的下边距,以保证上边距不变

OC 调用js

webViewDidFinishLoad:(UIWebView *)webView方法中

  • 删除增加结点
  • 添加点击事件

js调用OC

webView:(UIWebview )webView shouldStartLoadWithRequest:(NSURLRequest )request方法中

调用js页面点击按钮后保存图片到相册,必须调用系统提供的方法,不能自己自定义方法

SDWebImage的用法及原理

SDWebImage 介绍

SDWebImage 是用于网络中下载且缓存图片,并设置图片到对应的控件或上

Demo: https://github.com/aTreey/DownImage.git

  • 提供了UIImageView的category用来加载网络图片并且下载的图片的缓存进行管理
  • 采用异步方式来下载,确保不会阻塞主线程,memory+disk来缓存网络图片,自动管理缓存
  • 支持GIF动画,[self.imageView setImageWithURL:[[NSBundle mainBundle] URLForResource:@”xx.gif” withExtension:nil];
  • 支持WebP格式
  • 同一个URL的网络图片不会被重复下载
  • 失效的URL不会被无限重试

SDWebImage 的使用

克隆

git clone https://github.com/rs/SDWebImage.git
使用以上命令克隆会报错,框架中Vendors文件夹中的文件未能全部下载导致报错

解决办法:https://github.com/rs/SDWebImage/blob/master/Docs/ManualInstallation.md

git clone –recursive https://github.com/rs/SDWebImage.git

文件全部下载

自定义operation 加入NSOperationQueue中

  • 自定义NSOperation,
    重写main方法
1
2
3
4
5
6
7
8
9
10
11
- (void)main {
@autoreleasepool {
NSURL *url = [NSURL URLWithString:_urlStr];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];

if (_finishBlock) {
_finishBlock(image);
}
}
}
  • 一个NSOperation对象可以通过调用start方法来执行任务,默认是同步执行的。也可以将NSOperation添加到一个NSOperationQueue(操作队列)中去执行,而且是异步执行的
    创建队列
1
2
3
4
- (NSOperationQueue *)downLoadQueue {
if (!_downLoadQueue) _downLoadQueue = [[NSOperationQueue alloc] init];
return _downLoadQueue;
}
  • 添加一个任务到队列
1
2
3
4
5
6
7
8
9
[_downLoadQueue addOperation:operation];
添加一组operation, 是否阻塞当前线程

[_downLoadQueue addOperations:@[operation] waitUntilFinished:NO];
添加一个block 形式的operation

[_downLoadQueue addOperationWithBlock:^{
NSLog(@"执行一个新的线程");
}];

注意:

  1. NSOperation 添加到 queue之后,通常短时间内就会执行,但是如果存在依赖,或者整个queue被暂停等原因,也可能会需要等待

  2. NSOperation添加到queue之后,绝不要修改NSOperation对象的状态,因为NSOperation对象可能会在任何时候运行,因此改变NSOperation对象的依赖或者数据会产生不利的影响,只能查看NSOperation 对象的状态,比如是否正在运行、等待运行、已经完成等

  • NSOperation 添加依赖,
    依赖关系不局限于相同的queue 中的NSOperation对象,可以夸队列进行依赖,但是不能循环依赖,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// NSOperation 添加依赖
{
NSOperationQueue *testQueue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation1 NSThread = %@", [NSThread currentThread]);
}];

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation2 NSThead = %@", [NSThread currentThread]);
}];

// 添加依赖,1 依赖于 2,只有2 执行完之后才执行1
[operation1 addDependency:operation2];
// 加入到队列
[testQueue addOperation:operation1];
[testQueue addOperation:operation2];
}
  • 修改Operation的执行顺序
    对于添加到queue中的operation执行顺序有一下2点决定
    operation 是否已经准备好,是否添加了依赖
    根据多有operations的相对优先性来决定,优先等级是operation对象本身的一个属性,默认都是“普通”优先级,可以通过setQueuePriority方法提高和降低优先级,优先级只能用于相同 queue 中的 operation,多个queue中operation的优先级相互独立,因此不同queue中的低优先级的operation可能比高优先级的operation更早执行
    注意优先级和依赖不能相互替代,优先级只是对已经准备好的operation确定执行顺序,先满足依赖,然后再看已经准备好的operation中的优先级

  • 设置 queue 的最大并发数,队列中最多同时运行几条线程
    虽然NSOperationQueue类设计用于并发执行Operations,你也可以强制单个queue一次只能执行一个Operation。setMaxConcurrentOperationCount:方法可以配置queue的最大并发操作数量。设为1就表示queue每次只能执行一个操作。不过operation执行的顺序仍然依赖于其它因素,比如operation是否准备好和operation的优先级等。因此串行化的operation queue并不等同于GCD中的串行dispatch queue

1
2
3
4
// 每次只能执行一个操作  
queue.maxConcurrentOperationCount = 1;
// 或者这样写
[queue setMaxConcurrentOperationCount:1];
  • 取消 operations
    一旦添加到operation queue,queue就拥有了这个Operation对象并且不能被删除,只能取消。调用Operation对象的cancel方法取消单个操作,也可以调用operation queue的cancelAllOperations方法取消当前queue中的所有操作, 使用cancel属性来判断是否已经取消了

[operation cacel] 只是打了一个死亡标记, 并没有正真意义上的取消,称为 “自杀“,需要在被取消的任务中时时判断是否取消(在程序的关键处), 如果取消,结束任务, 具体是指在自定义的 operation 的main方法中结束

1
2
3
4
5
6
[queue cancelAllOperations] 由队列来取消,称为 “他杀”

// 取消单个操作
[operation cancel];
// 取消queue中所有的操作
[queue cancelAllOperations];
  • 等待operation 完成

为了最佳的性能,你应该设计你的应用尽可能地异步操作,让应用在Operation正在执行时可以去处理其它事情。如果需要在当前线程中处理operation完成后的结果,可以使用NSOperation的waitUntilFinished方法阻塞当前线程,等待operation完成。通常我们应该避免编写这样的代码,阻塞当前线程可能是一种简便的解决方案,但是它引入了更多的串行代码,限制了整个应用的并发性,同时也降低了用户体验。绝对不要在应用主线程中等待一个Operation,只能在第二或次要线程中等待。阻塞主线程将导致应用无法响应用户事件,应用也将表现为无响应。

1
2
// 会阻塞当前线程,等到某个operation执行完毕  
[operation waitUntilFinished];

除了等待单个Operation完成,你也可以同时等待一个queue中的所有操作,使用NSOperationQueue的waitUntilAllOperationsAreFinished方法。注意:在等待一个 queue时,应用的其它线程仍然可以往queue中添加Operation,因此可能会加长线程的等待时间。

// 阻塞当前线程,等待queue的所有操作执行完毕
[queue waitUntilAllOperationsAreFinished];
暂停和继续operation
如果你想临时暂停Operations的执行,可以使用queue的setSuspended:方法暂停queue。不过暂停一个queue不会导致正在执行的operation在任务中途暂停,只是简单地阻止调度新Operation执行。你可以在响应用户请求时,暂停一个queue来暂停等待中的任务。稍后根据用户的请求,可以再次调用setSuspended:方法继续queue中operation的执行

1
2
3
4
5
// 暂停queue  
[queue setSuspended:YES];

// 继续queue
[queue setSuspended:NO];

增加Manager管理类,

作用:

  • 下载队列
  • 图像缓存
  • 操作缓存(内存缓存)
  • 沙盒缓存
  • 防止错乱,取消老的未开始下载的操作
  • 图片显示逻辑:内存缓存 – 沙盒缓存 – 网络下载
  • 图片缓存逻辑:下载完成 – 缓存沙盒 – 加载到内存

注意:缓存到本地时只能保存property列表里的对象(NSData, NSDate, NSNumber, NSString, NSArray, NSDictory), 图片需要转化为二进制数据

SDWebImage

  • SDWebImageDownloader 下完图片后都需要手动设置给UIImageView
  • SDWebImageManager 下完图片后都需要手动设置给UIImageView
  • 下载后要显示可以使用 sd_setImageWithURL 方法

WKWebiView 使用

iOS中静态库动态库介绍及编译

  • 静态库

iOS中根据源码的公开情况可分为2种

  • 开源库
    源代码公开,能够看到具体的代码实现

比如 SDWebImage AFNetworking。。。

  • 闭源库
    不公开源代码, 是经过编译后的二进制文件, 看不到具体实现
    主要分为:静态库、动态库

  • 存在形式

静态库: .a 和 .framework
动态库:.tbd 和 .framework (iOS 9 之前是 .dylib, iOS 9 之后取消了 .dylib 而使用 .tbd 来代替 .dylib )

.framework 的库可以是静态库也可以是静态库, 凡是系统的 .framework 都是动态库,第三方的基本上全是静态中

  • 两者区别

静态库:链接时会被完整的复制到可执行文件中,每次被使用都会被复制
动态库:链接时不复制,程序运行时由系统动态加载到内存中,供程序调用,系统只加载一次,多个程序可以公用,节省内存

静态库编译

编译时如果使用的是模拟器,生成的 .a 只能模拟器用,真机不可用,生成的 .a 文件在 Debug-iphonesimulator 文件夹下

编译时如果使用 generic iOS Device,生成的 .a 只能真机而模拟器不可用,生成的 .a 文件在 Debug-iphoneos 文件夹下

编译静态库时,自己手动创建的类的头文件不会被导出,只是默认导出创建工程时类的头文件

导出手动添加类的头文件,点击工程名称 –> build Phases –> Copy Files 添加头文件,然后编译

  • 架构种类

模拟器:i386 32位架构 (4s~5) / x86_64 64位架构 (5s以后机型)
真机:armv7 32位架构 (4~4s) / armv7s 特殊架构 (5~5C) / arm64 64位架构 (5s 以后机型)

  • 架构查看
    lipo -info xxxx.a
    默认生成模拟器只生成一种架构 i386 x86_64;真机默认导出两种架构 armv7 / arm64

  • 合并多个架构

将模拟器的多个架构合并,项目名称–>Build Settings –> Achitectures –> Build Active Architecture Only –> Debug设置为NO
合并真机多个架构 按照以上方法运行真机即可
合并真机和模拟器架构 cd products lipo -create Debug-iphoneos/xxx.a Debug-iphonessimulator/xxx.a -output xx.a
合成 5 个架构 在Build Setting –> Architectures 中手动添加三种架构,再编译合并

  • 静态库报错

Undefined symbols for architecture (arm64 / armv7 / armv7s / i386 / x86_64),架构包导入出错,静态库有真机与模拟器

异步下载图片

异步下载

同步发送网络请求会卡UI线程,采用异步来下载图片。

异步下载存在的问题

  • 错行问题

    • 解决:使用MVC模式,使数据一对一可解决,
    • 在MVC中增加image属性后,会导致单个模式的体积增大
    • 内存占用
    • 当接收到内存警告时清空图片时需要遍历模型中将image置为nil
    • 将图片用字典缓存起来达到一对一, 清空内存中的图片时只需要remove字典就ok
    • 创建下载任务的字典,在开始下载时将正在下载的URL写入到字典中下载完成后将字典中对应的值移除,写入防止来回滑动时重复发送网络请求,下载完后移除是为了防止如果接受到内存警告后下载的图片都已清除,但是下载任务的字典的值为移除就不会再一次去下载图片
    • 缓存下载的图片到沙盒中

    • 获取图片下载路径最后一个分隔符后面的字符串作为图片名保存

stringByAppendingPathComponentstringByAppendingString 两个方法有区别

stringByAppendingString 方法是获取caches文件名,在文件夹名后面拼接字符串,在caches上一级目录下创建文件,和caches文件夹属于同一个级别

// 获取caches文件路径
NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);

NSString *URL = @"http://wwww.baidu.com/image/123456.png";

NSString *imageName = [URL lastPathComponent];

// 拼接路径
// 此方法生成的路径在caches文件下目录下
NSString *filePath = [cachesPath stringByAppendingPathComponent];


// 读取图片
if (内存中没有) {
    // 沙盒读取
    UIImage *sandBoxImage = [UIImage imageWithContetsOfFile:filePath];

    **加入到内存中,方便下次加载**
} 

if (sandBoxImage == nil) {
    // 下载图片
}

// 写入到沙盒
if (imageData != nil) {
    [imageData writeToFile: filePath atomically:NO];
}

Quartz 2D绘制形状(二)

使用Quartz 2D绘制圆弧,扇形,下载进度以及绘制的图形的填充和描边属性的设置.
将贝塞尔路径添加到图形上下文中
图形上下文中的状态保存和恢复
对图形上下文进行矩阵操作(平移,缩放,选转),实现一些特殊效果

使用贝塞尔曲线绘制矩形

1
2
3
4
5
6
7
{
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 20, 30)];
[[UIColor magentaColor] setStroke];
[[UIColor brownColor] setFill];
[path fill]; // 填充
[path stroke]; // 描边
}

圆角矩形

1
2
3
4
5
6
7
8
// 绘制圆角矩形
{
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(35, 10, 50, 70) cornerRadius:5.0];
[[UIColor magentaColor] setStroke];
[[UIColor cyanColor] setFill];
[path fill];
[path stroke];
}

绘制圆

1
2
3
4
5
6
7
8
// 画圆
{
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(90, 5, 40, 40) cornerRadius:20.0];
[[UIColor purpleColor] setStroke];
[[UIColor magentaColor] setFill];
[path fill];
[path stroke];
}

绘制扇形

1
2
3
4
5
6
CGPoint center = CGPointMake(200, 50);
UIBezierPath *sectorPath = [UIBezierPath bezierPathWithArcCenter:center radius:100 startAngle:0 endAngle:M_PI_4 clockwise:YES];
[sectorPath addLineToPoint:center];
[[UIColor brownColor] setStroke];
[sectorPath fill];
[sectorPath stroke];

绘制圆弧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 绘制圆弧
* 参数1: 圆心坐标
* 参数2: 半径
* 参数3: 开始角度
* 参数4: 结束角度 使用弧度制表示
* 参数5: clockwis顺时针方法的
*/

UIBezierPath *circuLarArcPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 60) radius:50.0 startAngle:0 endAngle:M_PI_2 clockwise:YES];

[circuLarArcPath moveToPoint:CGPointMake(100, 60)];

[[UIColor magentaColor] setStroke];
[[UIColor brownColor] setFill];
[circuLarArcPath stroke];
[circuLarArcPath fill];

绘制文字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 绘制文字
* drawAtPoint 不能换行,当文字过多过长时不会有效果
* 使用 drawInRect 可以实现换行
*/


NSString *text = @"我是绘制文字价格就看过几个框架刚开始大家高考的感觉是快乐的关键时刻大家赶快来时的结果看了风水格局来看就哭了感觉树大根深来对抗肌肤抵抗力";

NSMutableDictionary *attributeDict = [NSMutableDictionary dictionary];
attributeDict[NSFontAttributeName] = [UIFont systemFontOfSize:18];

// 字体颜色
attributeDict[NSForegroundColorAttributeName] = [UIColor greenColor];
// 描边颜色
attributeDict[NSStrokeColorAttributeName] = [UIColor yellowColor];
// 描边宽度
attributeDict[NSStrokeWidthAttributeName] = @1;
// 阴影
NSShadow *shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor redColor];
shadow.shadowOffset = CGSizeMake(1.0, 1.0);

attributeDict[NSShadowAttributeName] = shadow;

// 可以换行
[text drawInRect:self.bounds withAttributes:attributeDict];

// 不换行
[text drawAtPoint:CGPointZero withAttributes:attributeDict];

绘制图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 绘制图片
* drawAtPoint 默认绘制的内容尺寸和图片的尺寸一样大
*
*/

- (void)drawImage {

UIImage *image = [UIImage imageNamed:@"wc_dk"];
UIImage *image1 = [UIImage imageNamed:@"wc_hq"];

[image drawAtPoint:CGPointMake(10, 90)];
[image1 drawAtPoint:CGPointMake(70, 80)];

// 绘制在控件内部,和控件大小一致
[image1 drawInRect:self.bounds];

// 以平铺的方式绘制在控件的内部
[image1 drawAsPatternInRect:self.bounds];
}

圆形下载进度的绘制

drawRect 方法不能手动调用,因为图形上下文不能手动创建
需要调用setNeedsDisplay方法来重绘,系统会创建图形上下文
绘制时用到的定时器,一般不使用NSTimer定时器,因为调度优先级比较低,不会被准时带,界面会出现卡顿的现象
CADisplayLink 每次刷新屏幕的时候就会调用,1s刷新 60 次
setNeedsDisplay调用此方法时并不是立即调用drawRect方法,只是给给当前控件添加刷新标记,直到下一次屏幕刷新的时候才调用drawRect 方法,正好与CADisplayLink 方法一致,所以界面流畅不会卡顿
图形上下文状态栈

UIBezierPath图形上下文和CGContextRef的图形上下文不是同一个图形上下文

可以将贝塞尔路径添加到图形上下文中

也可以保存当前的图形上下文,CGContextSaveGState(ctx);
在下一次使用时可以恢复,CGContextRestoreGState(ctx);
画两种不同状态的线条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* 图形上下文栈的理解
* 画两条状态不同的线段
*/

// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 第一条
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10, 60)];
[path addLineToPoint:CGPointMake(240, 60)];

// 把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);

// 保存上下的状态
CGContextSaveGState(ctx);

// 设置当前状态
[[UIColor greenColor] setStroke];
//此时设置path 的状态无效,需要设置当前上下文的状态
path.lineWidth = 5.0;
CGContextSetLineWidth(ctx, 5.0);

// 渲染上下文
CGContextStrokePath(ctx);


// 第二根

// 可使用第一根的路径,因为每次绘制完成之后都会清除
path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(60, 10)];
[path addLineToPoint:CGPointMake(60, 240)];

// 添加路径到上下文 需要转换为CGPath
CGContextAddPath(ctx, path.CGPath);

// 还原之前保存的上下文状态
CGContextRestoreGState(ctx);
CGContextStrokePath(ctx);
图形上下文矩阵

// 使用矩阵

// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 使用贝塞尔路径
UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-150, -100, 300, 200)];
[[UIColor cyanColor] setFill];



// 以上绘制的图形有一部分看不到,通过对上下文矩阵操作来实现,平移上下文
// 操作矩阵必须在添加路径之前

// 平移
CGContextTranslateCTM(ctx, 150, 100);

// 缩放
CGContextScaleCTM(ctx, 0.5, 0.5);

// 旋转
// 可以实现点无法计算时的效果
CGContextRotateCTM(ctx, M_PI_4);



// 添加路径到上下文
CGContextAddPath(ctx, ovalPath.CGPath);

// 操作矩阵放在添加路径之后无效果
// CGContextTranslateCTM(ctx, 150, 100);

CGContextFillPath(ctx);

// 正常的绘制椭圆
// UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 20, 200, 100)];
// ovalPath.lineWidth = 4.0;
// [[UIColor magentaColor] setFill];
// [[UIColor yellowColor] setStroke];
// [ovalPath stroke];
// [ovalPath fill];

GCD实现按钮定时亮起

某些时候需要按钮不能连续点击,需要在一定的时间之后才可以允许交互,或者是实现类似发送验证码的按钮效果,具体做法是采用定时器

使用GCD定时器

创建定时器对象

__block NSInteger time = 30; // 需要强应用

创建队列

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

创建dispatch源(定时器)

(dispatach_source…timer..)
  • 01参数:要创建的source 是什么类型的,

    (DISPATCH_SOURCE_TYPE_TIMER)定时器

  • 04参数:队列 —-线程 决定block 在哪个线程中调用

代码

dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

设置定时器

  • 01参数:定时器对象

  • 02参数:开始时间 (DISPATCH_TIME_NOW) 什么时候开始执行第一次任务

  • 03参数:间隔时间 GCD时间单位:纳秒

  • 04参数:leewayInSeconds精准度:允许的误差: 0 表示绝对精准

code:

dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0);

定时器每隔一段时间就要执行任务(block回调)

dispatch_source_set_event_handler(_timer, ^{
    if (time <= 0) {
        dispatch_source_cancel(_timer);
        dispatch_async(dispatch_get_main_queue(), ^{

            // 设置按钮的样式
            [self.button setTitle:@"重新获取验证码" forState:UIControlStateNormal];
            [self.button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
            [self.button setUserInteractionEnabled:YES];
        });
    } else {

        NSInteger seconds = time;
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.button setTitle:[NSString stringWithFormat:@"重新发送(%.2ld)", seconds] forState:UIControlStateNormal];
            [self.button setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
            [self.button setUserInteractionEnabled:NO];
        });
        time--;
    }
});

启动定时器(默认是停止的)

dispatch_resume(timer);