「OC」探索CALayer:基础知识与实用技巧简要介绍

ops/2024/11/14 12:53:42/

「OC」探索CALayer:基础知识与实用技巧简要介绍

文章目录

  • 「OC」探索CALayer:基础知识与实用技巧简要介绍
    • 前言
    • 认识CALayer
      • CALayer的相关属性
    • UIView和CALayer
      • 区别
      • 联系
      • 创建UIView和CALayer的原因
    • 开始创建CALayer
      • 视图层级
      • CALayers 和 Sublayers
      • position与anchorPoint(锚点)
      • CGImage和CGColor
      • 设置旋转
      • 裁切
      • border属性
    • 隐式动画
    • 自定义CALayer
      • 重写CALayer的子类
      • 实现CALayer协议方法
    • 参考文章

前言

在顺利完成暑假之中的任务之后,终于可以学习一点之前一直没有时间学习的内容啦。上一片文章写到「iOS」自定义Modal转场——抽屉视图的实现,我们已经成功的实现了抽屉视图,接下来是要实现cell之中的圆角。我看到要想实现这个圆角cell,需要OC之中的图形绘制CALayer的内容,于是我继续对CALayer进行学习,如果篇幅过长那么会将圆角cell的实现放到后面来。

认识CALayer

CALayer是属于Core Animation,简单说就是呈现内容和动画的层。

在iOS之中我们能够看到的基本上都是UIView,比如一个按钮,一个文本框等等…

那么这些UIView能够被显示在屏幕之中被我们看到,其实是就是由于这个UIView的layer属性(也就是CALayer对象),当这个UIView需要倍显示在屏幕之上的时候,就会通过调用drawRect:这个方法,访问图层进行绘制,可以说UIView本身是没有显示的功能,只有它内部的层级才具有显示功能。

我们可以通过以下方法获取UIView之中的图层

CALayer *myLayer = myView.layer;

CALayer的相关属性

4408163-f04410c51fec8d39.png-3

可以看到CALayer有着丰富的属性可以用来:

  • 调整图层的大小和位置
  • 调整图层的背景颜色
  • 修改图层的内容 (一个图片,或者是用 CoreGraphics 绘制的东西)
  • 图层是否圆角
  • 添加黑色投影
  • 添加描边的边框

UIView和CALayer

区别

CAlayerUIView最大的不同是CALayer不处理用户的交互。CALayer并不清楚具体的响应链(iOS通过视图层级关系用来传送触摸事件的机制),于是它并不能够响应事件,即使它提供了一些方法来判断是否一个触点在图层的范围之内。

UIApplicationUIViewControllerUIView、和所有从UIView派生出来的UIKit类(包括UIWindow)都直接或间接地继承自UIResponder类。在 UIResponder中定义了处理各种事件和事件传递的接口, 而 CALayer直接继承 NSObject,并没有相应的处理事件的接口。

