//affineTransform
[self affineTransformTest];
//affineTransforms
[self affineTransformsTest];
//剪切變換
[self affineTransformShearTest];
//transform3DTest
[self transform3DTest];
//透視投影
[self transform3DM34Test];
//sublayerTransform
[self sublayerTransformTest];
//背面
[self doubleSidedTest];
//扁平化圖層
[self outerInnerViewTest];
//cubeTest
[self cubeTest];
仿射變換
//實際上UIView的transform屬性是一個CGAffineTransform類型,用于在二維空間做旋轉,縮放和平移。CGAffineTransform是一個可以和二維空間向量(例如CGPoint)做乘法的3X2的矩陣,圖層中平行的兩條線在變換之后任然保持平行
//創(chuàng)建一個CGAffineTransform
//CGAffineTransformMakeRotation(CGFloat angle)
//CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
//CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
//UIView可以通過設置transform屬性做變換,但實際上它只是封裝了內部圖層的變換。
//CALayer同樣也有一個transform屬性,但它的類型是CATransform3D,而不是CGAffineTransform
//CALayer對應于UIView的transform屬性叫做affineTransform
- (void)affineTransformTest {
layerView = [[UIView alloc] init];
layerView.frame = CGRectMake(20.0, 20.0f, 100.0f, 150.0f);
UIImage *image = [UIImage imageNamed:@"Meal"];
layerView.layer.contents = (__bridge id)image.CGImage;
[self.view addSubview:layerView];
//rotate the layer 45 degrees
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_4);
layerView.layer.affineTransform = transform;//layerView.transform = transform;
}
混合變換
//CGAffineTransformIdentity 創(chuàng)建一個CGAffineTransform類型的空值
//CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);
- (void)affineTransformsTest {
layerView = [[UIView alloc] init];
layerView.frame = CGRectMake(20.0, 20.0f, 100.0f, 150.0f);
UIImage *image = [UIImage imageNamed:@"Meal"];
layerView.layer.contents = (__bridge id)image.CGImage;
[self.view addSubview:layerView];
//create a new transform
CGAffineTransform transform = CGAffineTransformIdentity;
//scale by 50%
transform = CGAffineTransformScale(transform, 0.5, 0.5);
//rotate by 30 degrees
transform = CGAffineTransformRotate(transform, M_PI/180.0f * 30.0f);
// translate by 200 points
transform = CGAffineTransformTranslate(transform, 200.0f, 0);
//apply transform to layer
layerView.layer.affineTransform = transform;
//所以200像素的向右平移同樣也被旋轉了30度,縮小了50%,所以它實際上是斜向移動了100像素。
}
剪切變換
CGAffineTransform CGAffineTransformMakeShear(CGFloat x, CGFloat y) {
CGAffineTransform transform = CGAffineTransformIdentity;
transform.c = -x;
transform.b = y;
return transform;
}
- (void)affineTransformShearTest {
layerView = [[UIView alloc] init];
layerView.frame = CGRectMake(20.0, 20.0f, 100.0f, 150.0f);
UIImage *image = [UIImage imageNamed:@"Meal"];
layerView.layer.contents = (__bridge id)image.CGImage;
[self.view addSubview:layerView];
//shear the layer at a 45_degree angle
layerView.layer.affineTransform = CGAffineTransformMakeShear(1, 0);
}
//CG: Core Graphics CA:Core Animation
3D 變換
//transform:(CATransform3D類型) zPosition
//CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
//CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz)
//CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)
- (void)transform3DTest {
layerView = [[UIView alloc] init];
layerView.frame = CGRectMake(20.0, 20.0f, 100.0f, 150.0f);
UIImage *image = [UIImage imageNamed:@"Meal"];
layerView.layer.contents = (__bridge id)image.CGImage;
[self.view addSubview:layerView];
//rotate the layer 45 degree along the Y axis
CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
layerView.layer.transform = transform;
}
//透視投影 在3D變換中任然保持平行 在等距投影中,遠處的物體和近處的物體保持同樣的縮放比例,這種投影也有它自己的用處(例如建筑繪圖,顛倒,和偽3D視頻)
//投影變換(又稱作z變換) 控制:m34。m34用于按比例縮放X和Y的值來計算到底要離視角多遠。m34的默認值是0,我們可以通過設置m34為-1.0 / d來應用透視效果,d代表了想象中視角相機和屏幕之間的距離,以像素為單位, 通常500-1000就已經很好
- (void)transform3DM34Test {
layerView = [[UIView alloc] init];
layerView.frame = CGRectMake(20.0, 20.0f, 100.0f, 150.0f);
UIImage *image = [UIImage imageNamed:@"Meal"];
layerView.layer.contents = (__bridge id)image.CGImage;
[self.view addSubview:layerView];
//create a new transform
CATransform3D transform = CATransform3DIdentity;
//apply perspective
transform.m34 = - 1.0/ 500.0f;
//rotate by 45 degrees along the Y axis
transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);
//apply to layer
layerView.layer.transform = transform;
}
滅點
//Core Animation定義了這個點位于變換圖層的anchorPoint(通常位于圖層中心,但也有例外,見第三章)。這就是說,當圖層發(fā)生變換時,這個點永遠位于圖層變換之前anchorPoint的位置。
//當改變一個圖層的position,你也改變了它的滅點,做3D變換的時候要時刻記住這一點,當你視圖通過調整m34來讓它更加有3D效果,應該首先把它放置于屏幕中央,然后通過平移來把它移動到指定位置(而不是直接改變它的position),這樣所有的3D圖層都共享一個滅點。
sublayerTransform
//如果有多個視圖或者圖層,每個都做3D變換,那就需要分別設置相同的m34值,并且確保在變換之前都在屏幕中央共享同一個position
//CALayer有一個屬性叫做sublayerTransform。它也是CATransform3D類型,但和對一個圖層的變換不同,它影響到所有的子圖層。這意味著你可以一次性對包含這些圖層的容器做變換,于是所有的子圖層都自動繼承了這個變換方法。
//相較而言,通過在一個地方設置透視變換會很方便,同時它會帶來另一個顯著的優(yōu)勢:滅點被設置在容器圖層的中點,從而不需要再對子圖層分別設置了。這意味著你可以隨意使用position和frame來放置子圖層,而不需要把它們放置在屏幕中點,然后為了保證統(tǒng)一的滅點用變換來做平移
- (void)sublayerTransformTest {
UIView *containerView = [[UIView alloc] init];
containerView.frame = CGRectMake(20.f, 20.0f, 300.0f, 200.0f);
containerView.backgroundColor = [UIColor blackColor];
[self.view addSubview:containerView];
UIImageView *imageView1 = [[UIImageView alloc] init];
imageView1.frame = CGRectMake(0.0, 50.0f, 100.0f, 100.0);
imageView1.image = [UIImage imageNamed:@"Meal"];
[containerView addSubview:imageView1];
UIImageView *imageView2 = [[UIImageView alloc] init];
imageView2.frame = CGRectMake(200.0, 50.0f, 100.0f, 100.0);
imageView2.image = [UIImage imageNamed:@"Meal"];
[containerView addSubview:imageView2];
//apply perspective transform to container
CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = - 1.0/500.0;
containerView.layer.sublayerTransform = perspective;
//rotate imageView1 by 45 degrees along thr Y axis
CATransform3D transform1 = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
imageView1.layer.transform = transform1;
//rotate imageView2 by 45 degrees along thr Y axis
CATransform3D transform2 = CATransform3DMakeRotation(-M_PI_4, 0, 1, 0);
imageView2.layer.transform = transform2;
}
背面
//CALayer有一個叫做doubleSided的屬性來控制圖層的背面是否要被繪制。這是一個BOOL類型,默認為YES,如果設置為NO,那么當圖層正面從相機視角消失的時候,它將不會被繪制。
- (void)doubleSidedTest {
UIImageView *imageView1 = [[UIImageView alloc] init];
imageView1.frame = CGRectMake(5.0, 50.0f, 100.0f, 100.0);
imageView1.image = [UIImage imageNamed:@"Meal"];
[self.view addSubview:imageView1];
//rotate imageView1 by 180 degrees along thr Y axis
CATransform3D transform1 = CATransform3DMakeRotation(M_PI, 0, 1, 0);
imageView1.layer.transform = transform1;
imageView1.layer.doubleSided = YES;//鏡像圖片 默認為YES
}
扁平化圖層
- (void)outerInnerViewTest {
UIView *outerView = [[UIView alloc] init];
outerView.frame = CGRectMake(20.f, 20.0f, 300.0f, 300.0f);
outerView.backgroundColor = [UIColor blackColor];
[self.view addSubview:outerView];
UIImageView *innerView = [[UIImageView alloc] init];
innerView.frame = CGRectMake(100.0, 100.0f, 100.0f, 100.0);
innerView.image = [UIImage imageNamed:@"Meal"];
[outerView addSubview:innerView];
//rotate the outer layer 45 degrees
//CATransform3D outer = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);
CATransform3D outer = CATransform3DIdentity;
outer.m34 = -1.0/500.0f;
outer = CATransform3DRotate(outer, M_PI_4, 0, 1, 0);
outerView.layer.transform = outer;
//rotate the inner layer -45 degrees
// CATransform3D inner = CATransform3DMakeRotation(-M_PI_4, 0, 0, 1);
CATransform3D inner = CATransform3DIdentity;
inner.m34 = -1.0/500.0f;
inner = CATransform3DRotate(inner, -M_PI_4, 0, 1, 0);
innerView.layer.transform = inner;
}
固體對象
- (void)addFace:(NSInteger )index withTransform:(CATransform3D)transform {
//get the face view and add it to the container
UIView *face = faces[index];
[containerView addSubview:face];
//center the face view within the container
CGSize containerSize = containerView.bounds.size;
face.center = CGPointMake(containerSize.width/2.f, containerSize.height/2.0f);
//apply the transform
face.layer.transform = transform;
// apply lighting
[self applyLightingToFace:face.layer];
}
- (void)cubeTest {
containerView = [[UIView alloc] init];
containerView.frame = CGRectMake(20.0f, 20.0f, 300.0f, 300.0f);
containerView.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:containerView];
faces = [NSMutableArray array];
for (int k = 0; k < 6; k++) {
UIView *face = [[UIView alloc] init];
face.frame = CGRectMake(0, 0, 200.0f, 200.0f);
face.backgroundColor = [UIColor whiteColor];
UILabel *lab = [[UILabel alloc] init];
lab.frame = CGRectMake(50.0, 50.0, 100.0f, 100.0f);
lab.font = [UIFont systemFontOfSize:30];
lab.text = [NSString stringWithFormat:@"%d", k + 1];
lab.textAlignment = NSTextAlignmentCenter;
if (k == 2) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(50.0, 50.0, 100.0f, 100.0f);
button.layer.cornerRadius = 5.0f;
button.layer.borderWidth = 3.f;
button.backgroundColor = [UIColor redColor];
[button addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
[face addSubview:button];
}
else {
face.userInteractionEnabled = NO;
}
if ( k % 3 == 0) {
lab.textColor = [UIColor redColor];
}
else if ( k % 3 == 1) {
lab.textColor = [UIColor greenColor];
}
else if (k % 3 == 2) {
lab.textColor = [UIColor blueColor];
}
[face addSubview:lab];
[faces addObject:face];
}
//set up the container sublayer transform
CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = -1.0/500.f;
perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0);
perspective = CATransform3DRotate(perspective, -M_PI_4, 0, 1, 0);
containerView.layer.sublayerTransform = perspective;
//add cube face 1
CATransform3D trandform = CATransform3DMakeTranslation(0, 0, 100);
[self addFace:0 withTransform:trandform];
//add cude face2
trandform = CATransform3DMakeTranslation(100, 0, 0);
trandform = CATransform3DRotate(trandform, M_PI_2, 0, 1, 0);
[self addFace:1 withTransform:trandform];
//add cude face3
trandform = CATransform3DMakeTranslation(0, -100, 0);
trandform = CATransform3DRotate(trandform, M_PI_2, 1, 0, 0);
[self addFace:2 withTransform:trandform];
//add cude face4
trandform = CATransform3DMakeTranslation(0, 100, 0);
trandform = CATransform3DRotate(trandform, -M_PI_2, 1, 0, 0);
[self addFace:3 withTransform:trandform];
//add cude face5
trandform = CATransform3DMakeTranslation(-100, 0, 0);
trandform = CATransform3DRotate(trandform, -M_PI_2, 0, 1, 0);
[self addFace:4 withTransform:trandform];
//add cude face6
trandform = CATransform3DMakeTranslation(0, 0, -100);
trandform = CATransform3DRotate(trandform, M_PI, 0, 1, 0);
[self addFace:5 withTransform:trandform];
}
光亮和陰影
- (void)applyLightingToFace:(CALayer *)face {
//add lighting layer
CALayer *layer = [CALayer layer];
layer.frame = face.bounds;
[face addSublayer:layer];
//conver the face transform to matrix
//(GLKMatrix4 has the same structure as CATransform3D)
//譯者注:GLKMatrix4和CATransform3D內存結構一致,但坐標類型有長度區(qū)別,所以理論上應該做一次float到CGFloat的轉換,感謝[@zihuyishi](https://github.com/zihuyishi)同學~
CATransform3D transform = face.transform;
GLKMatrix4 matrix4 = *(GLKMatrix4 *)&transform;
GLKMatrix3 matrix3 = GLKMatrix4GetMatrix3(matrix4);
//get face normal
GLKVector3 normal = GLKVector3Make(0, 0, 1);
normal = GLKMatrix3MultiplyVector3(matrix3, normal);
normal = GLKVector3Normalize(normal);
//get dot product wiht light direction
GLKVector3 light = GLKVector3Normalize(GLKVector3Make(LIGHT_DIRECTION));
float dotProduct = GLKVector3DotProduct(light, normal);
//set lighting layer opacity
CGFloat shadow = 1 + dotProduct - AMBIENT_LIGHT;
UIColor *color = [UIColor colorWithWhite:0 alpha:shadow];
layer.backgroundColor = color.CGColor;
}
點擊事件
- (void)buttonAction {
//按照視圖/圖層順序來說,4,5,6在3的前面
//把除了表面3的其他視圖userInteractionEnabled屬性都設置成NO來禁止事件傳遞。或者簡單通過代碼把視圖3覆蓋在視圖6上。無論怎樣都可以點擊按鈕了
NSLog(@"Three face is tapped!");
}