已分解

Tests Docs

使用 CATransform3D 进行动画和交互非常具有挑战性… Decomposed 使 CATransform3Dmatrix_double4x4matrix_float4x4 更易于使用。

注意:Decomposed 的 API 仍在大量更改/优化中,请随时提供反馈,并预计随着时间的推移会出现重大更改。

具体来说,我真的很想弄清楚如何处理此存储库中引入的 Vector 类型。 如果它们有任何问题,请告诉我;我正在积极寻找改进方法。

简介

通常在 iOS 上,如果你想转换一个 CALayer,你会这样做:

let layer: CAlayer = ...
layer.transform = CATransform3DMakeScale(0.5, 0.5, 1.0)

但是,如果从其他地方给你一个转换,该怎么办? 你怎么知道图层的缩放比例是多少? 如果你想设置变换的缩放比例或平移,该怎么办? 如果没有大量的复杂线性代数,就不容易做到!

Decomposed 旨在通过允许分解、重组和修改 CATransform3Dmatrix_double4x4matrix_float4x4,而无需复杂的数学运算来简化此过程。

分解是将某事物分解为更小的组成部分的行为,在这种情况下,将变换矩阵分解为平移、缩放等,以便可以单独更改或重置它们。 支持以下内容:

它也由 Accelerate 提供支持,因此它应该为矩阵操作带来相对较低的开销。

用法

API 文档 在此

变换修改

Swift

创建一个在 Y 轴上平移 44pts,在 X 轴上旋转 .pi / 4.0 的变换

var transform: CATransform3D = CATransform3DIdentity
  .translatedBy(y: 44.0)
  .rotatedBy(angle: .pi / 4.0, x: 1.0)

Objective-C

创建一个在 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];

CALayer 扩展

通常,在使用 UIViewCALayer 进行交互式手势时,更改变换时最终会处理隐式操作(动画)。 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);

DecomposedTransform

每当你更改 CATransform3Dmatrix_double4x4 上的属性时,都需要将其分解、更改,然后重新组合。 如果多次执行此操作,这可能会很昂贵,因此应加以限制。 如果你同时进行多个更改,最好更改 DecomposedTransform,然后调用其 recomposed() 函数以获得重新组合的变换。

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 Double(some CGFloat)),你会发现 CGVector3CGVector4CGQuaternion,它们包装了 simd 对应项:simd_double3simd_double4simd_quatd

TranslationScale 等都是类型别名(即 CGVector3CGVector4),并且它们都符合 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 请求!

Swift Package Manager

将以下内容添加到你的 Package.swift(或通过 Xcode 的 GUI 添加)

.package(url: "https://github.com/b3ll/Decomposed", from: "0.0.1")

CocoaPods

将以下内容添加到你的 Podfile

pod 'Decomposed'

xcframework

每个标记的发行版都有一个内置的 xcframework。

注意事项

由于某些原因,当使用 CocoaPods 和此库的 Objective-C 部分时,你可能会看到类似以下的错误:

必须从模块“Decomposed.Swift”导入“DEDecomposedCATransform3D”的声明,然后才能使用它.

要解决此问题,你需要使用 Objective-C++ 文件(即从 .m 重命名为 .mm)或将你的导入更改为

#import <Decomposed/Decomposed.h>
#import <Decomposed/Decomposed-Swift.h>

目前我没有任何其他解决方法,但如果我有,我会进行更新。

Carthage

将以下内容添加到你的 Cartfile

"b3ll/Decomposed"

Xcode 子项目

Objective-C 注意事项

其他建议

Motion

这个库与 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