如果显示出来的东西需要跟用户进行交互的话,那我们肯定要用UIView;如果不需要跟用户进行交互,我们尽量使用`CALayer,因为它少了事件处理的功能,性能会高一些

联系

2144507-d32bcab32145e840.png

代理关系:UIView 实现了 CALayerDelegate 协议,这意味着当系统需要绘制 CALayer 的内容时,实际上是 UIView 在幕后调用相关方法进行绘制。这种设计使得开发者可以在 UIView 的上下文中定制内容绘制逻辑,同时利用 CALayer 的高效渲染能力。

属性映射:许多 UIView 的视觉属性(如背景色、边框等)实际上是对内部 CALayer 相关属性的封装。更改 UIView 的这些属性时,实际上是在更改其 CALayer 的对应属性。

创建UIView和CALayer的原因

我们都知道,UIView和CALayer为两个平行的层级关系,那么为什么要这么设计呢?

这里我直接引用大佬博客之中的内容:

原因在于要做职责分离,这样也能避免很多重复代码。在iOS和Mac OS两个平台上,事件和用户交互有很多地方的不同,基于多点触控的用户界面和基于鼠标键盘有着本质的区别,这就是为什么iOS有UIKit和UIView,但是Mac OS有AppKit和NSView的原因。他们功能上很相似,但是在实现上有着显著的区别。

绘图,布局和动画,相比之下就是类似Mac笔记本和桌面系列一样应用于iPhone和iPad触屏的概念。把这种功能的逻辑分开并应用到独立的Core Animation框架,苹果就能够在iOS和Mac OS之间共享代码,使得对苹果自己的OS开发团队和第三方开发者去开发两个平台的应用更加便捷。

开始创建CALayer

我们先创建一个CALayer并且把它加入到视图控制器之中,然后我们再进行相关内容的讲解

- (void)viewDidLoad {[super viewDidLoad];self.layer = [[CALayer alloc] init];//背景设置self.layer.backgroundColor = [UIColor redColor].CGColor;//布局self.layer.bounds = CGRectMake(0, 0, 100, 100);self.layer.position = CGPointMake(100, 100);self.layer.anchorPoint = CGPointMake(0, 0);//旋转self.layer.transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);//设置border属性self.layer.borderWidth = 4;self.layer.borderColor = [UIColor purpleColor].CGColor;//裁切self.layer.cornerRadius = 10.0;self.layer.masksToBounds = YES;// 将layer添加到图层[self.view.layer addSublayer:self.layer];
}

image-20240817185029038

视图层级

我们从视图层级的角度来看,其实不难发现他没有被单独作为一个模块列出

image-20240817002653997

CALayers 和 Sublayers

就像 UIView 有很多 subview 一样, CALayer 也有它的 sublayer

视图树中每一个 view 的根 layer 根据视图关系, 形成相同关系的 layer 树, 同时每一个根 layer 还可以有自己的 sublayer, 如下图所示:

img

position与anchorPoint(锚点)

  • position属性决定该控件在父控件中的位置,以父控件的左上角为原点
  • anchorPoint属性决定该控件上的哪个点位于position位置

若position为(100, 100),anchorPoint为(0, 0 ),如图
img

若position为(100, 100),anchorPoint为(0.5, 0.5),如图
img

当然CALayer还是可以使用frame进行布局,只是如果使用CALayer的属性进行布局在灵活性方面则更占优

CGImage和CGColor

CGImage 是 Core Graphics 框架中用于表示图像的类。它提供了对图像数据的低级别控制,适用于图像处理、渲染和绘制。

用途: CGImage 主要用于表示和操作位图图像数据。它是 UIImage 的底层表示形式,能够提供直接的图像数据访问和操作。

CGColor 是 Core Graphics 框架中用于表示颜色的类。它用于描述颜色的具体值,包括色彩空间、颜色分量等。

用途: CGColor 用于在 Core Graphics 和 Core Animation 中指定颜色。它是 UIColor 的底层表示形式,用于图层的背景色、边框色等。

我们不用先前我们所熟知的UIColor和UIImage是因为,他们被定义在UIKit.h的框架之中的,而CALayer是QuartzCore框架的内容。

但是,很多情况下,可以通过UIKit对象的特定方法,得到CoreGraphics对象,比如UIImage的CGImage方法可以返回一个CGImageRef

设置旋转

利用transform属性可以设置旋转、缩放等效果

CATransform3D CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z);

参数说明

  • angle: 旋转的角度,以弧度为单位。可以使用 M_PIM_PI / 180 将角度转换为弧度。(M_PI 即为 360°)
  • x, y, z: 旋转轴的坐标分量。决定了围绕哪个轴旋转:
    • 如果 x 为 1,y 为 0,z 为 0,则围绕 X 轴旋转。
    • 如果 x 为 0,y 为 1,z 为 0,则围绕 Y 轴旋转。
    • 如果 x 为 0,y 为 0,z 为 1,则围绕 Z 轴旋转。

裁切

另外当 layer 中的绘制内容超过其 frame 的边界时, 可以通过 masksToBounds 属性来决定是否绘制边界以外的内容. 而 view 的 clipsToBounds 属性实际就是在设置 layer 的 maskToBounds 属性. 默认情况下设置为 false, 即要绘制超过边界的内容.

    self.layer.cornerRadius = 10.0;self.layer.masksToBounds = YES;

border属性

border即为

//设置border属性
imageLayer.borderWidth = 2;
imageLayer.borderColor = [UIColor purpleColor].CGColor;

隐式动画

为了探究CALayer自带的隐式动画,我自己写了一个好玩的内容

#import "ViewController.h"@interface ViewController ()
@property (nonatomic, strong) CALayer *layer;
@property (nonatomic, strong) UIView *views;
@property (nonatomic, strong) NSTimer *timer;
@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];self.layer = [[CALayer alloc] init];self.layer.backgroundColor = [UIColor redColor].CGColor;self.layer.bounds = CGRectMake(0, 0, 100, 100);self.layer.position = CGPointMake(100, 100);self.layer.anchorPoint = CGPointMake(0, 0);self.layer.cornerRadius = 10.0;self.layer.transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);[self.view.layer addSublayer:self.layer];self.views = [[UIView alloc] init];self.views.backgroundColor = [UIColor redColor];self.views.frame = CGRectMake(50, 250, 100, 100);[self.view addSubview:self.views];self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0target:selfselector:@selector(changeColor)userInfo:nilrepeats:YES];
}- (void)changeColor {// 生成随机颜色CGFloat red = arc4random_uniform(256) / 255.0;CGFloat green = arc4random_uniform(256) / 255.0;CGFloat blue = arc4random_uniform(256) / 255.0;self.layer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;self.views.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
}
@end

通过动图我们可以看到,我们在没有添加任何动画的情况下CALayer,在颜色发生变化时,会自动产生动画,以下是代码运行时的变化过程。

每一个UIView内部都默认关联着一个CALayer,我们可用称这个Layer为Root Layer(根层)。所有的非Root Layer,也就是手动创建的CALayer对象,都存在着隐式动画。我们对UIView的属性修改时时不会产生默认动画,而对单独 layer属性直接修改会,这个默认动画的时间缺省值是0.25s.

当对非Root Layer的部分属性进行相应的修改时,默认会自动产生一些动画效果:

  • bounds:用于设置CALayer的宽度和高度。修改这个属性会产生缩放动画
  • backgroundColor:用于设置CALayer的背景色。修改这个属性会产生背景色的渐变动画
  • position:用于设置CALayer的位置。修改这个属性会产生平移动画

Aug-17-2024 09-37-22

自定义CALayer

重写CALayer的子类

我们可以通过创建CALayer的子类来描绘我们想要实现的layer,示例如下:

#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN@interface ImageLayer : CALayer@property (nonatomic, strong) UIImage *image;
@property (nonatomic, strong) UIColor *background;@endNS_ASSUME_NONNULL_END
—————————————————————————————————————————————————————————————————————————————————————————————————————————
#import "ImageLayer.h"@implementation ImageLayer- (void)drawInContext:(CGContextRef)ctx {// Set the background colorCGContextSetFillColorWithColor(ctx, self.background.CGColor);CGContextFillRect(ctx, self.bounds);if (self.image) {// Draw the image centered in the layerCGRect imageRect;imageRect.size = self.image.size;imageRect.origin.x = (CGRectGetWidth(self.bounds) - imageRect.size.width) / 2;imageRect.origin.y = (CGRectGetHeight(self.bounds) - imageRect.size.height) / 2;CGContextDrawImage(ctx, imageRect, self.image.CGImage);}
}@end
—————————————————————————————————————————————————————————————————————————————————————————————————————————
- (void)viewDidLoad {[super viewDidLoad];ImageLayer *imagelayer = [ImageLayer layer];imagelayer.image = [UIImage imageNamed:@"back3.jpeg"];imagelayer.background = [UIColor yellowColor];imagelayer.frame = CGRectMake(50, 50, 200, 400);[imagelayer setNeedsDisplay];[self.view.layer addSublayer:imagelayer];
}

通过运行代码,我们可以获得以下CALayer

image-20240817114908445

实现CALayer协议方法

//创建图层
CALayer *imageLayer = [[CALayer alloc] init];
//设置代理
imageLayer.delegate = self;
imageLayer.bounds = CGRectMake(0, 0, 100, 100);
imageLayer.position = CGPointMake(100, 200);
[imageLayer setNeedsDisplay];
[self.view.layer addSublayer:imageLayer];

实现代理方法

- (void)drawLayer:(nonnull CALayer *)layer inContext:(nonnull CGContextRef)ctx
{//通过绘图方法绘制内容
}

参考文章

iOS动画篇_CALayer这些牛逼的子类你造吗

读 iOS核心动画高级技巧

CALayer_超经典的阐述原理

详解CALayer 和 UIView的区别和联系

读书笔记: iOS Layer 绘制


http://www.ppmy.cn/ops/97192.html

相关文章

鸿蒙HarmonyOS实战:IPC与RPC设备内进程通信

基本 IPC&#xff08;Inter-Process Communication&#xff09;与RPC&#xff08;Remote Procedure Call&#xff09;用于实现跨进程通信&#xff0c;不同的是前者使用Binder驱动&#xff0c;用于设备内的跨进程通信&#xff0c;后者使用软总线驱动&#xff0c;用于跨设备跨进…

如何快速进入医疗器械维修行业

在当今医疗技术飞速发展的时代&#xff0c;医疗器械维修行业作为医疗服务的重要支撑&#xff0c;展现出了广阔的发展前景和就业机会。对于那些有志于进入这一领域的人来说&#xff0c;掌握正确的方法和途径至关重要。 首先&#xff0c;扎实的知识储备是进入医疗器械维修行业的…

以太网交换基础

1.以太网协议介绍 以太网是当今现有局域网&#xff08;Local Area Network, LAN&#xff09;采用的最通用的通信协议标准&#xff0c;该标准定义了在局域网中采用的电缆类型和信号处理方法。 冲突域 冲突域是指连接在同一共享介质上的所有节点的集合&#xff0c;冲突域内所有…

Graphpad Prism for Mac 医学绘图软件教程

Mac分享吧 文章目录 效果一、下载软件二、开始安装1、双击运行软件&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功 三、运行测试安装完成&#xff01;&#xff01;&#xff01; 效果 一、下载软件 下载软件…

数据库学习(进阶)

数据库学习&#xff08;进阶&#xff09; Mysql结构:连接层&#xff1a;服务层&#xff08;核心层&#xff09;&#xff1a;存储引擎层&#xff1a;系统文件层&#xff1a; 存储引擎&#xff08;概述&#xff09;:存储引擎特点&#xff1a;InnoDB存储引擎&#xff1a;(为并发条…

day23-测试自动化之Appium的滑动和拖拽事件、高级手势ActionChains、手机操作API

目录 一、滑动和拖拽事件 1.1.应用场景 1.2.swipe滑动事件 1.3.scroll滑动事件 1.4.drag_and_drop拖拽事件 1.5.滑动和拖拽事件的选择 二、高级手势ActionChains 2.1.应用场景 2.2.使用步骤 2.3.注意点 2.4.方法 1).手指轻敲操作 (掌握) 2).手势按下和抬起操作(掌握&#xff0…

响应式Web设计:纯HTML和CSS的实现技巧-1

响应式Web设计&#xff08;Responsive Web Design, RWD&#xff09;是一种旨在确保网站在不同设备和屏幕尺寸下都能良好运行的网页设计策略。通过纯HTML和CSS实现响应式设计&#xff0c;主要依赖于媒体查询&#xff08;Media Queries&#xff09;、灵活的布局、可伸缩的图片和字…

React 入门第一天:从Vue到React的初体验

作为一名合格的前端工程师&#xff0c;怎么能只会Vue呢&#xff1f;学习React不仅是一场新技术的探索&#xff0c;更是对前端开发思维的一次重新审视。在这里&#xff0c;我将分享学习React的心得&#xff0c;希望能帮助那些和我一样从Vue转向React的开发者。 1. 为什么选择Re…