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后面有机会再研究。