版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2018.01.21 |
前言
在蘋(píng)果的API文檔中惧财,有很多屬性和方法我們用的不是很多泌射,所以很容易忽略和出錯(cuò)国裳,下面我就用這一個(gè)專(zhuān)題專(zhuān)門(mén)說(shuō)一些不常用的API接口,下面開(kāi)始盗温。感興趣的可以參考前面幾篇文章。
1. 容易忽略的那些小點(diǎn)總結(jié) (一) —— UIView UIViewTintAdjustmentMode相關(guān)(一)
doubleSided
這是一個(gè)屬性成肘,看一下API
/* When false layers facing away from the viewer are hidden from view.
* Defaults to YES. Animatable. */
@property(getter=isDoubleSided) BOOL doubleSided;
它的作用就是:來(lái)控制圖層的背面是否要被繪制卖局。這是一個(gè)BOOL類(lèi)型,默認(rèn)為YES双霍,如果設(shè)置為NO砚偶,那么當(dāng)圖層正面從相機(jī)視角消失的時(shí)候,它將不會(huì)被繪制洒闸。
圖層有雙面染坯,是否都顯示,設(shè)置NO意思背面看不到丘逸。下圖是兩個(gè)圖層分別設(shè)置doubleSided
為NO和YES翻轉(zhuǎn)180°的效果单鹿。
+ (nullable id)defaultValueForKey:(NSString *)key
先看一下這個(gè)靜態(tài)方法
/** Property methods. **/
/* CALayer implements the standard NSKeyValueCoding protocol for all
* Objective C properties defined by the class and its subclasses. It
* dynamically implements missing accessor methods for properties
* declared by subclasses.
*
* When accessing properties via KVC whose values are not objects, the
* standard KVC wrapping conventions are used, with extensions to
* support the following types:
*
* C Type Class
* ------ -----
* CGPoint NSValue
* CGSize NSValue
* CGRect NSValue
* CGAffineTransform NSValue
* CATransform3D NSValue */
/* Returns the default value of the named property, or nil if no
* default value is known. Subclasses that override this method to
* define default values for their own properties should call `super'
* for unknown properties. */
/*
CALayer為所有類(lèi)及其子類(lèi)定義的Objective C屬性實(shí)現(xiàn)了標(biāo)準(zhǔn)的NSKeyValueCoding協(xié)議。
它動(dòng)態(tài)地為子類(lèi)聲明的屬性實(shí)現(xiàn)缺少的訪(fǎng)問(wèn)器方法深纲。
當(dāng)通過(guò)KVC訪(fǎng)問(wèn)不是對(duì)象的屬性時(shí)仲锄,使用標(biāo)準(zhǔn)的KVC包裝約定,擴(kuò)展名為支持以下幾種類(lèi)型:
C Type Class
------ -----
CGPoint NSValue
CGSize NSValue
CGRect NSValue
CGAffineTransform NSValue
CATransform3D NSValue
返回指定屬性的默認(rèn)值湃鹊,如果沒(méi)有默認(rèn)值儒喊,則返回nil。 重寫(xiě)此方法以定義其屬性的默認(rèn)值
的子類(lèi)應(yīng)該為未知屬性調(diào)用“super”
*/
+ (nullable id)defaultValueForKey:(NSString *)key;
例如:我們新建一個(gè)SubLayer
類(lèi)繼承自CALayer
币呵,則在SubLayer.m
中重寫(xiě)此方法怀愧。如下:
+ (id)defaultValueForKey:(NSString *)key
{
if ([key isEqualToString:@"backgroundColor"]) {
return (id)[UIColor blackColor].CGColor;
}
if ([key isEqualToString:@"cornerRadius"]) {
return @20.0;
}
return [super defaultValueForKey:key];
}
然后,我們?cè)赩C里面的view上添加一個(gè)SubLayer類(lèi)型的layer。代碼如下:
SubLayer *subLayer = [SubLayer layer];
subLayer.frame = CGRectMake(0,0,40,40);
subLayer.position = CGPointMake(100, 30);
[self.view.layer addSublayer:subLayer];
運(yùn)行起來(lái)芯义,就會(huì)發(fā)現(xiàn)
正好是一個(gè)黑色的圓形哈垢。
我們也可以在SubLayer中重寫(xiě)- (instancetype)init
代碼如下:
- (instanceType)init
{
self = [super init];
if(self){
self.transform=CATransform3DMakeRotation(M_PI_2, 1, 1, 1);
}
}
SubLayer *subLayer = [SubLayer layer];
subLayer.frame = CGRectMake(0,0,40,40);
subLayer.position = CGPointMake(100, 30);
[self.view.layer addSublayer:subLayer];
這樣我們做出來(lái)的SubLayer就會(huì)旋轉(zhuǎn)90度,具體效果如下所示毕贼。
+ (BOOL)needsDisplayForKey:(NSString *)key;
還是先看一下API
/* Method for subclasses to override. Returning true for a given
* property causes the layer's contents to be redrawn when the property
* is changed (including when changed by an animation attached to the
* layer). The default implementation returns NO. Subclasses should
* call super for properties defined by the superclass. (For example,
* do not try to return YES for properties implemented by CALayer,
* doing will have undefined results.) */
/*
子類(lèi)重寫(xiě)的方法温赔。 對(duì)給定的屬性,當(dāng)屬性發(fā)生更改(包括由附加到圖層的動(dòng)畫(huà)更改)時(shí)鬼癣,
將導(dǎo)致圖層的內(nèi)容重繪陶贼,這時(shí)返回true。默認(rèn)實(shí)現(xiàn)返回NO待秃。 子類(lèi)應(yīng)該為超類(lèi)定義的屬性
調(diào)用super拜秧。 (例如,對(duì)于CALayer實(shí)現(xiàn)的屬性不要試圖返回YES是由章郁,這樣做會(huì)有未知的結(jié)果枉氮。)
*/
+ (BOOL)needsDisplayForKey:(NSString *)key;
首先了解下layer自己的屬性如何實(shí)現(xiàn)動(dòng)畫(huà)。
layer首次加載時(shí)會(huì)調(diào)用
+(BOOL)needsDisplayForKey:(NSString *)key
方法來(lái)判斷當(dāng)前指定的屬性key改變是否需要重新繪制暖庄。當(dāng)
Core Animartion
中的key
或者keypath
等于+(BOOL)needsDisplayForKey:(NSString *)key
方法中指定的key聊替,便會(huì)自動(dòng)調(diào)用setNeedsDisplay
方法,這樣就會(huì)觸發(fā)重繪培廓,達(dá)到我們想要的效果惹悄。
下面我們就看一下layer方法響應(yīng)鏈
[layer setNeedDisplay] -> [layer displayIfNeed] -> [layer display] -> [layerDelegate displayLayer:]
[layer setNeedDisplay] -> [layer displayIfNeed] -> [layer display] -> [layer drawInContext:] -> [layerDelegate drawLayer: inContext:]
說(shuō)明一下,如果layerDelegate實(shí)現(xiàn)了displayLayer:
協(xié)議肩钠,之后layer就不會(huì)再調(diào)用自身的重繪代碼泣港。一會(huì)的示例代碼中就采用第二種響應(yīng)鏈進(jìn)行繪制。
下面看一下示例代碼
1. ViewController.m
#import "ViewController.h"
#import "JJCircleLayer.h"
@interface ViewController ()
@property (nonatomic, strong) JJCircleLayer *layer;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.layer = [[JJCircleLayer alloc] init];
self.layer.frame = CGRectMake(100, 200, 200, 200);
self.layer.backgroundColor = [UIColor lightGrayColor].CGColor;
[self.view.layer addSublayer:self.layer];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.layer animateCircle];
}
@end
2. JJCircleLayer.h
#import <QuartzCore/QuartzCore.h>
@interface JJCircleLayer : CALayer
- (void)animateCircle;
@end
3. JJCircleLayer.m
#import "JJCircleLayer.h"
#import <UIKit/UIKit.h>
@interface JJCircleLayer() <CAAnimationDelegate>
@property (nonatomic, assign) CGFloat progress;
@end
@implementation JJCircleLayer
#pragma mark - Override Base Function
+ (BOOL)needsDisplayForKey:(NSString *)key
{
BOOL result;
if ([key isEqualToString:@"progress"]) {
result = YES;
}
else {
result = [super needsDisplayForKey:key];
}
return result;
}
- (void)drawInContext:(CGContextRef)ctx
{
NSLog(@"progress: %f", self.progress);
CGContextSetLineWidth(ctx, 5.0f);
CGContextSetStrokeColorWithColor(ctx, [UIColor blueColor].CGColor);
CGContextAddArc(ctx, CGRectGetWidth(self.bounds) * 0.5, CGRectGetHeight(self.bounds) * 0.5, CGRectGetWidth(self.bounds) * 0.5 - 6, 0, 2 * M_PI * self.progress, 0);
CGContextStrokePath(ctx);
}
#pragma mark - Object Public Function
- (void)animateCircle
{
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"progress"];
anim.values = [self valuesListWithAnimationDuration: 3];
anim.duration = 3.0;
anim.fillMode = kCAFillModeForwards;
anim.removedOnCompletion = NO;
anim.delegate = self;
[self addAnimation:anim forKey:@"circle"];
}
#pragma mark - Object Private Function
- (NSMutableArray *)valuesListWithAnimationDuration:(CGFloat)duration
{
NSInteger numberOfFrames = duration * 60;
NSMutableArray *values = [NSMutableArray array];
// 注意這里的 fromValue和toValue是針對(duì)的progress的值的大小价匠。
CGFloat fromValue = 0.0;
CGFloat toValue = 1.0;
CGFloat diff = toValue - fromValue;
for (NSInteger frame = 1; frame <= numberOfFrames; frame++) {
CGFloat piece = (CGFloat)frame / (CGFloat)numberOfFrames;
CGFloat currentValue = fromValue + diff * piece;
[values addObject:@(currentValue)];
}
return values;
}
#pragma mark - CAAnimationDelegate
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
[self removeAnimationForKey:@"circle"];
self.progress = 1.0;
[self setNeedsDisplay];
}
@end
下面看一下效果
這里還有幾點(diǎn)需要注意当纱,對(duì)于needsDisplayForKey
方法:
- 此方法只會(huì)在圖層初始化的時(shí)候被調(diào)用一次。
- 代碼中通過(guò)判斷圖層的屬性名稱(chēng)來(lái)決定是否需要對(duì)對(duì)應(yīng)的
Core Animation
動(dòng)畫(huà)執(zhí)行UI重繪工作(本例中就是對(duì)自定義的progress屬性進(jìn)行處理)踩窖。 -
[super needsDisplayForKey:key];
這個(gè)父類(lèi)方法默認(rèn)的返回值是NO坡氯。 - 因?yàn)樵?code>needsDisplayForKey方法中指定了key的值是progress,所以這里的
animationWithKeyPath
動(dòng)畫(huà)操作會(huì)在動(dòng)畫(huà)執(zhí)行期間洋腮,不停的促發(fā)Core Graphics
的重繪工作廉沮,即不停的調(diào)用- (void)drawInContext:(CGContextRef)ctx
方法進(jìn)行繪制。 -
fillMode
和removeOnCompletion
兩個(gè)屬性指定動(dòng)畫(huà)在繪制完成后徐矩,對(duì)應(yīng)的動(dòng)畫(huà)對(duì)象不會(huì)從內(nèi)存中移除掉滞时。
后記
本篇已結(jié)束,下一篇更精彩~~~