[iOS Animation]-CALayer 视觉效果

视觉效果

嗯,圆和椭圆还不错,但若是是带圆角的矩形呢? git

咱们如今能作到那样了么? github

史蒂芬·乔布斯 框架

咱们在第三章『图层几何学』中讨论了图层的frame,第二章『寄宿图』则讨论了图层的寄宿图。可是图层不只仅能够是图片或是颜色的容器;还有一系列内建的特性使得创造美丽优雅的使人深入的界面元素成为可能。在这一章,咱们将会探索一些可以经过使用CALayer属性实现的视觉效果。 动画

圆角

圆角矩形是iOS的一个标志性审美特性。这在iOS的每个地方都获得了体现,不管是主屏幕图标,仍是警告弹框,甚至是文本框。按照这流行程度,你可能会认为必定有不借助Photoshop就能轻易建立圆角举行的方法。恭喜你,猜对了。 ui

CALayer有一个叫作conrnerRadius的属性控制着图层角的曲率。它是一个浮点数,默认为0(为0的时候就是直角),可是你能够把它设置成任意值。默认状况下,这个曲率值只影响背景颜色而不影响背景图片或是子图层。不过,若是把masksToBounds设置成YES的话,图层里面的全部东西都会被截取。 atom

咱们能够经过一个简单的项目来演示这个效果。在Interface Builder中,咱们放置一些视图,他们有一些子视图。并且这些子视图有一些超出了边界(如图4.1)。你可能没法看到他们超出了边界,由于在编辑界面的时候,超出的部分老是被Interface Builder裁切掉了。不过,你相信我就行了 :) spa

图4.1

图4.1 两个白色的大视图,他们都包含了小一些的红色视图。 .net

而后在代码中,咱们设置角的半径为20个点,并裁剪掉第一个视图的超出部分(见清单4.1)。技术上来讲,这些属性均可以在Interface Builder的探测板中分别经过『用户定义运行时属性』和勾选『裁剪子视图』(Clip Subviews)选择框来直接设置属性的值。不过,在这个示例中,代码可以表示得更清楚。图4.2是运行代码的结果 设计

清单4.1 设置cornerRadius和masksToBounds 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@interface ViewController ()
 
@property (nonatomic, weak) IBOutlet UIView *layerView1;
@property (nonatomic, weak) IBOutlet UIView *layerView2;
 
 
@implementation ViewController
- (void)viewDidLoad
{
  [super viewDidLoad];
 
  //set the corner radius on our layers
  self.layerView1.layer.cornerRadius = 20.0f;
  self.layerView2.layer.cornerRadius = 20.0f;
 
  //enable clipping on the second layer
  self.layerView2.layer.masksToBounds = YES;
}
@end

图4.2

右图中,红色的子视图沿角半径被裁剪了

如你所见,右边的子视图沿边界被裁剪了。

单独控制每一个层的圆角曲率也不是不可能的。若是想建立有些圆角有些直角的图层或视图时,你可能须要一些不一样的方法。好比使用一个图层蒙板(本章稍后会讲到)或者是CAShapeLayer(见第六章『专用图层』)。

图层边框

CALayer另外两个很是有用属性就是borderWidth和borderColor。两者共同定义了图层边的绘制样式。这条线(也被称做stroke)沿着图层的bounds绘制,同时也包含图层的角。

borderWidth是以点为单位的定义边框粗细的浮点数,默认为0.borderColor定义了边框的颜色,默认为黑色。

borderColor是CGColorRef类型,而不是UIColor,因此它不是Cocoa的内置对象。不过呢,你确定也清楚图层引用了borderColor,虽然属性声明并不能证实这一点。CGColorRef在引用/释放时候的行为表现得与NSObject极其类似。可是Objective-C语法并不支持这一作法,因此CGColorRef属性即使是强引用也只能经过assign关键字来声明。

边框是绘制在图层边界里面的,并且在全部子内容以前,也在子图层以前。若是咱们在以前的示例中(清单4.2)加入图层的边框,你就能看到究竟是怎么一回事了(如图4.3).

清单4.2 加上边框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@implementation ViewController
 
- (void)viewDidLoad
{
  [super viewDidLoad];
 
  //set the corner radius on our layers
  self.layerView1.layer.cornerRadius = 20.0f;
  self.layerView2.layer.cornerRadius = 20.0f;
 
  //add a border to our layers
  self.layerView1.layer.borderWidth = 5.0f;
  self.layerView2.layer.borderWidth = 5.0f;
 
  //enable clipping on the second layer
  self.layerView2.layer.masksToBounds = YES;
}
 
@end


图4.3

图4.3 给图层增长一个边框

仔细观察会发现边框并不会把寄宿图或子图层的形状计算进来,若是图层的子图层超过了边界,或者是寄宿图在透明区域有一个透明蒙板,边框仍然会沿着图层的边界绘制出来(如图4.4).

图4.4

图4.4 边框是跟随图层的边界变化的,而不是图层里面的内容

阴影

