UIView在ios开发里面是非常重要的。几乎所有的控件都是从UIView继承下来的。比如UILabel,UIText等。
今天有空看了一下UIView的内部结构。发现其实UIView的显示部分其实委托给CALayer(Core Animation Layer)来做的。
UIView类片段
NS_CLASS_AVAILABLE_IOS(2_0) @interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace> {
@package
CALayer *_layer;
id _gestureInfo;
NSMutableArray *_gestureRecognizers;
NSArray *_subviewCache;
float _charge;
NSInteger _tag;
UIViewController *_viewDelegate;
NSString *_backgroundColorSystemColorName;
NSUInteger _countOfMotionEffectsInSubtree;
里面有个CALayer的成员。
CALayer的主要功能就是在屏幕上显示东西了。
CALayer属性
我们可以通过CALayer的属性来改变图层的一些外观。
- (IBAction)btnClick:(id)sender {
CALayer *sublayer =[CALayer layer];
sublayer.backgroundColor =[UIColor blueColor].CGColor;
sublayer.shadowOffset = CGSizeMake(0, 3);
sublayer.shadowRadius =5.0;
sublayer.shadowColor =[UIColor blackColor].CGColor;
sublayer.shadowOpacity = 1;
sublayer.frame = CGRectMake(150, 20, 128, 50);
sublayer.borderColor =[UIColor blackColor].CGColor;
sublayer.borderWidth =2.0;
sublayer.cornerRadius =10.0;
sublayer.anchorPoint = CGPointMake(0, 0);
[self.view.layer addSublayer:sublayer];
CABasicAnimation *animation = [ CABasicAnimation animationWithKeyPath: @"transform" ];
animation.toValue = [ NSValue valueWithCATransform3D: CATransform3DMakeRotation(3.1415, 0, 0, 1.0) ];
animation.duration = 2;
animation.cumulative = YES;
animation.repeatCount = 2;
[sublayer addAnimation: animation forKey: @"animation" ];
}
上面的代码主要分几部分:
1. 创建一个CALayer的实例
2. 修改一些属性,比如阴影,圆角等。
3. 把新创建的CALayer实例加到当前view的层里面,作为子层。
4. 给新创建的CALayer实例增加一个动画效果。
点一下第一个按钮,就会发现有个蓝色的图层(CALayer),会旋转一圈。这些都是刚才的那些代码做的事情,非常简单。
CALayer显示图片
在ios上显示一张图片其实很简单,搞个ImageView控件就可以了。
实际上ImageView也是从UIView继承下来的,其实也是通过CALayer显示的。那么如果使用CALayer直接显示一张图片呢?
通过下面的代码就可以搞定了:
- (IBAction)btnContent:(id)sender {
CALayer *sublayer =[CALayer layer];
sublayer.backgroundColor =[UIColor orangeColor].CGColor;
sublayer.shadowOffset = CGSizeMake(0, 3);
sublayer.shadowRadius =5.0;
sublayer.shadowColor =[UIColor blackColor].CGColor;
sublayer.shadowOpacity = 1;
sublayer.borderColor =[UIColor blackColor].CGColor;
sublayer.borderWidth =2.0;
sublayer.cornerRadius =10.0;
sublayer.anchorPoint = CGPointMake(0, 0);
[self.view.layer addSublayer:sublayer];
CGImageRef img = [UIImage imageNamed:@"Image"].CGImage;
sublayer.contents = (__bridge id)img;
sublayer.frame = CGRectMake(180, 60, CGImageGetWidth(img), CGImageGetWidth(img));
CABasicAnimation *animation = [ CABasicAnimation animationWithKeyPath: @"transform" ];
animation.toValue = [ NSValue valueWithCATransform3D: CATransform3DMakeRotation(3.1415, 0, 0, 1.0) ];
animation.duration = 2;
animation.cumulative = YES;
animation.repeatCount = 2;
[sublayer addAnimation: animation forKey: @"animation" ];
}
代码也分几个部分
1. 创建一个新的CALayer对象
2. 设置一些属性
3. 把这个新创建的对象加到当前的view的CALayer里面。
4. 获取一张图片对象,赋给CALayer的contents
5. 跟前面一样,加个动画。
运行一下:
从截图里可以看到图片显示出来了(qq头像),并且有个旋转的动画效果。
CALayer是可以叠加的,如果有多个图片,那么就可以创建多个CALayer对象,一个个叠上去,同时还可以设置一些动画效果啥的。
比如,一个view消失的时候,我们可以放一些玻璃碎片图片上去,给每个CALayer对象加个掉下来的动画,这样看起来就像是玻璃破碎一样。
CALayer自定义绘画
除了简单修改CALayer对象属性和显示一张图片外,我们还可以自己绘制图层的内容。
先给出代码:
- (IBAction)btnCustomDraw:(id)sender {
CALayer *customDrawn = [CALayer layer];
customDrawn.delegate = self;
customDrawn.backgroundColor = [UIColor greenColor].CGColor;
customDrawn.frame = CGRectMake(180, 100, 128, 40);
customDrawn.shadowOffset = CGSizeMake(0, 3);
customDrawn.shadowRadius = 5.0;
customDrawn.shadowColor = [UIColor blackColor].CGColor;
customDrawn.shadowOpacity = 0.8;
customDrawn.cornerRadius = 10.0;
customDrawn.borderColor = [UIColor blackColor].CGColor;
customDrawn.borderWidth = 2.0;
customDrawn.masksToBounds = YES;
[self.view.layer addSublayer:customDrawn];
[customDrawn setNeedsDisplay];
}
void MyDrawColoredPattern (void *info, CGContextRef context) {
CGColorRef dotColor = [UIColor colorWithHue:0 saturation:0 brightness:0.07 alpha:1.0].CGColor;
CGColorRef shadowColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.1].CGColor;
CGContextSetFillColorWithColor(context, dotColor);
CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 1, shadowColor);
CGContextAddArc(context, 3, 3, 4, 0, 3.14, 0);
CGContextFillPath(context);
CGContextAddArc(context, 16, 16, 4, 0, 3.14, 0);
CGContextFillPath(context);
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context {
CGColorRef bgColor = [UIColor colorWithHue:0.6 saturation:1.0 brightness:1.0 alpha:1.0].CGColor;
CGContextSetFillColorWithColor(context, bgColor);
CGContextFillRect(context, layer.bounds);
static const CGPatternCallbacks callbacks = { 0, &MyDrawColoredPattern, NULL };
CGContextSaveGState(context);
CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
CGContextSetFillColorSpace(context, patternSpace);
CGColorSpaceRelease(patternSpace);
CGPatternRef pattern = CGPatternCreate(NULL,
layer.bounds,
CGAffineTransformIdentity,
24,
24,
kCGPatternTilingConstantSpacing,
true,
&callbacks);
CGFloat alpha = 1.0;
CGContextSetFillPattern(context, pattern, &alpha);
CGPatternRelease(pattern);
CGContextFillRect(context, layer.bounds);
CGContextRestoreGState(context);
}
创建CALayer对象那部分代码和前面的很相似, 唯一的区别就是设置了一个委托
customDrawn.delegate = self;
我们需要实现一个drawLayer的函数。这个函数是个非正式协议,也就是NSObject的类别,所以所有的OC对象都有这个函数的声明。看下面的代码,可以清楚的看到这函数。
@interface NSObject (CALayerDelegate)
/* If defined, called by the default implementation of the -display
* method, in which case it should implement the entire display
* process (typically by setting the `contents' property). */
- (void)displayLayer:(CALayer *)layer;
/* If defined, called by the default implementation of -drawInContext: */
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
/* Called by the default -layoutSublayers implementation before the layout
* manager is checked. Note that if the delegate method is invoked, the
* layout manager will be ignored. */
- (void)layoutSublayersOfLayer:(CALayer *)layer;
/* If defined, called by the default implementation of the
* -actionForKey: method. Should return an object implementating the
* CAAction protocol. May return 'nil' if the delegate doesn't specify
* a behavior for the current event. Returning the null object (i.e.
* '[NSNull null]') explicitly forces no further search. (I.e. the
* +defaultActionForKey: method will not be called.) */
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event;
@end
我们实现了drawLayer这个函数后,那么就可以在里面自己来画东西了。通常都是使用core graphics来画的。
运行结果:
基本上有关CALayer的绘制就这么几种吧
1. 最简单的设置属性
2. 显示图片
3. 自定义绘制。
有了这些基础后,配合动画就可以做一些比较炫的ui界面了。
动画和core graphics后面有机会再研究。