版本记录
版本号 | 时间 |
---|---|
V1.0 | 2020.07.10 星期五 |
前言
iOS中有关视图控件用户能看到的都在UIKit框架里面,用户交互也是通过UIKit进行的。感兴趣的参考上面几篇文章。
1. UIKit框架(一) —— UIKit动力学和移动效果(一)
2. UIKit框架(二) —— UIKit动力学和移动效果(二)
3. UIKit框架(三) —— UICollectionViewCell的扩张效果的实现(一)
4. UIKit框架(四) —— UICollectionViewCell的扩张效果的实现(二)
5. UIKit框架(五) —— 自定义控件:可重复使用的滑块(一)
6. UIKit框架(六) —— 自定义控件:可重复使用的滑块(二)
7. UIKit框架(七) —— 动态尺寸UITableViewCell的实现(一)
8. UIKit框架(八) —— 动态尺寸UITableViewCell的实现(二)
9. UIKit框架(九) —— UICollectionView的数据异步预加载(一)
10. UIKit框架(十) —— UICollectionView的数据异步预加载(二)
11. UIKit框架(十一) —— UICollectionView的重用、选择和重排序(一)
12. UIKit框架(十二) —— UICollectionView的重用、选择和重排序(二)
13. UIKit框架(十三) —— 如何创建自己的侧滑式面板导航(一)
14. UIKit框架(十四) —— 如何创建自己的侧滑式面板导航(二)
15. UIKit框架(十五) —— 基于自定义UICollectionViewLayout布局的简单示例(一)
16. UIKit框架(十六) —— 基于自定义UICollectionViewLayout布局的简单示例(二)
17. UIKit框架(十七) —— 基于自定义UICollectionViewLayout布局的简单示例(三)
18. UIKit框架(十八) —— 基于CALayer属性的一种3D边栏动画的实现(一)
19. UIKit框架(十九) —— 基于CALayer属性的一种3D边栏动画的实现(二)
20. UIKit框架(二十) —— 基于UILabel跑马灯类似效果的实现(一)
21. UIKit框架(二十一) —— UIStackView的使用(一)
22. UIKit框架(二十二) —— 基于UIPresentationController的自定义viewController的转场和展示(一)
23. UIKit框架(二十三) —— 基于UIPresentationController的自定义viewController的转场和展示(二)
24. UIKit框架(二十四) —— 基于UICollectionViews和Drag-Drop在两个APP间的使用示例 (一)
25. UIKit框架(二十五) —— 基于UICollectionViews和Drag-Drop在两个APP间的使用示例 (二)
26. UIKit框架(二十六) —— UICollectionView的自定义布局 (一)
27. UIKit框架(二十七) —— UICollectionView的自定义布局 (二)
28. UIKit框架(二十八) —— 一个UISplitViewController的简单实用示例 (一)
29. UIKit框架(二十九) —— 一个UISplitViewController的简单实用示例 (二)
30. UIKit框架(三十) —— 基于UICollectionViewCompositionalLayout API的UICollectionViews布局的简单示例(一)
31. UIKit框架(三十一) —— 基于UICollectionViewCompositionalLayout API的UICollectionViews布局的简单示例(二)
32. UIKit框架(三十二) —— 替换Peek and Pop交互的基于iOS13的Context Menus(一)
33. UIKit框架(三十三) —— 替换Peek and Pop交互的基于iOS13的Context Menus(二)
34. UIKit框架(三十四) —— Accessibility的使用(一)
35. UIKit框架(三十五) —— Accessibility的使用(二)
36. UIKit框架(三十六) —— UICollectionView UICollectionViewDiffableDataSource的使用(一)
37. UIKit框架(三十七) —— UICollectionView UICollectionViewDiffableDataSource的使用(二)
38. UIKit框架(三十八) —— 基于CollectionView转盘效果的实现(一)
39. UIKit框架(三十九) —— iOS 13中UISearchController 和 UISearchBar的新更改(一)
40. UIKit框架(四十) —— iOS 13中UISearchController 和 UISearchBar的新更改(二)
41. UIKit框架(四十一) —— 使用协议构建自定义Collection(一)
42. UIKit框架(四十二) —— 使用协议构建自定义Collection(二)
开始
首先看下主要内容:
在本文中,您将了解CALayer及其工作原理。 您将使用CALayer以获得酷炫的效果,例如形状,渐变和粒子系统。本文内容来自翻译。
接着看一下写作环境:
Swift 5, iOS 13, Xcode 11
下面就是正文了
您可能知道,在iOS应用中看到的所有内容都是视图。有按钮视图,表格视图,滑块视图,甚至包含其他视图的父视图!
但是您可能不知道,iOS中的每个视图都由另一个称为图层的类(具体来说就是CALayer
)支持。
在本文中,您将构建Layer Player
应用程序。在此过程中,您将学到:
- 什么是
CALayer
及其运作方式。 - 如何使用
CALayer
功能来实现酷效果,例如形状,渐变甚至粒子系统。
CALayer
具有多种属性和方法可以修改。它具有几个具有独特属性和方法的子类。
Layer Player
应用程序演示了九种不同的CALayer
功能。
入门项目并没有做什么用。您将要编写代码以将此应用程序变成功能齐全的CALayer
指导!
在每个部分中,您都将添加必要的代码以使神奇的图层发挥作用。 添加了每一行代码后,请按照刚刚启用的设置进行操作。 这将使您对所探索的每种功能的功能有更深入的了解。
但是首先,来一些理论。
How does CALayer relate to UIView?
UIView
处理许多事情,包括布局和触摸事件。 但是,它不能直接控制图形或动画。 UIKit
将该任务委托给Core Animation
框架,从而可以使用CALayer
。 实际上,UIView
只是CALayer
的封装。
每个UIView
都有一个根CALayer
,可以包含多个子层。 当您在UIView
上设置bounds
时,该视图又在其支持CALayer
上设置bounds
。 如果在UIView
上调用layoutIfNeeded()
,则该调用将转发到根CALayer
。
在深入研究Layer Player
应用程序之前,还应该了解一些有关CALayer
的知识:
- Layers can have sublayers - 层可以具有子层:就像视图可以具有子视图一样,层也可以具有子层。 您可以将它们用于一些很酷的效果!
- Their properties are animatable - 它们的属性是可设置动画的:更改图层的属性时,可以使用
CAAnimation
为更改设置动画。 - Layers are lightweight - 图层重量轻:图层的重量比视图轻,因此可以帮助您获得更好的性能。
- They have tons of useful properties - 它们具有大量有用的属性:您将在以下示例中进行探索。
现在,您就可以开始使用CALayer
创建一些自定义视图了。
Customizing Views With CALayer
打开CALayerViewController.swift
并将以下代码添加到setupLayer()
中:
//1
layer.frame = viewForLayer.bounds
layer.contents = UIImage(named: "star")?.cgImage
// 2
layer.contentsGravity = .center
layer.magnificationFilter = .linear
// 3
layer.cornerRadius = 100.0
layer.borderWidth = 12.0
layer.borderColor = UIColor.white.cgColor
layer.backgroundColor = swiftOrangeColor.cgColor
//4
layer.shadowOpacity = 0.75
layer.shadowOffset = CGSize(width: 0, height: 3)
layer.shadowRadius = 3.0
layer.isGeometryFlipped = false
在此代码中,您将创建一个自定义视图:
- 1)设置图层
layer
的边界bounds
,然后将图像设置为图层的内容。 注意底层CGImage
的使用。 - 2)然后,将图像居中放置在图层中。 您可以使用
contentsGravity
来更改大小(如resizing, resizing aspect and resizing aspect fill
)以及位置(中心,顶部,右上角,右边等)。magnificationFilter
控制放大图像的行为。 - 3)接下来,设置
CALayer
的背景颜色,使其变圆并为其添加边框。 请注意,您正在使用基础CGColor
对象更改图层的颜色属性。 - 4)最后,为图层创建阴影。 当
isGeometryFlipped
为true
时,位置几何形状和阴影将上下颠倒。
构建并运行。 选择CALayer
,然后检查结果:
控件目前不执行任何操作。 因此,添加以下内容以到prepare(for:sender:)
:
if segue.identifier == "DisplayLayerControls" {
(segue.destination as? CALayerControlsViewController)?.layerViewController = self
}
这将连接嵌入式CALayerControlsViewController
。 再次构建并运行,然后检查实际使用的图层属性。 试玩各种控件,以感受一下使用CALayer
可以做什么!
1. Improving Performance With ShouldRasterize and DrawsAsynchronously
CALayer
具有两个其他可改善性能的属性:shouldRasterize
和drawsAsynchronously
。
默认情况下,shouldRasterize
为false
。设置为true
时,图层的内容仅渲染一次,从而提高了性能。非常适合在屏幕上动起来但外观不变的对象。
drawsAsynchronously
与shouldRasterize
相反,默认情况下也为false
。当应用需要重复重绘图层内容时,将其设置为true
可以提高性能。例如,当您使用连续渲染动画粒子的发射器层时,可能会发生这种情况。您将在本教程的后面部分中使用此功能。
注意:在设置
shouldRasterize
或drawsAsynchronously
之前,请先考虑其含义。更改设置并比较性能。这将帮助您确定在给定情况下激活这些功能是否实际上会提高性能。如果您滥用这些属性,性能可能会降低!
Scrolling With CAScrollLayer
CAScrollLayer
显示可滚动图层的一部分。 这是非常基本的-它无法直接响应用户的触摸,甚至无法检查可滚动图层的bounds
。 但这确实很酷,例如防止滚动超出bounds
。
UIScrollView
不使用CAScrollLayer
来完成其工作。 而是直接更改图层的bounds
。 使用CAScrollLayer
,您可以:
- 将滚动模式设置为水平或垂直
(horizontal or vertical)
。 - 以编程方式滚动到特定点
point
或区域area
。
构建并运行,然后从菜单中选择CAScrollLayer
。 您会看到一张图片和两个控制滚动方向的开关。
现在是时候设置滚动了。
1. Setting up Scrolling
返回代码并打开CAScrollLayerViewController.swift
。 视图中已经有一个CAScrollLayer
。
将以下内容添加到panRecognized(_ :)
:
var newPoint = scrollingView.bounds.origin
newPoint.x -= sender.translation(in: scrollingView).x
newPoint.y -= sender.translation(in: scrollingView).y
sender.setTranslation(.zero, in: scrollingView)
scrollingViewLayer.scroll(to: newPoint)
if sender.state == .ended {
UIView.animate(withDuration: 0.3) {
self.scrollingViewLayer.scroll(to: CGPoint.zero)
}
}
发生平移手势时,您需要计算所需的相应平移,然后在CAScrollLayer
上调用scroll(to :)
方法以相应地移动图像。
scroll(to :)
不会自动设置动画,因此您可以使用UIView.animate(withDuration:animations :)
对其进行明确设置。
构建并运行,然后返回到CAScrollLayer
示例。 现在,平移图像时,您将看到类似以下的内容:
Layer Player
还包括两个控件,用于锁定水平和垂直滚动。 接下来,您将看一下这部分。
2. Locking Scrolling Directions
将以下代码添加到scrollingSwitchChanged(_ :)
:
switch (horizontalScrollingSwitch.isOn, verticalScrollingSwitch.isOn) {
case (true, true):
scrollingViewLayer.scrollMode = .both
case (true, false):
scrollingViewLayer.scrollMode = .horizontally
case (false, true):
scrollingViewLayer.scrollMode = .vertically
default:
scrollingViewLayer.scrollMode = .none
}
在这里,您添加了一个简单的switch
块。 它根据用户界面中开关的值确定滚动方向。
现在构建并运行。 返回应用程序,拨动开关,然后查看CAScrollLayer
的行为。
以下是何时使用或不使用CAScrollLayer
的一些经验法则:
- 如果您想要轻巧
(lightweight)
的东西并且只需要以编程方式滚动,请考虑使用CAScrollLayer
。 - 如果希望用户能够滚动,最好使用
UIScrollView
。 要了解更多信息,请查看我们的Scroll View School视频教程系列。 - 如果要滚动很大的图像,请考虑使用
CATiledLayer
。
Rendering Text With CATextLayer
CATextLayer
提供简单但快速的纯文本或属性字符串渲染。 与UILabel
不同,CATextLayer
不能具有分配的UIFont
,只能具有CTFont
或CGFont
。
打开CATextLayerViewController.swift
并将以下内容添加到setUpTextLayer()
的末尾:
// 1
textLayer.font = helveticaFont
textLayer.fontSize = Constants.baseFontSize
// 2
textLayer.foregroundColor = UIColor.darkGray.cgColor
textLayer.isWrapped = true
textLayer.alignmentMode = .left
textLayer.truncationMode = .end
// 3
textLayer.contentsScale = UIScreen.main.scale
这是您正在做的事情:
- 1) 您设置文本图层的字体。请注意,这是
CTFont
,而不是UIFont
。您可以使用CTFontCreateWithName(_:_:_ :)
创建这些文件。CATextLayer
允许您像在此处一样直接在图层上设置字体大小。 - 2) 接下来,设置文本颜色,换行,对齐和截断模式。所有这些都可以在常规
UILabel
或UITextView
上获得。 - 3) 最后,将图层的
contentsScale
设置为与屏幕的比例相匹配。默认情况下,所有图层类(不仅是CATextLayer
)都以比例因子1
进行渲染。将图层附加到视图会自动将其contentScale
设置为当前屏幕的适当比例因子。但是,对于您手动创建的任何图层,必须显式设置contentsScale
。否则,其比例因子将为1
,从而使其在Retina
显示器上显示为像素化。
构建并运行,然后从菜单中选择CATextLayer
。 Layer Player
具有可以更改许多CATextLayer
属性的控件。和他们一起玩,看看他们做什么:
接下来,您将使Layer Player
能够播放媒体文件。
Playing Media With AVPlayerLayer
AVPlayerLayer
为AVFoundation
增添了美好的一面。 它拥有一个AVPlayer
来播放AVPlayerItem
类型的媒体文件。
1. Setting up Media Playback
打开AVPlayerLayerViewController.swift
并将以下代码添加到setUpPlayerLayer()
中:
// 1
playerLayer.frame = viewForPlayerLayer.bounds
// 2
let url = Bundle.main.url(forResource: "colorfulStreak", withExtension: "m4v")!
let item = AVPlayerItem(asset: AVAsset(url: url))
let player = AVPlayer(playerItem: item)
// 3
player.actionAtItemEnd = .none
// 4
player.volume = 1.0
player.rate = 1.0
playerLayer.player = player
在此代码中,您设置了播放器。这是如何做:
- 1) 首先,设置图层的
frame
。 - 2) 接下来,使用
AVPlayerItem
创建一个player
。 - 3) 您告诉播放器完成播放后什么也不做。其他选项包括暂停或前进到下一个资源(如果适用)。
- 4) 最后,设置播放器的音量和速率。
现在您可以播放媒体文件了,您将使用户能够更改他们的播放速度。
2. Changing Playback Speeds
rate
设置视频播放速度。 0
表示暂停,1
表示视频以正常速度播放。
但是,设置速率rate
也会指示以该速率开始播放。换句话说,调用pause()
并将rate
设置为0
作用是一样的,同样调用play()
并将rate
设置为1
作用也是一样!
那么快进,慢动作或反向播放呢?好吧,AVPlayer
都可以满足您!
当您将rate
设置为高于1
的任何值时,播放将以该速度乘以常规速度开始。例如,将rate
设置为2
表示倍速。并将rate
设置为负数会导致以该速度乘以正常速度反向播放。
但是,在以常规速度向前播放以外的任何速率进行播放之前,应检查AVPlayerItem
中的相应变量以验证是否可以该速率播放:
-
canPlayFastForward
大于1
的任何数字。 -
canPlaySlowForward
的范围为0
到最大(但不包括1
)之间的任何数字。 -
canPlayReverse
为-1
。 -
canPlaySlowReverse
的范围是-1
到最大(但不包括0
)之间的任何数字。 -
canPlayFastReverse
的值小于-1
。
大多数视频可以以各种前进速度播放,但很少可以反向播放。
3. Updating the User Interface
现在,回到代码。当您点击播放按钮时,它应切换控件以播放AVAset
并设置按钮的标题。
将以下内容添加到playButtonTapped(_ :)
:
if player?.rate == 0 {
player?.rate = rate
updatePlayButtonTitle(isPlaying: true)
} else {
player?.pause()
updatePlayButtonTitle(isPlaying: false)
}
在此,您可以根据初始速率切换播放器的状态并更新播放按钮的标题。
最后,您将添加代码,以在播放器到达媒体文件的末尾时将播放光标移回开头。
4. Resetting the Playback Cursor
在viewDidLoad()
中,您可以看到AVPlayerItemDidPlayToEndTimeNotification
具有observer
。 当AVPlayerItem
到达末尾时,将调用此通知。
将以下代码添加到playerDidReachEndNotificationHandler(_ :)
:
// 1
guard let playerItem = notification.object as? AVPlayerItem else { return }
// 2
playerItem.seek(to: .zero, completionHandler: nil)
// 3
if player?.actionAtItemEnd == .pause {
player?.pause()
updatePlayButtonTitle(isPlaying: false)
}
在此代码中:
- 1) 您验证通知对象是
AVPlayerItem
。 - 2) 您可以使用
seek(to:completionHandler :)
将播放器发送到所需位置。 在这种情况下,您将播放器发送到CMTime.zero
这是开始。 - 3) 如果播放器的
actionAtItemEnd
设置为.pause
,则可以暂停播放器,并相应地设置按钮文本。
构建并运行,然后从菜单中选择AVPlayerLayer
。 更改控件上的值,以查看每个控件如何更改图层的行为。
下一步,您将看到使用CALayer
制作炫酷渐变非常容易。
Color Blending With CAGradientLayer
CAGradientLayer
使混合两种或多种颜色变得容易,这使其特别适合于背景。您可以通过分配以下内容进行配置:
-
CGColors
数组。 - 一个开始点
startPoint
,用于指定渐变图层应从何处开始。 - 一个
Endpoint
,用于指定渐变层应在何处结束。
请记住,startPoint
和endPoint
不是显式点。而是在单位坐标空间中定义它们,并在绘制它们时将它们映射到图层的边界。换一种说法:
-
x
值为1
表示该点位于图层的右边缘。 -
y
值为1
表示该点位于图层的底部边缘。
CAGradientLayer
具有type
属性,可用于更改渐变图案。默认(也是最常用的)是axial
(线性)。这意味着,如果在startPoint
和endPoint
之间绘制一条线(A)
,则渐变将沿着垂直于A
的假想线(B)
发生,并且B
上的所有点都将具有相同的颜色:
或者,您可以使用locations
属性控制渐变图案。 location
接受一个介于0
到1
之间的值的数组。这些值指定相对停止点,渐变层应在其中使用colors
数组中的下一个颜色。 如果未指定,则默认情况下停止位置的间隔均匀。
注意:如果设置
locations
,则其数量必须与colors
的数量匹配,否则会发生不良情况。 所以要当心!
打开CAGradientLayerViewController.swift
并将以下代码添加到setUpGradientLayer()
中:
gradientLayer.frame = viewForGradientLayer.bounds
gradientLayer.colors = colors
gradientLayer.locations = locations.map { NSNumber(value: $0) }
gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0)
gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
在此代码中,您为图层分配了一个颜色数组,并设置了起点和终点。
构建并运行,从菜单中选择CAGradientLayer
,然后查看结果:
好丰富多彩! Layer Player
为您提供了更改起点和终点,颜色和位置的控件。 尝试他们的乐趣,看看您能得到什么。
对于下一步,您将向Layer Player
添加加载动画。
Creating a Loading Animation with CAReplicatorLayer
CAReplicatorLayer
将图层复制指定的次数。 这使您可以创建一些令人惊讶的炫酷效果!
每个图层副本可以具有自己的颜色和位置更改。 您可以延迟其绘制,以使整个复制器层具有动画效果。
构建并运行,然后从菜单中选择CAReplicatorLayer
。 您会看到几个控件……但没有什么可控制:
首先,配置要复制的图层。
1. Configuring Replicator Layers
返回Xcode并打开CAReplicatorLayerViewController.swift
。 在// MARK: - Layer setup
下有三种方法要填充。
首先,将以下内容添加到setUpReplicatorLayer()
中:
// 1
replicatorLayer.frame = viewForReplicatorLayer.bounds
// 2
let count = instanceCountSlider.value
replicatorLayer.instanceCount = Int(count)
replicatorLayer.instanceDelay =
CFTimeInterval(instanceDelaySlider.value / count)
// 3
replicatorLayer.instanceColor = UIColor.white.cgColor
replicatorLayer.instanceRedOffset = offsetValueForSwitch(offsetRedSwitch)
replicatorLayer.instanceGreenOffset = offsetValueForSwitch(offsetGreenSwitch)
replicatorLayer.instanceBlueOffset = offsetValueForSwitch(offsetBlueSwitch)
replicatorLayer.instanceAlphaOffset = offsetValueForSwitch(offsetAlphaSwitch)
//4
let angle = Float.pi * 2.0 / count
replicatorLayer.instanceTransform =
CATransform3DMakeRotation(CGFloat(angle), 0.0, 0.0, 1.0)
//5
viewForReplicatorLayer.layer.addSublayer(replicatorLayer)
在此代码中,配置复制图层(replicator layer)
的实例:
- 1) 首先,设置图层的
frame
。 - 2) 您可以设置要创建的复制数量以及两次创建之间的时间延迟。
- 3) 您可以定义所有复制实例的基础颜色以及要添加到每个颜色组件的增量值。 每个默认值均为0,可有效保留所有实例的颜色值。 但是,在这种情况下,实例颜色最初设置为白色。 这意味着红色,绿色和蓝色已经是1.0。 例如,如果将红色设置为0,并将绿色和蓝色设置为负数,则红色将成为突出的颜色。 同样,将
alpha
偏移量添加到每个连续复制实例的alpha
中。 - 4) 现在,旋转每个实例以创建一个圆形效果。
- 5) 最后,将图层添加到视图的图层。
2. Setting Position and Fade
接下来,为replicator layer
创建一个实例层以供使用。 将以下内容添加到setUpInstanceLayer
:
let layerWidth = CGFloat(layerSizeSlider.value)
let midX = viewForReplicatorLayer.bounds.midX - layerWidth / 2.0
instanceLayer.frame = CGRect(
x: midX,
y: 0.0,
width: layerWidth,
height: layerWidth * lengthMultiplier)
instanceLayer.backgroundColor = UIColor.white.cgColor
replicatorLayer.addSublayer(instanceLayer)
此代码设置实例frame
。 在这里,第一个实例将绘制在中心x
和viewForReplicatorLayer
的bounds
的顶部。 然后,您设置实例的颜色并将实例层添加到replicator layer
。
现在用于淡入淡出动画。 将以下内容添加到setUpLayerFadeAnimation
中:
fadeAnimation.fromValue = 1.0
fadeAnimation.toValue = 0.0
fadeAnimation.repeatCount = Float(Int.max)
在这里,您使用CABasicAnimation
的fadeAnimation
将实例的不透明度从1
(不透明)更改为0
(透明)。
构建并运行并享受您的代码现在所做的工作:
Layer Player
包含用于操纵大多数这些属性的控件。 更改值,看看它们如何影响动画!
现在,您已经创建了一个有趣的动画圆,接下来将使用CALayer
的另一个属性绘制星星。
Drawing a Star With CAShapeLayer
CAShapeLayer
使用可缩放的矢量路径绘制线条,形状和图案-比使用图片要快得多! 另一个好处是,您无需提供常规的@ 2x
和@ 3x
尺寸的图像。
此外,您还可以使用各种属性来自定义图形。 例如,您可以调整线的粗细,颜色,虚线并指定线的连接方式。 而且,当您的线条形成形状时,您可以更改其填充方式。
现在该画一个橙色的星星了! 首先,打开CAShapeLayerViewController.swift
并将以下内容添加到setUpShapeLayer()
中:
// 1
shapeLayer.path = openPath.cgPath
// 2
shapeLayer.lineCap = .butt
shapeLayer.lineJoin = .miter
shapeLayer.miterLimit = 4.0
// 3
shapeLayer.lineWidth = CGFloat(lineWidthSlider.value)
shapeLayer.strokeColor = swiftOrangeColor.cgColor
shapeLayer.fillColor = UIColor.white.cgColor
// 4
shapeLayer.lineDashPattern = nil
shapeLayer.lineDashPhase = 0.0
viewForShapeLayer.layer.addSublayer(shapeLayer)
经历这个:
- 1)
CAShapeLayer
采用CGPath
,它定义了如何在屏幕上绘制它。 您很快就会画出这条路。 - 2)
lineJoin
和lineCap
定义路径中的线如何相交和结束。 - 3) 设置路径线的宽度和颜色。
- 4)
lineDashPattern
和lineDashPhase
允许您绘制虚线(dashed lines)
。 在这种情况下,您将绘制不带dashes
的简单线条。
接下来,您将绘制路径本身。 将以下内容添加到setUpPath()
中:
openPath.move(to: CGPoint(x: 30, y: 196))
openPath.addCurve(
to: CGPoint(x: 112.0, y: 12.5),
controlPoint1: CGPoint(x: 110.56, y: 13.79),
controlPoint2: CGPoint(x: 112.07, y: 13.01))
openPath.addCurve(
to: CGPoint(x: 194, y: 196),
controlPoint1: CGPoint(x: 111.9, y: 11.81),
controlPoint2: CGPoint(x: 194, y: 196))
openPath.addLine(to: CGPoint(x: 30.0, y: 85.68))
openPath.addLine(to: CGPoint(x: 194.0, y: 48.91))
openPath.addLine(to: CGPoint(x: 30, y: 196))
这将绘制您的星形。
构建并运行,然后选择CAShapeLayer
以查看结果:
Layer Player
包含用于操纵许多CAShapeLayer
属性的控件。 玩这些控件来调整星星:
CALayer
不仅可以让您绘制2D
图形,还可以绘制3D
形状。 这就是您在下一节中要做的。
Drawing a 3D Cube With CATransformLayer
CATransformLayer
不会像其他图层类一样展平其子图层层次结构。 这使得绘制3D结构非常方便。 CATransformLayer
实际上是其子层的容器。 每个子层可以具有自己的transforms
和opacity
更改。 但是,它忽略对其他渲染图层属性(如边框宽度和颜色)的更改。
在本部分中,您将构建一个交互式3D
立方体。 首先打开CATransformLayerViewController.swift
并将以下代码添加到buildCube()
中:
// 1
transformLayer = CATransformLayer()
// 2
let redLayer = sideLayer(color: redColor)
redLayer.addSublayer(swipeMeTextLayer)
transformLayer.addSublayer(redLayer)
// 3
let orangeLayer = sideLayer(color: orangeColor)
var orangeTransform = CATransform3DMakeTranslation(
sideLength / 2.0,
0.0,
sideLength / -2.0)
orangeTransform = CATransform3DRotate(
orangeTransform,
degreesToRadians(90.0),
0.0,
1.0,
0.0)
orangeLayer.transform = orangeTransform
transformLayer.addSublayer(orangeLayer)
在这里,您创建cube
的前两个侧面:
- 1) 首先,创建一个
CATransformerLayer
。 - 2) 接下来,添加一个
CALayer
来表示cube的红色面,并将其添加到
transformLayer`。 - 3) 然后,添加
cube的橙色面。 您可以通过设置
transform属性来定位它,该属性接受
CATransform3D`。
同样,添加以下代码:
let yellowLayer = sideLayer(color: yellowColor)
yellowLayer.transform = CATransform3DMakeTranslation(0.0, 0.0, -sideLength)
transformLayer.addSublayer(yellowLayer)
let greenLayer = sideLayer(color: greenColor)
var greenTransform = CATransform3DMakeTranslation(
sideLength / -2.0,
0.0,
sideLength / -2.0)
greenTransform = CATransform3DRotate(
greenTransform,
degreesToRadians(90.0),
0.0,
1.0,
0.0)
greenLayer.transform = greenTransform
transformLayer.addSublayer(greenLayer)
let blueLayer = sideLayer(color: blueColor)
var blueTransform = CATransform3DMakeTranslation(
0.0,
sideLength / -2.0,
sideLength / -2.0)
blueTransform = CATransform3DRotate(
blueTransform,
degreesToRadians(90.0),
1.0,
0.0,
0.0)
blueLayer.transform = blueTransform
transformLayer.addSublayer(blueLayer)
let purpleLayer = sideLayer(color: purpleColor)
var purpleTransform = CATransform3DMakeTranslation(
0.0,
sideLength / 2.0,
sideLength / -2.0)
purpleTransform = CATransform3DRotate(
purpleTransform,
degreesToRadians(90.0),
1.0,
0.0,
0.0)
purpleLayer.transform = purpleTransform
transformLayer.addSublayer(purpleLayer)
在这里,创建cube
的其余四个面。
最后,设置图层的z
锚点,然后通过添加以下内容将其添加到屏幕上:
transformLayer.anchorPointZ = sideLength / -2.0
viewForTransformLayer.layer.addSublayer(transformLayer)
在这里,您可以配置anchorPointZ
,以指定发生几何操作的z
轴上的锚点。
注意:要了解有关矩阵变换的更多信息,请查看以下练习:
- Rich Turton 的 3DTransformFun project项目。
- Enter The Matrix project 的
Matrix
项目。
现在,您已经创建了cube
,是时候设置它了。
1. Rotating the Cube
要旋转cube
,您需要处理触摸事件。 您无法直接点击hit test
变换图层(transform layer)
,因为它没有2D
坐标空间可用于映射接触点。 但是,可以对各个子层进行测试。
该项目代码已经包括Bill Dudney
的 TrackBall utility实用程序,该实用程序已移植到Swift
。 这使基于用户手势的3D transform
变得容易。 您可以在// MARK: - Touch Handling
下check it out
。
构建并运行。 从菜单中选择CATransformLayer
,您会看到一个红色正方形:
滑动cube
并切换开关以查看它们如何更改cube
:
Making a Shooting Star With CAEmitterLayer
在计算机图形学中,粒子系统Particle Systems用于生成现实世界的现象,例如火,烟,火花,烟花和爆炸。
在Core Animation
中,CAEmitterLayer
可用于渲染此类系统并控制作为CAEmitterCell
实例的动画粒子。 CAEmitterLayer
和CAEmitterCell
都具有更改渲染速率,大小,形状,颜色,速度,寿命等的属性。
1. Preparing the Emitter Layer
发射层配置发射粒子的位置和形状。
将以下内容添加到CAEmitterLayerViewController.swift
中的setUpEmitterLayer()
中:
// 1
resetEmitterCells()
emitterLayer.frame = viewForEmitterLayer.bounds
viewForEmitterLayer.layer.addSublayer(emitterLayer)
// 2
emitterLayer.seed = UInt32(Date().timeIntervalSince1970)
// 3
emitterLayer.emitterPosition = CGPoint(
x: viewForEmitterLayer.bounds.midX * 1.5,
y: viewForEmitterLayer.bounds.midY)
// 4
emitterLayer.renderMode = .additive
这是您所做的:
- 1) 首先,重置图层并将其添加到视图中。
- 2) 然后,为该层的随机数生成器提供种子。 反过来,这会随机化层的发射器单元的某些属性,例如速度。 有关更多信息,请参见下一个代码块之后的以下注释。
- 3) 您设置发射器位置。
- 4) 最后,定义粒子单元如何渲染到图层中。
2. Setting up the Emitter Cell
接下来,设置发射器单元(emitter cell)
,该单元确定粒子的特定行为和外观。
将以下内容添加到setUpEmitterCell()
中:
// 1
emitterCell.contents = UIImage(named: "smallStar")?.cgImage
// 2
emitterCell.velocity = 50.0
emitterCell.velocityRange = 500.0
// 3
emitterCell.color = UIColor.black.cgColor
// 4
emitterCell.redRange = 1.0
emitterCell.greenRange = 1.0
emitterCell.blueRange = 1.0
emitterCell.alphaRange = 0.0
emitterCell.redSpeed = 0.0
emitterCell.greenSpeed = 0.0
emitterCell.blueSpeed = 0.0
emitterCell.alphaSpeed = -0.5
emitterCell.scaleSpeed = 0.1
// 5
let zeroDegreesInRadians = degreesToRadians(0.0)
emitterCell.spin = degreesToRadians(130.0)
emitterCell.spinRange = zeroDegreesInRadians
emitterCell.emissionLatitude = zeroDegreesInRadians
emitterCell.emissionLongitude = zeroDegreesInRadians
emitterCell.emissionRange = degreesToRadians(360.0)
// 6
emitterCell.lifetime = 1.0
emitterCell.birthRate = 250.0
// 7
emitterCell.xAcceleration = -800
emitterCell.yAcceleration = 1000
逐步进行以下操作:
- 1) 您可以通过将发射器单元的内容设置为图像来设置发射器单元。该图像在
Layer Player
项目中可用。 - 2) 然后,使用
VelocityRange
指定初始速度和最大方差。发射器层使用上述种子创建随机数生成器。随机数生成器通过使用初始值加上和减去范围值来随机化范围内的值。对于以Range
结尾的所有属性,都会发生这种随机化。 - 3) 您将颜色设置为黑色。这样可以使方差与默认的白色不同,从而导致粒子过亮。
- 4) 接下来,使用与
velocityRange
相同的随机化设置一系列颜色范围。这次,随机化指定每种颜色的随机范围。速度值决定了每种颜色在单元的整个生命周期内变化的速度。 - 5) 在这里,您可以指定如何在整个圆锥周围分布
cell
。您可以通过设置发射器单元的旋转速度和发射范围来实现。emissionRange
定义以弧度指定的圆锥。emissionRange
确定如何在锥体周围分布发射器单元。 - 6) 您将
cell
的寿命设置为1秒。该属性的默认值为0
,因此,如果您未显式设置它,则永远不会显示cell
!每秒的birthRate
也是如此。birthRate
的默认值为0
,因此必须将其设置为正数才能显示cell
。 - 7) 最后,设置
cell
x
和y
的加速度。这些值会影响粒子发射的视角。
构建并运行,然后选择CAEmitterLayer
。结果如下:
返回Xcode
,找到prepare(for:sender :)
并取消注释该代码以连接控件表。
现在,再次构建并运行该应用程序,并使用控件来调整所有上述属性以及其他一些属性:
恭喜你! 您已经完成了精彩的CALayer
之旅。 您已经看到了9个有关如何使用CALayer
及其许多子类的示例。
但是不要在这里停下来! 打开一个新项目,或使用现有项目之一,然后看看如何使用图层。 您可能可以实现更好的性能,或者可以添加一些新的动画来让您的用户以及您自己赞叹不已!
后记
本篇主要讲述了
CALayer
的简单实用示例,感兴趣的给个赞或者关注~~~