iOS的另外一个常见特性呢,就是阴影。阴影每每能够达到图层深度暗示的效果。也可以用来强调正在显示的图层和优先级(好比说一个在其余视图以前的弹出框),不过有时候他们只是单纯的装饰目的。

给shadowOpacity属性一个大于默认值(也就是0)的值,阴影就能够显示在任意图层之下。shadowOpacity是一个必须在0.0(不可见)和1.0(彻底不透明)之间的浮点数。若是设置为1.0,将会显示一个有轻微模糊的黑色阴影稍微在图层之上。若要改动阴影的表现,你可使用CALayer的另外三个属性:shadowColor,shadowOffset和shadowRadius。

显而易见,shadowColor属性控制着阴影的颜色,和borderColor和backgroundColor同样,它的类型也是CGColorRef。阴影默认是黑色,大多数时候你须要的阴影也是黑色的(其余颜色的阴影看起来是否是有一点点奇怪。。)。

shadowOffset属性控制着阴影的方向和距离。它是一个CGSize的值,宽度控制这阴影横向的位移,高度控制着纵向的位移。shadowOffset的默认值是 {0, -3},意即阴影相对于Y轴有3个点的向上位移。

为何要默认向上的阴影呢?尽管Core Animation是从图层套装演变而来(能够认为是为iOS建立的私有动画框架),可是呢,它倒是在Mac OS上面世的,前面有提到,两者的Y轴是颠倒的。这就致使了默认的3个点位移的阴影是向上的。在Mac上,shadowOffset的默认值是阴影向下的,这样你就能理解为何iOS上的阴影方向是向上的了(如图4.5).

图4.5

图4.5 在iOS(左)和Mac OS(右)上shadowOffset的表现。

苹果更倾向于用户界面的阴影应该是垂直向下的,因此在iOS把阴影宽度设为0,而后高度设为一个正值不失为一个作法。

shadowRadius属性控制着阴影的模糊度,当它的值是0的时候,阴影就和视图同样有一个很是肯定的边界线。当值愈来愈大的时候,边界线看上去就会愈来愈模糊和天然。苹果自家的应用设计更偏向于天然的阴影,因此一个非零值再合适不过了。

一般来说,若是你想让视图或控件很是醒目独立于背景以外(好比弹出框遮罩层),你就应该给shadowRadius设置一个稍大的值。阴影越模糊,图层的深度看上去就会更明显(如图4.6).

图4.6

图4.6 大一些的阴影位移和角半径会增长图层的深度即视感

阴影裁剪

和图层边框不一样,图层的阴影继承自内容的外形,而不是根据边界和角半径来肯定。为了计算出阴影的形状,Core Animation会将寄宿图(包括子视图,若是有的话)考虑在内,而后经过这些来完美搭配图层形状从而建立一个阴影(见图4.7)。

图4.7

图4.7 阴影是根据寄宿图的轮廓来肯定的

当阴影和裁剪扯上关系的时候就有一个头疼的限制:阴影一般就是在Layer的边界以外,若是你开启了masksToBounds属性,全部从图层中突出来的内容都会被才剪掉。若是咱们在咱们以前的边框示例项目中增长图层的阴影属性时,你就会发现问题所在(见图4.8).

图4.8

图4.8 maskToBounds属性裁剪掉了阴影和内容

从技术角度来讲,这个结果是能够是能够理解的,但确实又不是咱们想要的效果。若是你想沿着内容裁切,你须要用到两个图层:一个只画阴影的空的外图层,和一个用masksToBounds裁剪内容的内图层。

若是咱们把以前项目的右边用单独的视图把裁剪的视图包起来,咱们就能够解决这个问题(如图4.9).

图4.9

图4.9 右边,用额外的阴影转换视图包裹被裁剪的视图

咱们只把阴影用在最外层的视图上,内层视图进行裁剪。清单4.3是代码实现,图4.10是运行结果。

清单4.3 用一个额外的视图来解决阴影裁切的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@interface ViewController ()
 
@property (nonatomic, weak) IBOutlet UIView *layerView1;
@property (nonatomic, weak) IBOutlet UIView *layerView2;
@property (nonatomic, weak) IBOutlet UIView *shadowView;
 
@end
 
@implementation ViewController
- (void)viewDidLoad
{
  [super viewDidLoad];
 
  //set the corner radius on our layers
  self.layerView1.layer.cornerRadius = 20.0f;
  self.layerView2.layer.cornerRadius = 20.0f;
 
  //add a border to our layers
  self.layerView1.layer.borderWidth = 5.0f;
  self.layerView2.layer.borderWidth = 5.0f;
 
  //add a shadow to layerView1
  self.layerView1.layer.shadowOpacity = 0.5f;
  self.layerView1.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
  self.layerView1.layer.shadowRadius = 5.0f;
 
  //add same shadow to shadowView (not layerView2)
  self.shadowView.layer.shadowOpacity = 0.5f;
  self.shadowView.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
  self.shadowView.layer.shadowRadius = 5.0f;
 
  //enable clipping on the second layer
  self.layerView2.layer.masksToBounds = YES;
}
 
@end

图4.10

图4.10 右边视图,不受裁切阴影的阴影视图。

相关文章
相关标签/搜索