iOS——Core Animation 知识摘抄(二)
陰影
主要是shadowOpacity 、shadowColor、shadowOffset和shadowRadius四個(gè)屬性
shadowPath屬性
我們已經(jīng)知道圖層陰影并不總是方的,而是從圖層內(nèi)容的形狀繼承而來。這看上去不錯(cuò),但是實(shí)時(shí)計(jì)算陰影也是一個(gè)非常消耗資源的,尤其是圖層有多個(gè)子圖層,每個(gè)圖層還有一個(gè)有透明效果的寄宿圖的時(shí)候。
如果你事先知道你的陰影形狀會是什么樣子的,你可以通過指定一個(gè)shadowPath來提高性能。shadowPath是一個(gè)CGPathRef類型(一個(gè)指向CGPath的指針)。CGPath是一個(gè)Core Graphics對象,用來指定任意的一個(gè)矢量圖形。我們可以通過這個(gè)屬性單獨(dú)于圖層形狀之外指定陰影的形狀。
@interface ViewController () @property (nonatomic, weak) IBOutlet UIView *layerView1; @property (nonatomic, weak) IBOutlet UIView *layerView2; @end @implementation ViewController - (void)viewDidLoad {[super viewDidLoad];//enable layer shadowsself.layerView1.layer.shadowOpacity = 0.5f;self.layerView2.layer.shadowOpacity = 0.5f;//create a square shadowCGMutablePathRef squarePath = CGPathCreateMutable();CGPathAddRect(squarePath, NULL, self.layerView1.bounds);self.layerView1.layer.shadowPath = squarePath;
CGPathRelease(squarePath);
?//create a circular shadowCGMutablePathRef circlePath = CGPathCreateMutable();CGPathAddEllipseInRect(circlePath, NULL, self.layerView2.bounds);self.layerView2.layer.shadowPath = circlePath;
CGPathRelease(circlePath); } @end
如果是一個(gè)舉行或是圓,用CGPath會相當(dāng)簡單明了。但是如果是更加復(fù)雜一點(diǎn)的圖形,UIBezierPath類會更合適,它是一個(gè)由UIKit提供的在CGPath基礎(chǔ)上的Objective-C包裝類。
?
圖層蒙板
CALayer有一個(gè)屬性叫做mask可以解決這個(gè)問題。這個(gè)屬性本身就是個(gè)CALayer類型,有和其他圖層一樣的繪制和布局屬性。它類似于一個(gè)子圖層,相對于父圖層(即擁有該屬性的圖層)布局,但是它卻不是一個(gè)普通的子圖層。不同于那些繪制在父圖層中的子圖層,mask圖層定義了父圖層的部分可見區(qū)域。
mask圖層的Color屬性是無關(guān)緊要的,真正重要的是圖層的輪廓。mask屬性就像是一個(gè)餅干切割機(jī),mask圖層實(shí)心的部分會被保留下來,其他的則會被拋棄。(如圖4.12)
@interface ViewController () @property (nonatomic, weak) IBOutlet UIImageView *imageView; @end @implementation ViewController - (void)viewDidLoad {[super viewDidLoad];//create mask layerCALayer *maskLayer = [CALayer layer];maskLayer.frame = self.layerView.bounds;UIImage *maskImage = [UIImage imageNamed:@"Cone.png"];maskLayer.contents = (__bridge id)maskImage.CGImage;//apply mask to image layer?self.imageView.layer.mask = maskLayer; } @end?
CALayer蒙板圖層真正厲害的地方在于蒙板圖不局限于靜態(tài)圖。任何有圖層構(gòu)成的都可以作為mask屬性,這意味著你的蒙板可以通過代碼甚至是動畫實(shí)時(shí)生成。
?
組透明
iOS常見的做法是把一個(gè)空間的alpha值設(shè)置為0.5(50%)以使其看上去呈現(xiàn)為不可用狀態(tài)。對于獨(dú)立的視圖來說還不錯(cuò),但是當(dāng)一個(gè)控件有子視圖的時(shí)候就有點(diǎn)奇怪了,圖4.20展示了一個(gè)內(nèi)嵌了UILabel的自定義UIButton;左邊是一個(gè)不透明的按鈕,右邊是50%透明度的相同按鈕。我們可以注意到,里面的標(biāo)簽的輪廓跟按鈕的背景很不搭調(diào)。
?
理想狀況下,當(dāng)你設(shè)置了一個(gè)圖層的透明度,你希望它包含的整個(gè)圖層樹像一個(gè)整體一樣的透明效果。你可以通過設(shè)置Info.plist文件中的UIViewGroupOpacity為YES來達(dá)到這個(gè)效果,但是這個(gè)設(shè)置會影響到這個(gè)應(yīng)用,整個(gè)app可能會受到不良影響。如果UIViewGroupOpacity并未設(shè)置,iOS 6和以前的版本會默認(rèn)為NO(也許以后的版本會有一些改變)。
另一個(gè)方法就是,你可以設(shè)置CALayer的一個(gè)叫做shouldRasterize屬性(見清單4.7)來實(shí)現(xiàn)組透明的效果,如果它被設(shè)置為YES,在應(yīng)用透明度之前,圖層及其子圖層都會被整合成一個(gè)整體的圖片,這樣就沒有透明度混合的問題了(如圖4.21)。
為了啟用shouldRasterize屬性,我們設(shè)置了圖層的rasterizationScale屬性。默認(rèn)情況下,所有圖層拉伸都是1.0, 所以如果你使用了shouldRasterize屬性,你就要確保你設(shè)置了rasterizationScale屬性去匹配屏幕,以防止出現(xiàn)Retina屏幕像素化的問題。
當(dāng)shouldRasterize和UIViewGroupOpacity一起的時(shí)候,性能問題就出現(xiàn)了(我們在第12章『速度』和第15章『圖層性能』將做出介紹),但是性能碰撞都本地化了(譯者注:這句話需要再翻譯)。
@interface ViewController () @property (nonatomic, weak) IBOutlet UIView *containerView; @end @implementation ViewController - (UIButton *)customButton {//create buttonCGRect frame = CGRectMake(0, 0, 150, 50);UIButton *button = [[UIButton alloc] initWithFrame:frame];button.backgroundColor = [UIColor whiteColor];button.layer.cornerRadius = 10;//add labelframe = CGRectMake(20, 10, 110, 30);UILabel *label = [[UILabel alloc] initWithFrame:frame];label.text = @"Hello World";label.textAlignment = NSTextAlignmentCenter;[button addSubview:label];return button; } - (void)viewDidLoad {[super viewDidLoad];//create opaque buttonUIButton *button1 = [self customButton];button1.center = CGPointMake(50, 150);[self.containerView addSubview:button1];//create translucent buttonUIButton *button2 = [self customButton];?button2.center = CGPointMake(250, 150);button2.alpha = 0.5;[self.containerView addSubview:button2];//enable rasterization for the translucent buttonbutton2.layer.shouldRasterize = YES;button2.layer.rasterizationScale = [UIScreen mainScreen].scale; } @end仿射變換
當(dāng)對圖層應(yīng)用變換矩陣,圖層矩形內(nèi)的每一個(gè)點(diǎn)都被相應(yīng)地做變換,從而形成一個(gè)新的四邊形的形狀。CGAffineTransform中的“仿射”的意思是無論變換矩陣用什么值,圖層中平行的兩條線在變換之后任然保持平行,CGAffineTransform可以做出任意符合上述標(biāo)注的變換,圖5.2顯示了一些仿射的和非仿射的變換:
UIView可以通過設(shè)置transform屬性做變換,但實(shí)際上它只是封裝了內(nèi)部圖層的變換。
CALayer同樣也有一個(gè)transform屬性,但它的類型是CATransform3D,而不是CGAffineTransform,本章后續(xù)將會詳細(xì)解釋。CALayer對應(yīng)于UIView的transform屬性叫做affineTransform,清單5.1的例子就是使用affineTransform對圖層做了45度順時(shí)針旋轉(zhuǎn)。
@interface ViewController () @property (nonatomic, weak) IBOutlet UIView *layerView; @end @implementation ViewController - (void)viewDidLoad {[super viewDidLoad];//rotate the layer 45 degreesCGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_4);self.layerView.layer.affineTransform = transform; } @end注意我們使用的旋轉(zhuǎn)常量是M_PI_4,而不是你想象的45,因?yàn)閕OS的變換函數(shù)使用弧度而不是角度作為單位。弧度用數(shù)學(xué)常量pi的倍數(shù)表示,一個(gè)pi代表180度,所以四分之一的pi就是45度。
C的數(shù)學(xué)函數(shù)庫(iOS會自動引入)提供了pi的一些簡便的換算,M_PI_4于是就是pi的四分之一,如果對換算不太清楚的話,可以用如下的宏做換算:
#define?RADIANS_TO_DEGREES(x)?((x)/M_PI*180.0)? #define?DEGREES_TO_RADIANS(x)?((x)/180.0*M_PI)混合變換
當(dāng)操縱一個(gè)變換的時(shí)候,初始生成一個(gè)什么都不做的變換很重要--也就是創(chuàng)建一個(gè)CGAffineTransform類型的空值,矩陣論中稱作單位矩陣,Core Graphics同樣也提供了一個(gè)方便的常量:CGAffineTransformIdentity
最后,如果需要混合兩個(gè)已經(jīng)存在的變換矩陣,就可以使用如下方法,在兩個(gè)變換的基礎(chǔ)上創(chuàng)建一個(gè)新的變換:CGAffineTransformConcat(CGAffineTransform?t1,?CGAffineTransform?t2);
我們來用這些函數(shù)組合一個(gè)更加復(fù)雜的變換,先縮小50%,再旋轉(zhuǎn)30度,最后向右移動200個(gè)像素(清單5.2)。圖5.4顯示了圖層變換最后的結(jié)果。 - (void)viewDidLoad {[super viewDidLoad];//create a new transformCGAffineTransform transform = CGAffineTransformIdentity;
//scale by 50%transform = CGAffineTransformScale(transform, 0.5, 0.5);
//rotate by 30 degreestransform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30.0);
//translate by 200 pointstransform = CGAffineTransformTranslate(transform, 200, 0);//apply transform to layerself.layerView.layer.affineTransform = transform; }
圖5.4中有些需要注意的地方:圖片向右邊發(fā)生了平移,但并沒有指定距離那么遠(yuǎn)(200像素),另外它還有點(diǎn)向下發(fā)生了平移。原因在于當(dāng)你按順序做了變換,上一個(gè)變換的結(jié)果將會影響之后的變換,所以200像素的向右平移同樣也被旋轉(zhuǎn)了30度,縮小了50%,所以它實(shí)際上是斜向移動了100像素。
這意味著變換的順序會影響最終的結(jié)果,也就是說旋轉(zhuǎn)之后的平移和平移之后的旋轉(zhuǎn)結(jié)果可能不同。
3D變換
繞Y軸旋轉(zhuǎn)圖層
@implementation ViewController - (void)viewDidLoad {[super viewDidLoad];//rotate the layer 45 degrees along the Y axisCATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);self.layerView.layer.transform = transform; } @end透視投影
CATransform3D的透視效果通過一個(gè)矩陣中一個(gè)很簡單的元素來控制:m34。m34(圖5.9)用于按比例縮放X和Y的值來計(jì)算到底要離視角多遠(yuǎn)。
m34的默認(rèn)值是0,我們可以通過設(shè)置m34為-1.0 / d來應(yīng)用透視效果,d代表了想象中視角相機(jī)和屏幕之間的距離,以像素為單位,那應(yīng)該如何計(jì)算這個(gè)距離呢?實(shí)際上并不需要,大概估算一個(gè)就好了。
因?yàn)橐暯窍鄼C(jī)實(shí)際上并不存在,所以可以根據(jù)屏幕上的顯示效果自由決定它的防止的位置。通常500-1000就已經(jīng)很好了,但對于特定的圖層有時(shí)候更小后者更大的值會看起來更舒服,減少距離的值會增強(qiáng)透視效果,所以一個(gè)非常微小的值會讓它看起來更加失真,然而一個(gè)非常大的值會讓它基本失去透視效果,對視圖應(yīng)用透視的代碼見清單5.5,結(jié)果見圖5.10。
@implementation ViewController - (void)viewDidLoad {[super viewDidLoad];//create a new transformCATransform3D transform = CATransform3DIdentity;//apply perspectivetransform.m34 = - 1.0 / 500.0;//rotate by 45 degrees along the Y axistransform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);//apply to layerself.layerView.layer.transform = transform; } @end
消亡點(diǎn)
Core Animation定義了這個(gè)點(diǎn)位于變換圖層的anchorPoint(通常位于圖層中心,但也有例外,見第三章)。這就是說,當(dāng)圖層發(fā)生變換時(shí),這個(gè)點(diǎn)永遠(yuǎn)位于圖層變換之前anchorPoint的位置。
當(dāng)改變一個(gè)圖層的position,你也改變了它的消亡點(diǎn),做3D變換的時(shí)候要時(shí)刻記住這一點(diǎn),當(dāng)你視圖通過調(diào)整m34來讓它更加有3D效果,應(yīng)該首先把它放置于屏幕中央,然后通過平移來把它移動到指定位置(而不是直接改變它的position),這樣所有的3D圖層都共享一個(gè)消亡點(diǎn)。
sublayerTransform屬性
如果有多個(gè)視圖或者圖層,每個(gè)都做3D變換,那就需要分別設(shè)置相同的m34值,并且確保在變換之前都在屏幕中央共享同一個(gè)position,如果用一個(gè)函數(shù)封裝這些操作的確會更加方便,但仍然有限制(例如,你不能在Interface Builder中擺放視圖),這里有一個(gè)更好的方法。
CALayer有一個(gè)屬性叫做sublayerTransform。它也是CATransform3D類型,但和對一個(gè)圖層的變換不同,它影響到所有的子圖層。這意味著你可以一次性對包含這些圖層的容器做變換,于是所有的子圖層都自動繼承了這個(gè)變換方法。
相較而言,通過在一個(gè)地方設(shè)置透視變換會很方便,同時(shí)它會帶來另一個(gè)顯著的優(yōu)勢:消亡點(diǎn)被設(shè)置在容器圖層的中點(diǎn),從而不需要再對子圖層分別設(shè)置了。這意味著你可以隨意使用position和frame來放置子圖層,而不需要把它們放置在屏幕中點(diǎn),然后為了保證統(tǒng)一的消亡點(diǎn)用變換來做平移。
我們來用一個(gè)demo舉例說明。這里用Interface Builder并排放置兩個(gè)視圖(圖5.12),然后通過設(shè)置它們?nèi)萜饕晥D的透視變換,我們可以保證它們有相同的透視和消亡點(diǎn),代碼見清單5.6,結(jié)果見圖5.13。
@interface ViewController () @property (nonatomic, weak) IBOutlet UIView *containerView; @property (nonatomic, weak) IBOutlet UIView *layerView1; @property (nonatomic, weak) IBOutlet UIView *layerView2; @end @implementation ViewController - (void)viewDidLoad {[super viewDidLoad];//apply perspective transform to containerCATransform3D perspective = CATransform3DIdentity;perspective.m34 = - 1.0 / 500.0;self.containerView.layer.sublayerTransform = perspective;//rotate layerView1 by 45 degrees along the Y axisCATransform3D transform1 = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);self.layerView1.layer.transform = transform1;//rotate layerView2 by 45 degrees along the Y axisCATransform3D transform2 = CATransform3DMakeRotation(-M_PI_4, 0, 1, 0);self.layerView2.layer.transform = transform2; }背面(相當(dāng)于水平翻轉(zhuǎn),但其實(shí)是。。。)
但這并不是一個(gè)很好的特性,因?yàn)槿绻麍D層包含文本或者其他控件,那用戶看到這些內(nèi)容的鏡像圖片當(dāng)然會感到困惑。另外也有可能造成資源的浪費(fèi):想象用這些圖層形成一個(gè)不透明的固態(tài)立方體,既然永遠(yuǎn)都看不見這些圖層的背面,那為什么浪費(fèi)GPU來繪制它們呢?
CALayer有一個(gè)叫做doubleSided的屬性來控制圖層的背面是否要被繪制。這是一個(gè)BOOL類型,默認(rèn)為YES,如果設(shè)置為NO,那么當(dāng)圖層正面從相機(jī)視角消失的時(shí)候,它將不會被繪制。
固體對象
我們把一個(gè)有顏色的UILabel放置在視圖內(nèi)部,是為了清楚的辨別它們之間的關(guān)系,并且UIButton被放置在第三個(gè)面視圖里面,后面會做簡單的解釋。
@interface ViewController () @property (nonatomic, weak) IBOutlet UIView *containerView; @property (nonatomic, strong) IBOutletCollection(UIView) NSArray *faces; @end @implementation ViewController - (void)addFace:(NSInteger)index withTransform:(CATransform3D)transform {//get the face view and add it to the containerUIView *face = self.faces[index];[self.containerView addSubview:face];//center the face view within the containerCGSize containerSize = self.containerView.bounds.size;face.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);// apply the transformface.layer.transform = transform; } - (void)viewDidLoad {[super viewDidLoad];//set up the container sublayer transformCATransform3D perspective = CATransform3DIdentity;perspective.m34 = -1.0 / 500.0;self.containerView.layer.sublayerTransform = perspective;//add cube face 1CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);[self addFace:0 withTransform:transform];//add cube face 2transform = CATransform3DMakeTranslation(100, 0, 0);transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);[self addFace:1 withTransform:transform];//add cube face 3transform = CATransform3DMakeTranslation(0, -100, 0);transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);[self addFace:2 withTransform:transform];//add cube face 4transform = CATransform3DMakeTranslation(0, 100, 0);transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0);[self addFace:3 withTransform:transform];//add cube face 5transform = CATransform3DMakeTranslation(-100, 0, 0);transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0);[self addFace:4 withTransform:transform];//add cube face 6transform = CATransform3DMakeTranslation(0, 0, -100);transform = CATransform3DRotate(transform, M_PI, 0, 1, 0);[self addFace:5 withTransform:transform]; } @end從這個(gè)角度看立方體并不是很明顯;看起來只是一個(gè)方塊,為了更好地欣賞它,我們將更換一個(gè)不同的視角。
旋轉(zhuǎn)這個(gè)立方體將會顯得很笨重,因?yàn)槲覀円獑为?dú)對每個(gè)面做旋轉(zhuǎn)。另一個(gè)簡單的方案是通過調(diào)整容器視圖的sublayerTransform去旋轉(zhuǎn)照相機(jī)。
添加如下幾行去旋轉(zhuǎn)containerView圖層的perspective變換矩陣:
perspective?=?CATransform3DRotate(perspective,?-M_PI_4,?1,?0,?0);? perspective?=?CATransform3DRotate(perspective,?-M_PI_4,?0,?1,?0); 這就對相機(jī)(或者相對相機(jī)的整個(gè)場景,你也可以這么認(rèn)為)繞Y軸旋轉(zhuǎn)45度,并且繞X軸旋轉(zhuǎn)45度。現(xiàn)在從另一個(gè)角度去觀察立方體,就能看出它的真實(shí)面貌(圖5.21)。光亮和陰影
如果需要?jiǎng)討B(tài)地創(chuàng)建光線效果,你可以根據(jù)每個(gè)視圖的方向應(yīng)用不同的alpha值做出半透明的陰影圖層,但為了計(jì)算陰影圖層的不透明度,你需要得到每個(gè)面的正太向量(垂直于表面的向量),然后根據(jù)一個(gè)想象的光源計(jì)算出兩個(gè)向量叉乘結(jié)果。叉乘代表了光源和圖層之間的角度,從而決定了它有多大程度上的光亮。
清單5.10實(shí)現(xiàn)了這樣一個(gè)結(jié)果,我們用GLKit框架來做向量的計(jì)算(你需要引入GLKit庫來運(yùn)行代碼),每個(gè)面的CATransform3D都被轉(zhuǎn)換成GLKMatrix4,然后通過GLKMatrix4GetMatrix3函數(shù)得出一個(gè)3×3的旋轉(zhuǎn)矩陣。這個(gè)旋轉(zhuǎn)矩陣指定了圖層的方向,然后可以用它來得到正太向量的值。
結(jié)果如圖5.22所示,試著調(diào)整LIGHT_DIRECTION和AMBIENT_LIGHT的值來切換光線效果
#import "ViewController.h" #import #import #define LIGHT_DIRECTION 0, 1, -0.5 #define AMBIENT_LIGHT 0.5 @interface ViewController () @property (nonatomic, weak) IBOutlet UIView *containerView; @property (nonatomic, strong) IBOutletCollection(UIView) NSArray *faces; @end @implementation ViewController - (void)applyLightingToFace:(CALayer *)face {//add lighting layerCALayer *layer = [CALayer layer];layer.frame = face.bounds;[face addSublayer:layer];//convert the face transform to matrix//(GLKMatrix4 has the same structure as CATransform3D)CATransform3D transform = face.transform;GLKMatrix4 matrix4 = *(GLKMatrix4 *)&transform;GLKMatrix3 matrix3 = GLKMatrix4GetMatrix3(matrix4);//get face normalGLKVector3 normal = GLKVector3Make(0, 0, 1);normal = GLKMatrix3MultiplyVector3(matrix3, normal);normal = GLKVector3Normalize(normal);//get dot product with light directionGLKVector3 light = GLKVector3Normalize(GLKVector3Make(LIGHT_DIRECTION));float dotProduct = GLKVector3DotProduct(light, normal);//set lighting layer opacityCGFloat shadow = 1 + dotProduct - AMBIENT_LIGHT;UIColor *color = [UIColor colorWithWhite:0 alpha:shadow];layer.backgroundColor = color.CGColor; } - (void)addFace:(NSInteger)index withTransform:(CATransform3D)transform {//get the face view and add it to the containerUIView *face = self.faces[index];[self.containerView addSubview:face];//center the face view within the containerCGSize containerSize = self.containerView.bounds.size;face.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);// apply the transformface.layer.transform = transform;//apply lighting [self applyLightingToFace:face.layer]; } - (void)viewDidLoad {[super viewDidLoad];//set up the container sublayer transformCATransform3D perspective = CATransform3DIdentity;perspective.m34 = -1.0 / 500.0;perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0);perspective = CATransform3DRotate(perspective, -M_PI_4, 0, 1, 0);self.containerView.layer.sublayerTransform = perspective;//add cube face 1CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);[self addFace:0 withTransform:transform];//add cube face 2transform = CATransform3DMakeTranslation(100, 0, 0);transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);[self addFace:1 withTransform:transform];//add cube face 3transform = CATransform3DMakeTranslation(0, -100, 0);transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);[self addFace:2 withTransform:transform];//add cube face 4transform = CATransform3DMakeTranslation(0, 100, 0);transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0);[self addFace:3 withTransform:transform];//add cube face 5transform = CATransform3DMakeTranslation(-100, 0, 0);transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0);[self addFace:4 withTransform:transform];//add cube face 6transform = CATransform3DMakeTranslation(0, 0, -100);transform = CATransform3DRotate(transform, M_PI, 0, 1, 0);[self addFace:5 withTransform:transform]; } @end點(diǎn)擊事件
你應(yīng)該能注意到現(xiàn)在可以在第三個(gè)表面的頂部看見按鈕了,點(diǎn)擊它,什么都沒發(fā)生,為什么呢?
這并不是因?yàn)閕OS在3D場景下正確地處理響應(yīng)事件,實(shí)際上是可以做到的。問題在于視圖順序。在第三章中我們簡要提到過,點(diǎn)擊事件的處理由視圖在父視圖中的順序決定的,并不是3D空間中的Z軸順序。當(dāng)給立方體添加視圖的時(shí)候,我們實(shí)際上是按照一個(gè)順序添加,所以按照視圖/圖層順序來說,4,5,6在3的前面。
即使我們看不見4,5,6的表面(因?yàn)楸?,2,3遮住了),iOS在事件響應(yīng)上仍然保持之前的順序。當(dāng)試圖點(diǎn)擊表面3上的按鈕,表面4,5,6截?cái)嗔它c(diǎn)擊事件(取決于點(diǎn)擊的位置),這就和普通的2D布局在按鈕上覆蓋物體一樣。
你也許認(rèn)為把doubleSided設(shè)置成NO可以解決這個(gè)問題,因?yàn)樗辉黉秩疽晥D后面的內(nèi)容,但實(shí)際上并不起作用。因?yàn)楸硨ο鄼C(jī)而隱藏的視圖仍然會響應(yīng)點(diǎn)擊事件(這和通過設(shè)置hidden屬性或者設(shè)置alpha為0而隱藏的視圖不同,那兩種方式將不會響應(yīng)事件)。所以即使禁止了雙面渲染仍然不能解決這個(gè)問題(雖然由于性能問題,還是需要把它設(shè)置成NO)。
這里有幾種正確的方案:把除了表面3的其他視圖userInteractionEnabled屬性都設(shè)置成NO來禁止事件傳遞。或者簡單通過代碼把視圖3覆蓋在視圖6上。無論怎樣都可以點(diǎn)擊按鈕了(圖5.23)。
轉(zhuǎn)載于:https://www.cnblogs.com/lihaiyin/p/4450663.html
總結(jié)
以上是生活随笔為你收集整理的iOS——Core Animation 知识摘抄(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Javascript let和const
- 下一篇: Android 基础 —— 活动的启动模