GTMotionTransitioning
用于创建 UIViewController 过渡的轻量级 API。
该库将 iOS 上创建过渡的方式标准化,您可以使用一行代码轻松选择您想要使用的自定义过渡。
let viewController = MyViewController()
viewController.gtm_transitionController.transition = CustomTransition()
present(viewController, animated: true)
MyViewController *viewController = [[MyViewController alloc] init];
viewController.gtm_transitionController.transition = [[CustomTransition alloc] init];
[self presentViewController:viewController animated:true completion:nil];
使用此库创建过渡的最简单方法是为符合 Transition
协议的类创建一个类。
final class CustomTransition: NSObject, Transition {
func start(with context: TransitionContext) {
CATransaction.begin()
CATransaction.setCompletionBlock {
context.transitionDidEnd()
}
// Add animations...
CATransaction.commit()
}
}
@interface CustomTransition: NSObject <GTMTransition>
@end
@implementation CustomTransition
- (void)startWithContext:(id<GTMTransitionContext>)context {
[CATransaction begin];
[CATransaction setCompletionBlock:^{
[context transitionDidEnd];
}];
// Add animations...
[CATransaction commit];
}
@end
安装
使用 CocoaPods 安装
CocoaPods 是 Objective-C 和 Swift 库的依赖管理器。CocoaPods 自动化在项目中使用第三方库的过程。有关更多信息,请参阅入门指南。您可以使用以下命令安装它:
gem install cocoapods
将 GTMotionTransitioning
添加到您的 Podfile
中
pod 'GTMotionTransitioning'
然后运行以下命令
pod install
用法
导入框架
@import GTMotionTransitioning;
您现在将可以访问所有 API。
示例 apps/单元测试
检查本地仓库的副本以通过运行以下命令访问“目录”应用程序
git clone https://github.com/material-motion/transitioning-objc.git
cd transitioning-objc
pod install
open GTMotionTransitioning.xcworkspace
指南
架构
背景:在 iOS 中,通过在视图控制器上设置“transitioningDelegate”来自定义转换。当一个视图控制器被展示时,UIKit 会请求转换委托的一个动画、交互和展示控制器。然后预计这些控制器将实现转换的运动。
MotionTransitioning通过以下优点提供在这些协议之上的一个薄层
- 每个视图控制器都有自己的转换控制器。这鼓励根据上下文选择转换。
- 转换是以前向/后向来表示的,而不是从/到。当我们展示时,我们正在向前移动。当我们取消展示时,我们正在向后移动。这使得转换代码的逻辑分支更少。
- 转换对象可以通过符合一系列
TransitionWith*
协议来自定义其行为。
如何创建淡入淡出转换
我们将创建一个新的淡入淡出转换,以便以下代码行可以自定义我们的视图控制器的展示和取消展示
let viewController = MyViewController()
viewController.gtm_transitionController.transition = FadeTransition()
present(viewController, animated: true)
步骤 1: 定义一个新的转换类型
转换是一个符合 Transition
协议的 NSObject
子类。
您需要实现的唯一方法是 start(with context:)
。每次关联的视图控制器呈现或消失时,都会调用此方法。
final class FadeTransition: NSObject, Transition {
func start(with context: TransitionContext) {
}
}
步骤 2:当所有动画完成后调用完成处理程序
每个转换都提供了一个转换上下文。转换上下文必须在转换的运动完成后告知,这样上下文就可以通知UIKit视图控制器转换的完成。
如果使用显式Core Animation动画
final class FadeTransition: NSObject, Transition {
func start(with context: TransitionContext) {
CATransaction.begin()
CATransaction.setCompletionBlock {
context.transitionDidEnd()
}
// Your motion...
CATransaction.commit()
}
}
如果使用隐式UIView动画
final class FadeTransition: NSObject, Transition {
func start(with context: TransitionContext) {
UIView.animate(withDuration: context.duration, animations: {
// Your motion...
}, completion: { didComplete in
context.transitionDidEnd()
})
}
}
步骤 3:实现运动
基本框架搭建好后,现在可以实现您的运动。为了简化,我们将在本例中使用隐式UIView动画来构建运动,但您可以使用您喜欢的任何动画系统。
final class FadeTransition: NSObject, Transition {
func start(with context: TransitionContext) {
// This is a fairly rudimentary way to calculate the values on either side of the transition.
// You may want to try different patterns until you find one that you prefer.
// Also consider trying the MotionAnimator library provided by the Material Motion team:
// https://github.com/material-motion/motion-animator-objc
let backOpacity = 0
let foreOpacity = 1
let initialOpacity = context.direction == .forward ? backOpacity : foreOpacity
let finalOpacity = context.direction == .forward ? foreOpacity : backOpacity
context.foreViewController.view.alpha = initialOpacity
UIView.animate(withDuration: context.duration, animations: {
context.foreViewController.view.alpha = finalOpacity
}, completion: { didComplete in
context.transitionDidEnd()
})
}
}
如何定制展示
当需要执行以下操作时,您可以定制转换的展示:
- 添加视图,例如渐暗视图,该视图的生命周期超出了转换。
- 更改呈现视图控制器的目标框架。
您有两种定制展示的方式
- 使用提供的
TransitionPresentationController
API。 - 构建您自己的
UIPresentationController
子类。
选项 2:子类化 UIPresentationController
首先定义一个新的展示控制器类型
final class MyPresentationController: UIPresentationController {
}
您的转换类型必须符合 TransitionWithPresentation
以便定制展示。从必需的方法中返回您的自定义展示控制器类,并确保返回 .custom
展示样式,否则UIKit不会使用您的展示控制器。
extension VerticalSheetTransition: TransitionWithPresentation {
func defaultModalPresentationStyle() -> UIModalPresentationStyle {
return .custom
}
func presentationController(forPresented presented: UIViewController,
presenting: UIViewController,
source: UIViewController?) -> UIPresentationController? {
return MyPresentationController(presentedViewController: presented, presenting: presenting)
}
}
如果您需要将您的展示控制器中的任何内容进行动画,您可以通过遵守Transition
协议来接收每次过渡开始时的一个start
调用。展示控制器的start
将在过渡的start
之前调用。
注意:就像您的过渡一样,您的展示控制器最终必须在它的上下文中调用
transitionDidEnd
,否则您的过渡将不会完成。这是因为过渡控制器会等待所有相关过渡完成后,再通知UIKit关于视图控制器过渡完成的信息。
extension MyPresentationController: Transition {
func start(with context: TransitionContext) {
// Your motion...
}
}
如何定制导航控制器过渡
UINavigationController
会忽略压入或从堆栈中弹出任何视图控制器时的transitioningDelegate
属性,而是依赖于它的代理实例来自定义任何过渡。这意味着我们的transitionController
将由导航控制器忽略。
要使用transitionController
自定义单独的推入/弹出过渡,您可以使用TransitionNavigationControllerDelegate
单例类。如果您将共享代理分配给导航控制器的前端代理,则导航控制器将尊重您为单独视图控制器的transitionController
定义的动画和交互设置。
navigationController.delegate = TransitionNavigationControllerDelegate.sharedDelegate()
// Subsequent pushes and pops will honor the pushed/popped view controller's
// transitionController settings as though the view controllers were being
// presented/dismissed.
作者
liuxc123, [email protected]
许可
GTMotionTransitioning可在MIT许可下使用。有关更多信息,请参阅LICENSE文件。