大发体育娱乐在线-大发体育娱乐官方网站-大发体育娱乐登录网址
做最好的网站

从VVeboTableView德姆o到YYAsyncLayer(二)

来源:http://www.dfwstonefabricators.com 作者:编程应用 人气:132 发布时间:2019-09-19
摘要:本节关键字 异步绘制 RunLoop YYAsyncLayer目录结构 这是 YYAsyncLayer 的结构 YYAsyncLayer:异步绘制的CALayer子类,这一个类做的核心和 VVeboTableViewDemo 中 VVeboLabel 的大旨是一模二样的。你能够到

本节关键字

  • 异步绘制
  • RunLoop

图片 1YYAsyncLayer目录结构

这是YYAsyncLayer的结构

  • YYAsyncLayer:异步绘制的CALayer子类,这一个类做的核心和VVeboTableViewDemoVVeboLabel 的大旨是一模二样的。你能够到回去拜候从VVeboTableView德姆o到YYAsyncLayer

  • YYSentinel:线程安全的计数器。

  • YYTransaction:注册RunLoop在系统空闲时调用。(如果您不驾驭依旧尚未耳闻过RunLoop,不用担忧,上面笔者同样会推荐相关的篇章,让您询问和推行RunLoop)

  • YYAsyncLayerDisplayTask: 用于回调画布

  • YYAsyncLayerDelegate: 给接收YYAsyncLayer的UIView开的接口

上学地点:https://github.com/AttackOnDobby/iOS-Core-Animation-Advanced-Techniques
读书CoreAnimation 的第一步是上学CALayer,因为对三个VIew增加CoreAnimation动画,本质上是对view.lay实行动画操作.

正文还是是用Swift版YYAsyncLayer扩充剖析

YYAsyncLayer

图片 2荧屏快照2017-04-14 凌晨5.10.56.png

上海教室中高亮的主意为一体类的着力措施

