Skip to content
This repository was archived by the owner on Aug 30, 2023. It is now read-only.

Commit 1ddd7da

Browse files
author
Jeff Verkoeyen
committed
Merge branch 'release-candidate' into stable
2 parents 92ab4a0 + f08a19b commit 1ddd7da

38 files changed

+1455
-498
lines changed

CHANGELOG.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,71 @@
1+
# 4.0.0
2+
3+
This major release adds support for composable transitions. See the catalog app for a variety of
4+
examples making use of this new functionality.
5+
6+
## Fixed issues
7+
8+
- [Transitions would not complete if the presentation controller didn't implement the startWithContext method](https://github.com/material-motion/transitioning-objc/pull/45)
9+
10+
## Breaking changes
11+
12+
- `MDMTransitionWithFallback`'s return value is now nonnull. If you depended on the nil behavior,
13+
you must now conform to the new protocol `MDMTransitionWithFeasibility` and return `NO` for
14+
`canPerformTransitionWithContext:`.
15+
- `MDMTransitionDirection` has been renamed to `TransitionDirection` in Swift.
16+
17+
## New features
18+
19+
`MDMTransitionWithFeasibility` allows a transition to indicate whether it is capable of performing
20+
the transition with a given context.
21+
22+
The new `composeWithTransition:` API on `MDMTransitionContext` makes it possible to build modular
23+
transition objects that delegate responsibility out to other transition objects. View the
24+
`PhotoAlbumTransition` example transition to see the following code in action:
25+
26+
```swift
27+
context.compose(with: FadeTransition(target: .foreView, style: .fadeIn))
28+
context.compose(with: SpringFrameTransition(target: .target(snapshotContextView),
29+
size: fitSize))
30+
31+
if let toolbar = foreDelegate.toolbar(for: self) {
32+
context.compose(with: SlideUpTransition(target: .target(toolbar)))
33+
}
34+
```
35+
36+
## Source changes
37+
38+
* [Add nullability annotations to MDMTransitionNavigationControllerDelegate. (#46)](https://github.com/material-motion/motion-transitioning-objc/commit/302d3c4ec526ffa942d23937fdfe8ef5163d473d) (featherless)
39+
* [Update Xcode build settings to Xcode 9 warnings and resolve build error.](https://github.com/material-motion/transitioning-objc/commit/5ed85cdc795ae6660901c5e2ae237732f04649e1) (Jeff Verkoeyen)
40+
* [Rework multi-transition support using composition. (#43)](https://github.com/material-motion/transitioning-objc/commit/0b57361557476c7d3ecb8f4c9878da21a2e735ab) (featherless)
41+
* [Fix the Swift symbol name for MDMTransitionDirection. (#44)](https://github.com/material-motion/transitioning-objc/commit/4cdcf4ca0324a1f83d572440887fe5a5d18ee00b) (featherless)
42+
* [Fix bug where transitions would not complete if the presentation controller didn't implement the startWithContext method. (#45)](https://github.com/material-motion/transitioning-objc/commit/784328dae8509df0a2beb3a5afa9701f1e275950) (featherless)
43+
* [Fix broken unit tests.](https://github.com/material-motion/transitioning-objc/commit/46c92ebcab642969ba70ea43aa512cac1cc3cad4) (Jeff Verkoeyen)
44+
* [Add multi-transition support. (#40)](https://github.com/material-motion/transitioning-objc/commit/8653958a5a9419891861fb6fd7648791ca3c744c) (featherless)
45+
* [Remove unused protocol forward declaration.](https://github.com/material-motion/transitioning-objc/commit/74c1655fc3614e5e9788db8b53e8bff83691137a) (Jeff Verkoeyen)
46+
47+
## API changes
48+
49+
### MDMTransitionWithCustomDuration
50+
51+
*changed* protocol `MDMTransitionWithCustomDuration` now conforms to `MDMTransition`.
52+
53+
### MDMTransitionWithFallback
54+
55+
*changed* protocol `MDMTransitionWithFallback` now conforms to `MDMTransition`.
56+
57+
### MDMTransitionWithFeasibility
58+
59+
*new* protocol `MDMTransitionWithFeasibility`.
60+
61+
### MDMTransitionContext
62+
63+
*new* method `composeWithTransition:`
64+
65+
## Non-source changes
66+
67+
* [Add platform to the Podfile per pod install recommendation.](https://github.com/material-motion/transitioning-objc/commit/7384187b2ddd6a2760f5279cabb5032ea3b1e24e) (Jeff Verkoeyen)
68+
169
# 3.3.0
270

371
This minor release deprecates some behavior and replaces it with a new API.

MotionTransitioning.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Pod::Spec.new do |s|
22
s.name = "MotionTransitioning"
33
s.summary = "Light-weight API for building UIViewController transitions."
4-
s.version = "3.3.0"
4+
s.version = "4.0.0"
55
s.authors = "The Material Motion Authors"
66
s.license = "Apache 2.0"
77
s.homepage = "https://github.com/material-motion/transitioning-objc"

Podfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
workspace 'MotionTransitioning.xcworkspace'
22
use_frameworks!
3+
platform :ios, '8.0'
34

45
target "TransitionsCatalog" do
56
pod 'CatalogByConvention'

Podfile.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
PODS:
22
- CatalogByConvention (2.1.1)
3-
- MotionTransitioning (3.3.0)
3+
- MotionTransitioning (4.0.0)
44

55
DEPENDENCIES:
66
- CatalogByConvention
77
- MotionTransitioning (from `./`)
88

99
EXTERNAL SOURCES:
1010
MotionTransitioning:
11-
:path: "./"
11+
:path: ./
1212

1313
SPEC CHECKSUMS:
1414
CatalogByConvention: c3a5319de04250a7cd4649127fcfca5fe3322a43
15-
MotionTransitioning: caaa488e0469d93f004793b96a2ed04447af808d
15+
MotionTransitioning: be4161ebcbff7911a1d9c4549e396f486041ca6f
1616

17-
PODFILE CHECKSUM: db2e7ac8d9d65704a2cbffa0b77e39a574cb7248
17+
PODFILE CHECKSUM: 25d5942fb7698339a03667bb46c3fbb77529b92d
1818

19-
COCOAPODS: 1.2.1
19+
COCOAPODS: 1.3.1

README.md

Lines changed: 62 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ you can pick the custom transition you want to use:
1414
```swift
1515
let viewController = MyViewController()
1616
viewController.transitionController.transition = CustomTransition()
17-
present(modalViewController, animated: true)
17+
present(viewController, animated: true)
1818
```
1919

2020
```objc
@@ -102,7 +102,7 @@ commands:
102102
## Guides
103103
104104
1. [Architecture](#architecture)
105-
2. [How to create a simple transition](#how-to-create-a-simple-transition)
105+
2. [How to create a fade transition](#how-to-create-a-fade-transition)
106106
3. [How to customize presentation](#how-to-customize-presentation)
107107
4. [How to customize navigation controller transitions](#how-to-customize-navigation-controller-transitions)
108108
@@ -118,23 +118,27 @@ MotionTransitioning provides a thin layer atop these protocols with the followin
118118
- Every view controller has its own **transition controller**. This encourages choosing the
119119
transition based on the context.
120120
- Transitions are represented in terms of **backward/forward** rather than from/to. When presenting,
121-
we're moving forward. When dismissing, we're moving backward. This makes it easier to refer to
122-
each "side" of a transition consistently.
123-
- Transition objects can customize their behavior by conforming to more `TransitionWith*` protocols.
124-
This protocol-oriented design is more Swift-friendly than a variety of optional methods on a
125-
protocol.
126-
- But most importantly: **this library handles the plumbing, allowing you to focus on the motion**.
121+
we're moving forward. When dismissing, we're moving backward. This allows transition code to be
122+
written with fewer conditional branches of logic.
123+
- Transition objects can customize their behavior by conforming to the family of `TransitionWith*` protocols.
127124
128-
### How to create a simple transition
125+
### How to create a fade transition
129126
130-
In this guide we'll create scaffolding for a simple transition.
127+
We'll create a new fade transition so that the following lines of code customizes the presentation
128+
and dismissal of our view controller:
129+
130+
```swift
131+
let viewController = MyViewController()
132+
viewController.transitionController.transition = FadeTransition()
133+
present(viewController, animated: true)
134+
```
131135

132136
#### Step 1: Define a new Transition type
133137

134-
Transitions must be `NSObject` types that conform to the `Transition` protocol.
138+
A transition is an `NSObject` subclass that conforms to the `Transition` protocol.
135139

136-
The sole method we're expected to implement, `start`, is invoked each time the view controller is
137-
presented or dismissed.
140+
The only method you have to implement is `start(with context:)`. This method is invoked each time
141+
the associated view controller is presented or dismissed.
138142

139143
```swift
140144
final class FadeTransition: NSObject, Transition {
@@ -146,7 +150,11 @@ final class FadeTransition: NSObject, Transition {
146150

147151
#### Step 2: Invoke the completion handler once all animations are complete
148152

149-
If using Core Animation explicitly:
153+
Every transition is provided with a transition context. The transition context must be told when the
154+
transition's motion has completed so that the context can then inform UIKit of the view controller
155+
transition's completion.
156+
157+
If using explicit Core Animation animations:
150158

151159
```swift
152160
final class FadeTransition: NSObject, Transition {
@@ -164,7 +172,7 @@ final class FadeTransition: NSObject, Transition {
164172
}
165173
```
166174

167-
If using UIView implicit animations:
175+
If using implicit UIView animations:
168176

169177
```swift
170178
final class FadeTransition: NSObject, Transition {
@@ -181,41 +189,57 @@ final class FadeTransition: NSObject, Transition {
181189

182190
#### Step 3: Implement the motion
183191

184-
With the basic scaffolding in place, you can now implement your motion.
192+
With the basic scaffolding in place, you can now implement your motion. For simplicity's sake we'll
193+
use implicit UIView animations in this example to build our motion, but you're free to use any
194+
animation system you prefer.
195+
196+
```swift
197+
final class FadeTransition: NSObject, Transition {
198+
func start(with context: TransitionContext) {
199+
// This is a fairly rudimentary way to calculate the values on either side of the transition.
200+
// You may want to try different patterns until you find one that you prefer.
201+
// Also consider trying the MotionAnimator library provided by the Material Motion team:
202+
// https://github.com/material-motion/motion-animator-objc
203+
let backOpacity = 0
204+
let foreOpacity = 1
205+
let initialOpacity = context.direction == .forward ? backOpacity : foreOpacity
206+
let finalOpacity = context.direction == .forward ? foreOpacity : backOpacity
207+
context.foreViewController.view.alpha = initialOpacity
208+
UIView.animate(withDuration: context.duration, animations: {
209+
context.foreViewController.view.alpha = finalOpacity
210+
211+
}, completion: { didComplete in
212+
context.transitionDidEnd()
213+
})
214+
}
215+
}
216+
```
185217

186218
### How to customize presentation
187219

188-
You'll customize the presentation of a transition when you need to do any of the following:
220+
Customize the presentation of a transition when you need to do any of the following:
189221

190222
- Add views, such as dimming views, that live beyond the lifetime of the transition.
191223
- Change the destination frame of the presented view controller.
192224

193-
#### Step 1: Subclass UIPresentationController
225+
You have two options for customizing presentation:
194226

195-
You must subclass UIPresentationController in order to implement your custom behavior. If the user
196-
of your transition can customize any presentation behavior then you'll want to define a custom
197-
initializer.
227+
1. Use the provided `TransitionPresentationController` API.
228+
2. Build your own UIPresentationController subclass.
198229

199-
> Note: Avoid storing the transition context in your presentation controller. Presentation
200-
> controllers live for as long as their associated view controller, while the transition context is
201-
> only valid while a transition is active. Each presentation and dismissal will receive its own
202-
> unique transition context. Storing the context in the presentation controller would keep the
203-
> context alive longer than it's meant to.
230+
#### Option 2: Subclass UIPresentationController
204231

205-
Override any `UIPresentationController` methods you'll need in order to implement your motion.
232+
Start by defining a new presentation controller type:
206233

207234
```swift
208235
final class MyPresentationController: UIPresentationController {
209236
}
210237
```
211238

212-
#### Step 2: Implement TransitionWithPresentation on your transition
213-
214-
This ensures that your transition implement the required methods for presentation.
215-
216-
Presentation will only be customized if you return `.custom` from the
217-
`defaultModalPresentationStyle` method and a non-nil `UIPresentationController` subclass from the
218-
`presentationController` method.
239+
Your Transition type must conform to `TransitionWithPresentation` in order to customize
240+
presentation. Return your custom presentation controller class from the required methods and be sure
241+
to return the `.custom` presentation style, otherwise UIKit will not use your presentation
242+
controller.
219243

220244
```swift
221245
extension VerticalSheetTransition: TransitionWithPresentation {
@@ -231,15 +255,14 @@ extension VerticalSheetTransition: TransitionWithPresentation {
231255
}
232256
```
233257

234-
#### Optional Step 3: Implement Transition on your presentation controller
235-
236258
If your presentation controller needs to animate anything, you can conform to the `Transition`
237259
protocol in order to receive a `start` invocation each time a transition begins. The presentation
238260
controller's `start` will be invoked before the transition's `start`.
239261

240-
> Note: It's possible for your presentation controller and your transition to have different ideas
241-
> of when a transition has completed, so consider which object should be responsible for invoking
242-
> `transitionDidEnd`. The `Transition` object is usually the one that calls this method.
262+
> Note: Just like your transition, your presentation controller must eventually call
263+
> `transitionDidEnd` on its context, otherwise your transition will not complete. This is because
264+
> the transitioning controller waits until all associated transitions have completed before
265+
> informing UIKit of the view controller transition's completion.
243266
244267
```swift
245268
extension MyPresentationController: Transition {

examples/ContextualExample.swift

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ class ContextualExampleViewController: ExampleViewController {
3535
// Note that in this example we're populating the contextual transition with the tapped view.
3636
// Our rudimentary transition will animate the context view to the center of the screen from its
3737
// current location.
38-
controller.transitionController.transition = ContextualTransition(contextView: tapGesture.view!)
38+
controller.transitionController.transition = CompositeTransition(transitions: [
39+
FadeTransition(target: .foreView),
40+
ContextualTransition(contextView: tapGesture.view!)
41+
])
3942

4043
present(controller, animated: true)
4144
}
@@ -97,12 +100,6 @@ private class ContextualTransition: NSObject, Transition {
97100
context.transitionDidEnd()
98101
}
99102

100-
let fadeIn = CABasicAnimation(keyPath: "opacity")
101-
fadeIn.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
102-
fadeIn.fromValue = 0
103-
fadeIn.toValue = 1
104-
addAnimationToLayer(fadeIn, context.foreViewController.view.layer)
105-
106103
// We use a snapshot view to accomplish two things:
107104
// 1) To not affect the context view's state.
108105
// 2) To allow our context view to appear in front of the fore view controller's view.
@@ -128,11 +125,8 @@ private class ContextualTransition: NSObject, Transition {
128125
y: context.foreViewController.view.bounds.midY)
129126
addAnimationToLayer(shift, snapshotContextView.layer)
130127

131-
let fadeOut = CABasicAnimation(keyPath: "opacity")
132-
fadeOut.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
133-
fadeOut.fromValue = 1
134-
fadeOut.toValue = 0
135-
addAnimationToLayer(fadeOut, snapshotContextView.layer)
128+
context.compose(with: FadeTransition(target: .target(snapshotContextView),
129+
style: .fadeOut))
136130

137131
CATransaction.commit()
138132
}

examples/CustomPresentationExample.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,12 @@ final class VerticalSheetTransition: NSObject, Transition {
103103
}
104104
}
105105

106-
extension VerticalSheetTransition: TransitionWithPresentation, TransitionWithFallback {
106+
extension VerticalSheetTransition: TransitionWithPresentation, TransitionWithFeasibility {
107107

108108
// We customize the transition going forward but fall back to UIKit for dismissal. Our
109109
// presentation controller will govern both of these transitions.
110-
func fallbackTransition(with context: TransitionContext) -> Transition? {
111-
return context.direction == .forward ? self : nil
110+
func canPerformTransition(with context: TransitionContext) -> Bool {
111+
return context.direction == .forward
112112
}
113113

114114
// This method is invoked when we assign the transition to the transition controller. The result

examples/FadeExample.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ - (void)didTap {
2727

2828
// The transition controller is an associated object on all UIViewController instances that
2929
// allows you to customize the way the view controller is presented. The primary API on the
30-
// controller that you'll make use of is the `transition` property. Setting this property will
30+
// controller that you'll make use of is the `transitions` property. Setting this property will
3131
// dictate how the view controller is presented. For this example we've built a custom
3232
// FadeTransition, so we'll make use of that now:
3333
viewController.mdm_transitionController.transition = [[FadeTransition alloc] init];

examples/FadeExample.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class FadeExampleViewController: ExampleViewController {
3030
// controller that you'll make use of is the `transition` property. Setting this property will
3131
// dictate how the view controller is presented. For this example we've built a custom
3232
// FadeTransition, so we'll make use of that now:
33-
modalViewController.transitionController.transition = FadeTransition()
33+
modalViewController.transitionController.transition = FadeTransition(target: .foreView)
3434

3535
// Note that once we assign the transition object to the view controller, the transition will
3636
// govern all subsequent presentations and dismissals of that view controller instance. If we

examples/NavControllerFadeExample.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class NavControllerFadeExampleViewController: ExampleViewController {
3131
// controller that you'll make use of is the `transition` property. Setting this property will
3232
// dictate how the view controller is presented. For this example we've built a custom
3333
// FadeTransition, so we'll make use of that now:
34-
modalViewController.transitionController.transition = FadeTransition()
34+
modalViewController.transitionController.transition = FadeTransition(target: .foreView)
3535

3636
cachedNavDelegate = navigationController?.delegate
3737

0 commit comments

Comments
 (0)