分解
操作和使用CATransform3D
进行动画和交互相当具有挑战性... 分解使CATransform3D
、matrix_double4x4
和matrix_float4x4
更容易使用。
注意:分解的API仍在不断更改/优化,所以请随时提供反馈,并预料未来可能会有重大更改。
具体来说,我很想弄清楚如何处理在这个仓库中引入的Vector类型。如果有任何问题,请告诉我;我正在积极寻找使它变得更加完善的途径。
简介
通常在iOS上,如果你想转换一个CALayer
,你会这样做
let layer: CAlayer = ...
layer.transform = CATransform3DMakeScale(0.5, 0.5, 1.0)
然而,如果你从一个别的地方得到了一个转换怎么办?你怎么知道层的缩放比例是多少?如果你想要设置转换的缩放或平移怎么办?没有复杂的线性代数,这并不容易做到!
Decomposed旨在通过允许对CATransform3D
、matrix_double4x4
和matrix_float4x4
进行分解、重新组合和变异,从而简化这一点,而不需要复杂的数学。
分解是将某物分解成较小的组件的过程,在这种情况下是将转换矩阵分解成翻译、缩放等,这样它们可以单独更改或重置。以下是所支持的:
- 平移
- 缩放
- 旋转(使用四元数或欧拉角)
- 倾斜
- 透视
它还由Accelerate支持,因此它在矩阵操作中应该引入相对较低的开销。
使用方法
API 文档在这里:here.
转换修改
Swift
创建一个沿Y轴平移44个点,以π/4.0沿X轴旋转的转换
var transform: CATransform3D = CATransform3DIdentity
.translatedBy(y: 44.0)
.rotatedBy(angle: .pi / 4.0, x: 1.0)
Objective-C
创建一个沿Y轴平移44个点,绕X轴旋转π/40的变换。
CATransform3DDecomposed *decomposed = [DEDecomposedCATransform3D decomposedTransformWithTransform:CATransform3DIdentity];
decomposed.translation = CGPoint(0.0, 44.0);
decomposed.rotation = simd_quaternion(M_PI / 4.0, simd_make_double3(1.0, 0.0, 0.0));
transform = [decomposed recompose];
CALayer 扩展
通常在使用 UIView
和 CALayer
进行交互式手势操作时,您在更改变换时需要处理隐式动作(动画)。 Decomposed 可以自动为您处理这个问题,而不需要在代码中封装 CATransactions
并禁用动作。
Swift
// In some UIPanGestureRecognizer handling method
layer.translation = panGestureRecognizer.translation(in: self)
layer.scale = CGPoint(x: 0.75, y: 0.75)
Objective-C
由于Objective-C中存在命名空间冲突,您可以通过`transformProxy`属性进行类似更改。对此代理对象所做的更改将应用于层变换,且启用了隐式动画。
// In some UIPanGestureRecognizer handling method
layer.transformProxy.translation = [panGestureRecognizer translationInView:self];
layer.transformProxy.scale = CGPoint(x: 0.75, y: 0.75);
分解变换
每次在CATransform3D
或matrix_double4x4
上更改属性时,都需要进行分解、更改,然后再重新组合。这如果很频繁进行可能会很昂贵,因此应该限制其使用。如果您一次更改多个属性,最好是先更改分解变换
,然后调用其重新组合
函数以获取重新组合的变换。
Swift
var decomposed = transform.decomposed()
decomposed.translation = Translation(44.0, 44.0, 0.0)
decomposed.scale = Scale(0.75, 0.75, 0.0)
decomposed.rotation = CGQuaternion(angle: .pi / 4.0, axis: CGVector3(1.0, 0.0, 0.0))
let changedTransform = decomposed.recomposed()
Objective-C
DEDecomposedCATransform3D *decomposed = [DEDecomposedCATransform3D decomposedTransformWithTransform:transform];
decomposed.translation = CGPointMake(44.0, 44.0);
decomposed.scale = CGPointMake(0.75, 0.75);
decomposed.rotation = simd_quaternion(M_PI / 4.0, simd_make_double3(1.0, 0.0, 0.0));
CATransform3D changedTransform = [decomposed recomposed];
CGVector3 / CGVector4 / CGQuaternion
很遗憾,simd
不支持存储CGFloat
(即使它们是Double
)。为了让这个库更容易使用(例如,不需要将所有内容都强制转换为双精度浮点数,使用Double(some CGFloat)
),您可以找到CGVector3
、CGVector4
和CGQuaternion
,分别封装了simd
对应的类型:simd_double3
、simd_double4
和simd_quatd
。
《平移》,`缩放`等等都是类型别名(即
CGVector3
或CGVector4
),并且它们都遵循ArrayLiteralRepresentable
协议,因此可以使用`Array
layer.translation = Translation(44.0, 44.0, 0.0)
layer.scale = Scale(0.5, 0.75, 0.0)
注意:当前这种形式的API存在疑问,因为它与Swift的`Vector`类型(只是 simd 类型,我认为所有内容都应公开为`simd`类型)冲突,所以我欢迎您提供反馈!
可插值
此外,它还通过可插值
协议提供了从任何转换到任何转换的线性插值功能。这使得您能够以受控的方式轻松地动画化/过渡到转换之间。
let transform: CATransform3D = CATransform3DIdentity
.translatedBy(x: 44.0, y: 44.0)
let transform2 = CATransform3DIdentity
.translatedBy(x: 120.0, y: 240.0)
.scaled(by: [0.5, 0.75, 1.0])
.rotatedBy(angle: .pi / 4.0, x: 1.0)
let interpolatedTransform = transform.lerp(to: transform2, fraction: 0.5)
安装
要求
- iOS 13+、macOS 10.15+
- Swift 5.0 或更高版本
目前Decomposed支持Swift Package Manager、CocoaPods、Carthage,以及作为Xcode子项目手动使用。欢迎为其他依赖系统/构建系统提交拉取请求!
Swift 包管理器
将以下内容添加到您的 Package.swift
文件中(或者通过 Xcode 的 GUI 添加)
.package(url: "https://github.com/b3ll/Decomposed", from: "0.0.1")
CocoaPods
将以下内容添加到您的 Podfile
pod 'Decomposed'
注释
由于某种原因,当使用CocoaPods和本库的Objective-C部分时,您可能会看到以下错误
声明'DEDecomposedCATransform3D'必须在使用前从模块'Decomposed.Swift'导入
.
要修复此问题,您需要使用Objective-C++文件(即将文件名从.m
更改为.mm
)或更改导入如下
#import <Decomposed/Decomposed.h>
#import <Decomposed/Decomposed-Swift.h>
目前我没有任何其他解决方案,如果将来有,我会进行更新。
Carthage
将以下内容添加到您的 Cartfile
"b3ll/Decomposed"
Xcode 子项目
- 将
Decomposed.xcodeproj
添加到您的项目中 - 将
Decomposed.framework
添加为嵌入式框架(它是一个静态库,因此不应嵌入式)。
Objective-C 笔记
- Objective-C 支持通过 Swift 包管理器不可用。请使用手动 Xcode 子项目。
- 您需要使用
#import <Decomposed/Decomposed.h>
,因为这个文件包含 Objective-C 类的生成的 Swift 接口和 CALayer 分类。 - 由于 Swift / Objective-C 互操作性限制,并非所有 API 都可用。遗憾的是,API 的实现可能不会那么优雅,但是
CATransform3DDecomposed
允许对CATransform3D
进行分解和重新组合(类似的类也存在于matrix_double4x4
和matrix_float4x4
中,并且是它们的 Swift 对应包装器)以及CALayer
上的便利分类。
其他推荐
高级
此库与iOS基于物理动画系统的高级(a physics-based animation system for iOS)配合得非常好。
通过修改层上的变换来在弹簧上动画是一个层,这从未如此简单!通常,您必须手动将事物包装在禁用动作的CATransaction
中,而使用CATransform3DTranslate
变得相当繁琐。
使用分解+高级,这变得超级简单。
let layer = ...
let spring = Spring<CGPoint>(initialValue: .zero)
spring.onChange = { [layer] translation in
layer.translation = translation
}
// In your pan gesture recognizer callback
let translation = panGestureRecognizer.translation(in: self.view)
let velocity = panGestureRecognizer.velocity(in: self.view)
switch panGestureRecognizer.state {
case .began:
spring.reset(to: .zero)
break
case .changed:
layer.translation = translation
break
case .ended:
spring.velocity = velocity
spring.target = CGPoint(x: 200.0, y: 200.0) // wherever you want it to go
break
}
查看DraggingCard示例,了解如何使用此功能 :)
许可证
Decomposed 采用 BSD 2-clause 许可证 许可。
联系方式
欢迎在推特上关注我:[b3ll](https://www.twitter.com/b3ll)!