private func _displayAsync(_ async: Bool) { /// 如果需要使用异步绘制的地方没有实现该代理,直接返回 guard let mydelegate = delegate as? YYAsyncLayerDelegate else { return } /// 接收来自需要异步绘制类的任务对象 let task = mydelegate.newAsyncDisplayTask /// 如果display闭包为空,直接返回 if task.display == nil { task.willDisplay? contents = nil task.didDisplay?(self, true) return } // 是否需要异步绘制,默认是开启异步绘制的 if async { /// 绘制将要开始 task.willDisplay? /// https://github.com/ibireme/YYAsyncLayer/issues/6 /* 一个Operation/Task对应唯一一个isCancelled,在NSOperation中是函数调用,在这里是这个isCancelled block。所以每次提交到queue的task的isCancelled block是不同的block对象,其中捕获的value的值都是这个task创建时sentinel.value的值,而捕获的sentinel的引用都是这个layer的sentinel的引用,最后在block执行的时候,value的值就是捕获的value,而sentinel.value则可能已经发生了变化。 */ let sentinel = _sentinel let value = sentinel!.value let isCancelled:  -> Bool) = { return value != sentinel!.value } let size = bounds.size let opaque = isOpaque let scale = contentsScale let backgroundColor = (opaque && self.backgroundColor != nil) ? self.backgroundColor : nil /// 太小不绘制 if size.width < 1 || size.height < 1 { var image = contents contents = nil if image != nil { YYAsyncLayerGetReleaseQueue.async { image = nil } } task.didDisplay?(self, true) return } /// 将绘制操作放入自定义队列中 YYAsyncLayerGetDisplayQueue.async { if isCancelled() { return } /// 第一个参数表示所要创建的图片的尺寸; /// 第二个参数用来指定所生成图片的背景是否为不透明,如上我们使用true而不是false,则我们得到的图片背景将会是黑色,显然这不是我想要的; /// 第三个参数指定生成图片的缩放因子,这个缩放因子与UIImage的scale属性所指的含义是一致的。传入0则表示让图片的缩放因子根据屏幕的分辨率而变化,所以我们得到的图片不管是在单分辨率还是视网膜屏上看起来都会很好。 /// 注意这个与UIGraphicsEndImageContext()成对出现 /// iOS10 中新增了UIGraphicsImageRenderer(bounds: _) UIGraphicsBeginImageContextWithOptions(size, opaque, scale) /// 获取绘制画布 /// 每一个UIView都有一个layer,每一个layer都有个content,这个content指向的是一块缓存,叫做backing store。 /// UIView的绘制和渲染是两个过程,当UIView被绘制时,CPU执行drawRect,通过context将数据写入backing store /// http://vizlabxt.github.io/blog/2012/10/22/UIView-Rendering/ guard let context = UIGraphicsGetCurrentContext() else { return } if opaque { /* 成对出现 CGContextSaveGState与CGContextRestoreGState的作用 使用Quartz时涉及到一个图形上下文,其中图形上下文中包含一个保存过的图形状态堆栈。在Quartz创建图形上下文时,该堆栈是空的。CGContextSaveGState函数的作用是将当前图形状态推入堆栈。之后,您对图形状态所做的修改会影响随后的描画操作,但不影响存储在堆栈中的拷贝。在修改完成后。 您可以通过CGContextRestoreGState函数把堆栈顶部的状态弹出,返回到之前的图形状态。这种推入和弹出的方式是回到之前图形状态的快速方法,避免逐个撤消所有的状态修改;这也是将某些状态恢复到原有设置的唯一方式。 */ context.saveGState() if backgroundColor == nil || backgroundColor!.alpha < 1 { context.setFillColor(UIColor.white.cgColor) // 设置填充颜色,setStrokeColor为边框颜色 context.addRect(CGRect(x: 0, y: 0, width: size.width * scale, height: size.height * scale)) context.fillPath() // 填充路径 // 上面两句与这句等效// context.fill(CGRect(x: 0, y: 0, width: size.width * scale, height: size.height * scale)) } if let backgroundColor = backgroundColor { context.setFillColor(backgroundColor) context.addRect(CGRect(x: 0, y: 0, width: size.width * scale, height: size.height * scale)) context.fillPath() } context.restoreGState() } // 回调绘制 task.display?(context, size, isCancelled) // 如果取消,提前结束绘制 if isCancelled() { UIGraphicsEndImageContext() DispatchQueue.main.async { task.didDisplay?(self, false) } return } // 从画布中获取图片,与UIGraphicsEndImageContext()成对出现 let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() // 如果取消,提前结束绘制 if isCancelled() { DispatchQueue.main.async { task.didDisplay?(self, false) } return } DispatchQueue.main.async { if isCancelled() { task.didDisplay?(self, false) } else { // 绘制成功 self.contents = image?.cgImage task.didDisplay?(self, true) } } } } else { // 同步绘制 _sentinel.increase() task.willDisplay? UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, contentsScale) guard let context = UIGraphicsGetCurrentContext() else { return } if isOpaque { var size = bounds.size size.width *= contentsScale size.height *= contentsScale context.saveGState() if backgroundColor == nil || backgroundColor!.alpha < 1 { context.setFillColor(UIColor.white.cgColor) context.addRect(CGRect(origin: .zero, size: size)) context.fillPath() } if let backgroundColor = backgroundColor { context.setFillColor(backgroundColor) context.addRect(CGRect(origin: .zero, size: size)) context.fillPath() } context.restoreGState() } task.display?(context, bounds.size, {return false }) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() contents = image?.cgImage task.didDisplay?(self, true) } }

举个例子你去对待从VVeboTableView德姆o到YYAsyncLayer中的VVeboLabel,他们的大旨理想其实是一模一样的

YYTransaction

以此类和别的多少个类是独立的。那么他是干嘛用的了?笔者营造他的理由是怎么吗?大家看看那张图:(在随机档期的顺序的func viewDidLoad()中打个断点,你的酒店消息大致正是如此的)

图片 3显示器快速照相2017-04-17 中午2.28.54.png

图中有个CATransaction 的东西,似乎和YYTransaction很一般。其实他们不唯有命名很相似,正是内部结构也很相似。再看YYTransaction

图片 4显示器快速照相2017-04-17 早上2.35.41.png

其中YYTransactionSetup

func YYTransactionSetup() { DispatchQueue.once(token: onceToken) { transactionSet = Set() /// 获取main RunLoop let runloop = CFRunLoopGetMain() var observer: CFRunLoopObserver? /// http://www.jianshu.com/p/6757e964b956 /// 创建一个RunLoop的观察者 /// allocator:该参数为对象内存分配器,一般使用默认的分配器kCFAllocatorDefault。或者nil /// activities:该参数配置观察者监听Run Loop的哪种运行状态。在示例中,我们让观察者监听Run Loop的所有运行状态。 /// repeats:该参数标识观察者只监听一次还是每次Run Loop运行时都监听。 /// order: 观察者优先级,当Run Loop中有多个观察者监听同一个运行状态时,那么就根据该优先级判断,0为最高优先级别。 /// callout:观察者的回调函数,在Core Foundation框架中用CFRunLoopObserverCallBack重定义了回调函数的闭包。 /// context:观察者的上下文。 (类似与KVO传递的context,可以传递信息,)因为这个函数创建ovserver的时候需要传递进一个函数指针,而这个函数指针可能用在n多个oberver 可以当做区分是哪个observer的状机态。(下面的通过block创建的observer一般是一对一的,一般也不需要Context,),还有一个例子类似与NSNOtificationCenter的 SEL和 Block方式。 observer = CFRunLoopObserverCreate( kCFAllocatorDefault, CFRunLoopActivity.beforeWaiting.rawValue | CFRunLoopActivity.exit.rawValue, true, 0xFFFFFF, YYRunLoopObserverCallBack, nil ) //将观察者添加到主线程runloop的common模式下的观察中 CFRunLoopAddObserver(runloop, observer, .commonModes) observer = nil }}

到这里,大家能够认为到YYTransaction的用处和CATransaction的用处是有某种相似之处的。

再看

苹果对CATransaction的概念(你也能够看看这本书里的解释)

A mechanism for batching multiple layer-tree operations into atomic updates to the render tree.

**谷歌(Google)翻译: ** 用于将多少个层树操作批量化为渲染树的原子更新的建制。

政工是经过CATransaction类来做管理,这一个类的安顿性有个别不敢相信 无法相信,不像您从它的命名预期的那么去管理三个总结的作业,而是处理了一叠你无法采访的业务。CATransaction未有品质也许实例方法,并且也不可能用+alloc和-init方法创设它。但是足以用+begin和+commit分别来入栈也许出栈。任何能够做动画的图层属性都会被增加到栈顶的思想政治工作,你可以透过+setAnimationDuration:方法设置当前业务的动画时间,只怕经过+animationDuration方法来得到值。Core Animation在各类run loop周期中自动初叶一次新的事务(run loop是iOS担负收罗客商输入,管理计时器大概互联网事件同期重新绘制显示屏的东西),尽管你不显式的用[CATransaction begin]初叶一次工作,任何在壹次run loop巡回中属性的变动都会被聚集起来,然后做二遍0.25秒的动画片。

由此这段解释,大家能够获得首要新闻是:CATransaction是用了对事物来做管理的。Core Animation在各类run loop周期中自动初叶贰回新的业务

在此间你无需恐惧RunLoop,即使大家一些也不通晓,下边包车型客车代码也是能够看懂的

let YYRunLoopObserverCallBack: CFRunLoopObserverCallBack = {_,_,_ in if (transactionSet?.count ?? 0) == 0 { return } let currentSet = transactionSet transactionSet = Set() for item in currentSet! { _ = (item.target as? NSObject)?.perform(item.selector) }}func YYTransactionSetup() { DispatchQueue.once(token: onceToken) { transactionSet = Set() /// 获取main RunLoop let runloop = CFRunLoopGetMain() var observer: CFRunLoopObserver? /// http://www.jianshu.com/p/6757e964b956 /// 创建一个RunLoop的观察者 /// allocator:该参数为对象内存分配器,一般使用默认的分配器kCFAllocatorDefault。或者nil /// activities:该参数配置观察者监听Run Loop的哪种运行状态。在示例中,我们让观察者监听Run Loop的所有运行状态。 /// repeats:该参数标识观察者只监听一次还是每次Run Loop运行时都监听。 /// order: 观察者优先级,当Run Loop中有多个观察者监听同一个运行状态时,那么就根据该优先级判断,0为最高优先级别。 /// callout:观察者的回调函数,在Core Foundation框架中用CFRunLoopObserverCallBack重定义了回调函数的闭包。 /// context:观察者的上下文。 (类似与KVO传递的context,可以传递信息,)因为这个函数创建ovserver的时候需要传递进一个函数指针,而这个函数指针可能用在n多个oberver 可以当做区分是哪个observer的状机态。(下面的通过block创建的observer一般是一对一的,一般也不需要Context,),还有一个例子类似与NSNOtificationCenter的 SEL和 Block方式。 observer = CFRunLoopObserverCreate( kCFAllocatorDefault, CFRunLoopActivity.beforeWaiting.rawValue | CFRunLoopActivity.exit.rawValue, true, 0xFFFFFF, YYRunLoopObserverCallBack, nil ) //将观察者添加到主线程runloop的common模式下的观察中 CFRunLoopAddObserver(runloop, observer, .commonModes) observer = nil }}

轻易了然这段代码的最首要职能:

  • 是观察RunLoop的情形为beforeWaitingexit时举行回调。
  • 其中selector实在正是CATransaction中的事物,target纵然推行selector的对象

只顾:动画并无需你在Core Animation中手动展开,相反须要精通地关闭,不然他会一贯存在。

结论

那现在大家就足以基本清楚了YYTransaction的功力就是,把你需求推行的点子,先存款和储蓄起来,等到RunLoop的气象为beforeWaitingexit时统一实践。

因此这两篇源码的深入分析,异步绘制那么些定义应该在大脑里曾经有了自然的纪念了,稍加演练,其实就能够掌握领会。

YYAsyncLayer的大旨便是那些了,其实通篇看下来,你会意识中央没有啥样费脑的地点。在倾倒小编的还要,大家越来越多的是需求反思本身,即使各种人的先性情区别,不过我们的不竭程度之低,往往没到拼天赋那一步。

在下一篇小说中自己会挨个对这一个进展回复,并且贴出它们的规律,与大家一道真正调控iOS优化

  • 何以须要60fps?
  • 缘何要削减混合?
  • 干什么要防止离屏渲染?
  • UIView和CALayer的关系?
  • 何以在4过后推文(Tweet)的绘图方案不能够升高品质了?......

RunLoop:深深明白RunLoopiOS线下分享《RunLoop》iOS RunLoop 编制程序手册 runloop原理

YYAsyncLayer使用:

其他iOS Core Animation: Advanced Techniques汉语译本

隐式动画#

所谓的隐式动画片。之所以叫隐式是因为我们并未点名别的动画的种类。大家仅仅转移了八个天性,然后Core Animation来调节哪些何况曾几何时去做动画。

本质:Core Animation在每个run loop周期中活动起初三回新的政工(run loop是iOS担负收集客商输入,管理停车计时器或许网络事件同不经常间重新绘制荧屏的事物),纵然你不显式的用[CATransaction begin]
起来一次事情,任何在二次run loop巡回中属性的改观都会被集中起来,然后做一次0.25秒的卡通片。
<code><pre>
@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *layerView;

@property (nonatomic, weak) IBOutlet CALayer *colorLayer;

@end

@implementation ViewController

- (void)viewDidLoad{
   [super viewDidLoad]; 
  //create sublayer self.colorLayer = [CALayer layer]; 
   self.colorLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f); 
   self.colorLayer.backgroundColor = [UIColor blueColor].CGColor; 
//add it to our view
   [self.layerView.layer addSublayer:self.colorLayer];

}

  • (IBAction)changeColor{
    //randomize the layer background color CGFloat red = arc4random() / (CGFloat)INT_MAX;

       CGFloat green = arc4random() / (CGFloat)INT_MAX; 
    
       CGFloat blue = arc4random() / (CGFloat)INT_MAX; 
    
       self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
    

}

@end
</code></pre>

一:学习第一个类:CATransaction类

(transaction 英 træn'zækʃ(ə)n)有职业的情致
事务 实际上是Core Animatino 用来含有一文山会海属性动画集结的机制.任何用钦定 事务 去改变能够做动画的图层属性都不会立时发生变化,而是当事情一旦 提交 的时候初叶用一个动画片过渡到新值。

上边的代码创制的隐式动画正是非事务的动画.

瞩目:CATransaction类无法用allloc 和init的法子开创,不过能够因此 +begin 和 +commit 分别来入栈和出栈.

<code><pre>

  • (IBAction)changeColor{
    //begin a new transaction

[CATransaction begin];

//set the animation duration to 1 second

[CATransaction setAnimationDuration:1.0];

//add the spin animation on completion

[CATransaction setCompletionBlock:^{

//rotate the layer 90 degrees

CGAffineTransform transform = self.colorLayer.affineTransform; transform = CGAffineTransformRotate(transform, M_PI_2);

self.colorLayer.affineTransform = transform;
}];

//randomize the layer background color

CGFloat red = arc4random() / (CGFloat)INT_MAX;

CGFloat green = arc4random() / (CGFloat)INT_MAX; CGFloat blue = arc4random() / (CGFloat)INT_MAX;

self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor; //commit the transaction [CATransaction commit];
}
</code></pre>

小心旋转动画要比颜色渐变快得多,那是因为成功块是在颜色渐变的事务提交并出栈之后才被实施,于是,用暗中同意的事情做变换,默许的小时也就改为了0.25秒。

分解:这里能够领略为多个事情,一个是改动颜色,大家设定的动画时间是1.0秒,第二个业务正是在颜色渐变的政工提交之后,实行的私下认可事务时间. 是上边提到的runloop循环管理的暗许时间 0.25秒.

二:UIView图层上的CALayer动画

评释:在此以前的卡通片都做在独立的CALayer 增加到UIView上的.这里的CALayer 是和UIView关联的CALayer 通过self.view.layer 获取的.

UIView 上的CALayer ,UIView 都会私下认可成为CALayer 的CALayerDelegate代理对象.
默许实现了

-(id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event{

 return nil;
}

禁绝了隐式动画.这里能够领略下

当你转移CALayer的贰个可做动画的性情,它并不可能立刻在屏幕上体现出来。相反,它是从先前的值平滑过渡到新的值。这一切都以暗许的行为,你无需做额外的操作。

咱们在修改UIView中layer属性的时候,其实是默许有动画的,只是UIView通过落到实处地点的图层代理给禁掉了.

自然重返nil并非剥夺隐式动画独一的不二等秘书籍,CATransaction有个法子叫做+setDisableActions:,能够用来对具有属性张开也许关闭隐式动画。尽管在清单7.2的 [CATransaction begin] 之后增添上面包车型大巴代码,一样也会阻拦动画的发生:

[CATransaction setDisableActions:YES];

最重大的下结论一下,我们通晓了之类几点

  1. UIView关联的图层禁止使用了隐式动画,对这种图层做动画的独一方法就是使用UIView的卡通函数(并非信赖CATransaction),也许承继UIView,并覆盖-actionForLayer:forKey:方法,或然直接开立一个显式动画
  1. 对于单身存在的图层,大家得以由此兑现图层的-actionForLayer:forKey:委托方法,大概提供叁个actions字典来支配隐式动画。

备注: 单独图层的卡通片,退换

<code><pre>
@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *layerView;

@property (nonatomic, strong) CALayer *colorLayer;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

self.colorLayer = [CALayer layer];
self.colorLayer.frame = CGRectMake(50.0f, 50.0f,200.0f, 100.0f);
self.colorLayer.backgroundColor = [UIColor blueColor].CGColor;
//add a custom action
CATransition *transition = [CATransition animation];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;

///这里对actions赋值贰个自定对应上面总计的第二点就好了解了,对单独的图层的背景颜色增添动画,
//当颜色退换的时候,颜色更改时,设置的动画片

self.colorLayer.actions = @{@"backgroundColor": transition};
//add it to our view
[self.layerView.layer addSublayer:self.colorLayer];

}

- (IBAction)action:(id)sender {
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;

//commit the transaction
[CATransaction commit];

}
</code></pre>

显示模型

权且还非常小概知晓,先搁下

[self.colorLayer.presentationLayer hitTest:point]

未完待续......

本文由大发体育娱乐在线发布于编程应用,转载请注明出处:从VVeboTableView德姆o到YYAsyncLayer(二)

关键词:

上一篇:没有了

下一篇:直播疑难杂症排查

最火资讯