MaterialMotionRuntime 6.0.1

MaterialMotionRuntime 6.0.1

测试已测试
Lang语言 Obj-CObjective C
许可证 Apache 2
发布最后发布2017年2月

Jeff Verkoeyen 维护。



  • Material Motion 作者。

适用于 Apple 设备的 Material Motion Runtime

Material Motion Runtime 是一个用于声明式描述运动的工具。

声明式运动:运动作为数据

这个库本身没有做太多。然而,它所做的事情是使运动作为离散的数据单元被表达,这些数据可以进行检查、组合,并通过网络发送。

这个库鼓励您以数据的形式描述运动,我们称之为 计划。计划被提交到一个 运行时。运行时负责协调 表演者(performers)的创建,它们是负责将计划转换为具体执行的对象。

安装

使用

导入 Material Motion Runtime 框架

@import MaterialMotionRuntime;

您现在将能够访问所有 API。

示例应用/单元测试

运行以下命令,检查本地的仓库副本以访问 Catalog 应用程序

git clone https://github.com/material-motion/material-motion-runtime-objc.git
cd material-motion-runtime-objc
pod install
open MaterialMotionRuntime.xcworkspace

指南

  1. 架构
  2. 如何定义新的计划和执行者类型
  3. 如何将计划提交给运行时
  4. 如何将命名的计划提交给运行时
  5. 如何在 Swift 中处理多个计划类型
  6. 如何使用命名计划配置执行者
  7. 如何使用组合满足计划
  8. 如何表示连续的性能
  9. 如何跟踪内部运行时事件
  10. 如何将运行时事件记录到控制台
  11. 如何观察时间轴事件

架构

Material Motion Runtime 由两组 API 组成:一组是运行时/事务对象,另一组是由计划和执行者类型松散组成的协议集合。

运行时

Runtime 对象是一个协调实体,其主要责任是通过创建执行者来履行计划。您可以在应用程序生命周期中的任何时候创建多个运行时。一个好规则是每个交互或过渡都使用一个运行时。

计划 + 执行类型

计划执行协议分别为Material Motion Runtime定义了被视为计划或执行者所需的最小特性。

计划和执行者之间存在着共生关系。计划由它定义的执行者执行。执行者行为由提供的计划实例配置。

通过阅读Starmap了解Material Motion Runtime的更多信息。

如何创建新的计划和学生类型

以下步骤提供了可复制粘贴的代码片段。

步骤 1:定义计划类型

创建新的计划类型时需要问自己的问题

  • 我想我的计划/执行者完成什么?
  • 我的执行者需要许多计划来实现预期的结果吗?
  • 我该如何命名我的计划,使其明确传达出行为或状态变化?

一般规则如下

  1. -able后缀结尾的计划会改变目标的行为,通常是不确定的。例如:拖动、缩放、抛掷。
  2. 动词形式的计划描述了状态的变化,通常在一段时间内完成。例如:淡入、过渡、弹簧跳转。

代码片段

在Objective-C中

@interface <#Plan#> : NSObject
@end

@implementation <#Plan#>
@end

在Swift中

class <#Plan#>: NSObject {
}

步骤 2:定义执行者类型

执行者负责执行计划。实现方式有多种

有关每种执行类型的详细信息,请参阅相关链接。

注意:每个目标类型仅创建一个执行者实例。这允许您将多个计划注册到同一目标,以便配置执行者。有关更多信息,请参阅如何使用计划配置执行者

代码片段

在Objective-C中

@interface <#Performer#> : NSObject <MDMPerforming>
@end

@implementation <#Performer#> {
  UIView *_target;
}

- (instancetype)initWithTarget:(id)target {
  self = [super init];
  if (self) {
    assert([target isKindOfClass:[UIView class]]);
    _target = target;
  }
  return self;
}

- (void)addPlan:(id<MDMPlan>)plan {
  <#Plan#>* <#casted plan instance#> = plan;

  // Do something with the plan.
}

@end

在Swift中

class <#Performer#>: NSObject, Performing {
  let target: UIView
  required init(target: Any) {
    self.target = target as! UIView
    super.init()
  }

  func addPlan(_ plan: Plan) {
    let <#casted plan instance#> = plan as! <#Plan#>

    // Do something with the plan.
  }
}

步骤 3:将计划类型定义为正式计划

遵守计划要求

  1. 定义你的计划所需的执行者类型,并且
  2. 你的计划应该是可复制的。

