使用 CATransform3D
进行动画和交互非常具有挑战性… Decomposed 使 CATransform3D
、matrix_double4x4
和 matrix_float4x4
更易于使用。
注意:Decomposed 的 API 仍在大量更改/优化中,请随时提供反馈,并预计随着时间的推移会出现重大更改。
具体来说,我真的很想弄清楚如何处理此存储库中引入的 Vector 类型。 如果它们有任何问题,请告诉我;我正在积极寻找改进方法。
通常在 iOS 上,如果你想转换一个 CALayer
,你会这样做:
let layer: CAlayer = ...
layer.transform = CATransform3DMakeScale(0.5, 0.5, 1.0)
但是,如果从其他地方给你一个转换,该怎么办? 你怎么知道图层的缩放比例是多少? 如果你想设置变换的缩放比例或平移,该怎么办? 如果没有大量的复杂线性代数,就不容易做到!
Decomposed 旨在通过允许分解、重组和修改 CATransform3D
、matrix_double4x4
和 matrix_float4x4
,而无需复杂的数学运算来简化此过程。
分解是将某事物分解为更小的组成部分的行为,在这种情况下,将变换矩阵分解为平移、缩放等,以便可以单独更改或重置它们。 支持以下内容:
它也由 Accelerate 提供支持,因此它应该为矩阵操作带来相对较低的开销。
API 文档 在此。
创建一个在 Y 轴上平移 44pts,在 X 轴上旋转 .pi / 4.0 的变换
var transform: CATransform3D = CATransform3DIdentity
.translatedBy(y: 44.0)
.rotatedBy(angle: .pi / 4.0, x: 1.0)
创建一个在 Y 轴上平移 44pts,在 X 轴上旋转 .pi / 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];
通常,在使用 UIView
和 CALayer
进行交互式手势时,更改变换时最终会处理隐式操作(动画)。 Decomposed 会自动为你执行此操作,而不是将你的代码包装在 CATransactions
中并禁用操作。
// In some UIPanGestureRecognizer handling method
layer.translation = panGestureRecognizer.translation(in: self)
layer.scale = CGPoint(x: 0.75, y: 0.75)
由于 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
上的属性时,都需要将其分解、更改,然后重新组合。 如果多次执行此操作,这可能会很昂贵,因此应加以限制。 如果你同时进行多个更改,最好更改 DecomposedTransform
,然后调用其 recomposed()
函数以获得重新组合的变换。
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()
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];
遗憾的是,simd
不支持存储 CGFloat
(即使它们是 Double
)。 为了使这个库更容易使用(即,无需一直将所有内容强制转换为 double Double(some CGFloat)
),你会发现 CGVector3
、CGVector4
和 CGQuaternion
,它们包装了 simd
对应项:simd_double3
、simd_double4
和 simd_quatd
。
Translation
、Scale
等都是类型别名(即 CGVector3
或 CGVector4
),并且它们都符合 ArrayLiteralRepresentable
,因此你可以使用 Array<CGFloat>
来初始化它们。
layer.translation = Translation(44.0, 44.0, 0.0)
layer.scale = Scale(0.5, 0.75, 0.0)
注意:此 API 以其当前形式存在问题,因为它与 Swift 的 Vector
类型冲突(它们只是 simd 类型,并且我认为所有内容都应该作为 simd
类型公开),所以我很乐意接受反馈!
它还提供通过 Interpolatable
协议从任何变换线性插值到任何变换的功能。 这使你可以轻松地以受控方式在变换之间进行动画/过渡。
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)
目前,Decomposed 支持 Swift Package Manager、CocoaPods、Carthage,用作 xcframework,并手动用作 Xcode 子项目。 欢迎使用其他依赖系统/构建系统的 Pull 请求!
将以下内容添加到你的 Package.swift
(或通过 Xcode 的 GUI 添加)
.package(url: "https://github.com/b3ll/Decomposed", from: "0.0.1")
将以下内容添加到你的 Podfile
pod 'Decomposed'
每个标记的发行版都有一个内置的 xcframework。
由于某些原因,当使用 CocoaPods 和此库的 Objective-C 部分时,你可能会看到类似以下的错误:
必须从模块“Decomposed.Swift”导入“DEDecomposedCATransform3D”的声明,然后才能使用它
.
要解决此问题,你需要使用 Objective-C++ 文件(即从 .m
重命名为 .mm
)或将你的导入更改为
#import <Decomposed/Decomposed.h>
#import <Decomposed/Decomposed-Swift.h>
目前我没有任何其他解决方法,但如果我有,我会进行更新。
将以下内容添加到你的 Cartfile
"b3ll/Decomposed"
Decomposed.xcodeproj
添加到你的项目中Decomposed.framework
添加为嵌入式框架(它是一个静态库,因此不应嵌入)。#import <Decomposed/Decomposed.h>
,因为它包含 Objective-C 类和 CALayer 类别的生成的 Swift 接口。CATransform3DDecomposed
将允许分解和重组 CATransform3D
(matrix_double4x4
和 matrix_float4x4
存在类似的类,并且是其 Swift 对应项的包装器)以及 CALayer
上的便捷类别。这个库与 Motion 非常匹配,Motion 是一个用于 iOS、macOS 和 tvOS 上手势驱动的用户界面、动画和交互的动画引擎。
通过修改图层的变换在弹簧上动画图层从未如此简单! 通常,你需要手动将事物包装在禁用操作的 CATransaction
中,并且 CATransform3DTranslate
的使用变得非常繁琐。
使用 Decomposed + Motion 这非常容易。
let layer = ...
let springAnimation = SpringAnimation<CGPoint>(initialValue: .zero)
springAnimation.onValueChanged(disableActions: true) { [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:
springAnimation.stop()
springAnimation.updateValue(to: .zero)
case .changed:
springAnimation.updateValue(to: translation)
layer.translation = translation
case .ended:
springAnimation.velocity = velocity
springAnimation.toValue = CGPoint(x: 200.0, y: 200.0) // wherever you want it to go
springAnimation.start()
default:
break
}
请参阅 DraggingCard 演示,以获得一个很好的例子 :)
Decomposed 在 BSD 2-clause license 下获得许可。
请随时在 Twitter 上关注我:@b3ll!