代码片段

在Objective-C中

@interface <#Plan#> : NSObject <MDMPlan>
@end

@implementation <#Plan#>

- (Class)performerClass {
  return [<#Plan#> class];
}

- (id)copyWithZone:(NSZone *)zone {
  return [[[self class] allocWithZone:zone] init];
}

@end

在Swift中

class <#Plan#>: NSObject, Plan {
  func performerClass() -> AnyClass {
    return <#Performer#>.self
  }
  func copy(with zone: NSZone? = nil) -> Any {
    return <#Plan#>()
  }
}

如何将计划提交给运行时

步骤 1: 创建并存储运行时实例的引用

代码片段

在Objective-C中

@interface MyClass ()
@property(nonatomic, strong) MDMRuntime* runtime;
@end

- (instancetype)init... {
  ...
  self.runtime = [MDMRuntime new];
  ...
}

在Swift中

class MyClass {
  let runtime = Runtime()
}

步骤 2: 将计划与目标关联

代码片段

在Objective-C中

[runtime addPlan:<#Plan instance#> to:<#View instance#>];

在Swift中

runtime.addPlan(<#Plan instance#>, to:<#View instance#>)

如何将命名计划提交给运行时

步骤 1: 创建并存储运行时实例的引用

代码片段

在Objective-C中

@interface MyClass ()
@property(nonatomic, strong) MDMRuntime* runtime;
@end

- (instancetype)init... {
  ...
  self.runtime = [MDMRuntime new];
  ...
}

在Swift中

class MyClass {
  let runtime = Runtime()
}

步骤 2: 将命名计划与目标关联

代码片段

在Objective-C中

[runtime addPlan:<#Plan instance#> named:<#name#> to:<#View instance#>];

在Swift中

runtime.addPlan(<#Plan instance#>, named:<#name#>, to:<#View instance#>)

如何在Swift中处理多种计划类型

利用Swift的类型化switch/case语句处理多种计划类型。

func addPlan(_ plan: Plan) {
  switch plan {
  case let <#plan instance 1#> as <#Plan type 1#>:
    ()

  case let <#plan instance 2#> as <#Plan type 2#>:
    ()

  case is <#Plan type 3#>:
    ()

  default:
    assert(false)
  }
}

如何使用命名计划配置执行者

代码片段

在Objective-C中

@interface <#Performer#> (NamedPlanPerforming) <MDMNamedPlanPerforming>
@end

@implementation <#Performer#> (NamedPlanPerforming)

- (void)addPlan:(id<MDMNamedPlan>)plan named:(NSString *)name {
  <#Plan#>* <#casted plan instance#> = plan;

  // Do something with the plan.
}

- (void)removePlanNamed:(NSString *)name {
  // Remove any configuration associated with the given name.
}

@end

在Swift中

extension <#Performer#>: NamedPlanPerforming {
  func addPlan(_ plan: NamedPlan, named name: String) {
    let <#casted plan instance#> = plan as! <#Plan#>

    // Do something with the plan.
  }

  func removePlan(named name: String) {
    // Remove any configuration associated with the given name.
  }
}

如何使用组合来履行计划

组合执行者能够通过计划发射器发射新的计划。这一功能使得计划的复用和高级抽象的创建成为可能。

步骤 1: 遵守ComposablePerforming协议并存储计划发射器

代码片段

在Objective-C中

@interface <#Performer#> ()
@property(nonatomic, strong) id<MDMPlanEmitting> planEmitter;
@end

@interface <#Performer#> (Composition) <MDMComposablePerforming>
@end

@implementation <#Performer#> (Composition)

- (void)setPlanEmitter:(id<MDMPlanEmitting>)planEmitter {
  self.planEmitter = planEmitter;
}

@end

在Swift中

// Store the emitter in your class' definition.
class <#Performer#>: ... {
  ...
  var emitter: PlanEmitting!
  ...
}

extension <#Performer#>: ComposablePerforming {
  var emitter: PlanEmitting!
  func setPlanEmitter(_ planEmitter: PlanEmitting) {
    emitter = planEmitter
  }
}

步骤 2: 发射计划

执行者只能为其关联的目标发射计划。

代码片段

在Objective-C中

[self.planEmitter emitPlan:<#(nonnull id<MDMPlan>)#>];

在Swift中

emitter.emitPlan<#T##Plan#>)

如何指示持续的性能

执行者可能会在一段时间内或在交互活动期间执行动作。这类执行者被称为持续执行者。

持续执行者能够通过生成is-active令牌来影响运行时的活动状态。只要存在is-active令牌且未被终止,运行时就被认为处于活动状态。当与对应工作完成的令牌预期终止时,持续执行者应当终止令牌。

例如,注册平台动画的执行者可能在动画开始时生成令牌。当动画完成后,令牌会被终止。

步骤 1: 遵守ContinuousPerforming协议并存储令牌生成器

代码片段

在Objective-C中

@interface <#Performer#> ()
@property(nonatomic, strong) id<MDMIsActiveTokenGenerating> tokenGenerator;
@end

@interface <#Performer#> (Composition) <MDMComposablePerforming>
@end

@implementation <#Performer#> (Composition)

- (void)setIsActiveTokenGenerator:(id<MDMIsActiveTokenGenerating>)isActiveTokenGenerator {
  self.tokenGenerator = isActiveTokenGenerator;
}

@end

在Swift中

// Store the emitter in your class' definition.
class <#Performer#>: ... {
  ...
  var tokenGenerator: IsActiveTokenGenerating!
  ...
}

extension <#Performer#>: ContinuousPerforming {
  func set(isActiveTokenGenerator: IsActiveTokenGenerating) {
    tokenGenerator = isActiveTokenGenerator
  }
}

步骤 2: 在某种持续工作开始时生成令牌

你可能会需要存储令牌,以便稍后引用。

代码片段

在Objective-C中

id<MDMIsActiveTokenable> token = [self.tokenGenerator generate];
tokenMap[animation] = token;

在Swift中

let token = tokenGenerator.generate()!
tokenMap[animation] = token

步骤 3:工作完成时终止令牌

代码片段

在Objective-C中

[token terminate];

在Swift中

token.terminate()

如何跟踪内部运行时事件

跟踪允许您观察运行时内部发生的事件。这些信息可用于以下目的:

  • 调试日志。
  • 检查工具。

不支持其他目的的使用。

步骤 1:创建跟踪器类

代码片段

在Objective-C中

@interface <#Custom tracer#> : NSObject <MDMTracing>
@end

@implementation <#Custom tracer#>
@end

在Swift中

class <#Custom tracer#>: NSObject, Tracing {
}

步骤 2:实现方法

跟踪协议的文档列举了可用的方法。

代码片段

在Objective-C中

@implementation <#Custom tracer#>

- (void)didAddPlan:(id<MDMPlan>)plan to:(id)target {

}

@end

在Swift中

class <#Custom tracer#>: NSObject, Tracing {
  func didAddPlan(_ plan: Plan, to target: Any) {

  }
}

如何将运行时事件记录到控制台

代码片段

在Objective-C中

[runtime addTracer:[MDMConsoleLoggingTracer new]];

在Swift中

runtime.addTracer(ConsoleLoggingTracer())

如何观察时间线事件

步骤 1:符合时间线观察协议

代码片段

在Objective-C中

@interface <#SomeClass#> () <MDMTimelineObserving>
@end

@implementation <#SomeClass#>

- (void)timeline:(MDMTimeline *)timeline didAttachScrubber:(MDMTimelineScrubber *)scrubber {

}

- (void)timeline:(MDMTimeline *)timeline didDetachScrubber:(MDMTimelineScrubber *)scrubber {

}

- (void)timeline:(MDMTimeline *)timeline scrubberDidScrub:(NSTimeInterval)timeOffset {

}

@end

在Swift中

extension <#SomeClass#>: TimelineObserving {
  func timeline(_ timeline: Timeline, didAttach scrubber: TimelineScrubber) {
  }

  func timeline(_ timeline: Timeline, didDetach scrubber: TimelineScrubber) {
  }

  func timeline(_ timeline: Timeline, scrubberDidScrub timeOffset: TimeInterval) {
  }
}

步骤 2:将您的观察员添加到时间线

代码片段

在Objective-C中

[timeline addTimelineObserver:<#(nonnull id<MDMTimelineObserving>)#>];

在Swift中

timeline.addObserver(<#T##observer: TimelineObserving##TimelineObserving#>)

参与贡献

我们欢迎贡献!

查看我们的即将到来的里程碑

了解有关我们的团队我们的社区贡献者必备的更多信息。

许可

根据Apache 2.0许可证授权。请参阅LICENSE获取详细信息。