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

Commit e139cc2

Browse files
authored
Add support for fallback transitioning. (#16)
Fallback transitioning allows a transition to choose not to drive a transition by swapping itself out with another transition instance or a UIKit native transition. This is commonly used when a transition expects some sort of context to be available, e.g. a context view, and that context is not immediately available. In this case the transition can fall back to a simpler transition or to use the default UIKit transition. Closes #13
1 parent cf1e796 commit e139cc2

6 files changed

+78
-2
lines changed

examples/CustomPresentationExample.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,13 @@ final class VerticalSheetTransition: NSObject, Transition {
101101
}
102102
}
103103

104-
extension VerticalSheetTransition: TransitionWithPresentation {
104+
extension VerticalSheetTransition: TransitionWithPresentation, TransitionWithFallback {
105+
106+
// We customize the transition going forward but fall back to UIKit for dismissal. Our
107+
// presentation controller will govern both of these transitions.
108+
func fallbackTransition(with context: TransitionContext) -> Transition? {
109+
return context.direction == .forward ? self : nil
110+
}
105111

106112
// This method is invoked when we assign the transition to the transition controller. The result
107113
// is assigned to the view controller's modalPresentationStyle property.
@@ -174,9 +180,21 @@ final class DimmingPresentationController: UIPresentationController {
174180
}
175181
}
176182

183+
override func dismissalTransitionWillBegin() {
184+
// We fall back to an alongside fade out when there is no active transition instance because
185+
// our start implementation won't be invoked in this case.
186+
if presentedViewController.transitionController.activeTransition == nil {
187+
presentedViewController.transitionCoordinator?.animate(alongsideTransition: { context in
188+
self.dimmingView.alpha = 0
189+
})
190+
}
191+
}
192+
177193
override func dismissalTransitionDidEnd(_ completed: Bool) {
178194
if completed {
179195
dimmingView.removeFromSuperview()
196+
} else {
197+
dimmingView.alpha = 1
180198
}
181199
}
182200

src/MDMTransition.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,29 @@ NS_SWIFT_NAME(TransitionWithCustomDuration)
4747
- (NSTimeInterval)transitionDurationWithContext:(nonnull id<MDMTransitionContext>)context;
4848
@end
4949

50+
/**
51+
A transition can return an alternative fallback transition instance.
52+
*/
53+
NS_SWIFT_NAME(TransitionWithFallback)
54+
@protocol MDMTransitionWithFallback
55+
56+
/**
57+
Asks the receiver to return a transition instance that should be used to drive this transition.
58+
59+
If nil is returned, then the system transition will be used.
60+
If self is returned, then the receiver will be used.
61+
If a new instance is returned and the returned instance also conforms to this protocol, the
62+
returned instance will be queried for a fallback.
63+
64+
Will be queried twice. The first time this method is invoked it's possible to return nil. Doing so
65+
will result in UIKit taking over the transition and a system transition being used. The second time
66+
this method is invoked, the custom transition will already be underway from UIKit's point of view
67+
and a nil return value will be treated equivalent to returning self.
68+
*/
69+
- (nullable id<MDMTransition>)fallbackTransitionWithContext:(nonnull id<MDMTransitionContext>)context;
70+
71+
@end
72+
5073
/**
5174
A transition with presentation is able to customize the overall presentation of the transition,
5275
including adding temporary views and changing the destination frame of the presented view

src/MDMTransitionController.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,11 @@ NS_SWIFT_NAME(TransitionController)
3838
*/
3939
@property(nonatomic, strong, nullable) id<MDMTransition> transition;
4040

41+
/**
42+
The active transition instance.
43+
44+
This may be non-nil while a transition is active.
45+
*/
46+
@property(nonatomic, strong, nullable, readonly) id<MDMTransition> activeTransition;
47+
4148
@end

src/private/MDMPresentationTransitionController.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ - (void)setTransition:(id<MDMTransition>)transition {
5656
}
5757
}
5858

59+
- (id<MDMTransition>)activeTransition {
60+
return _context.transition;
61+
}
62+
5963
#pragma mark - UIViewControllerTransitioningDelegate
6064

6165
// Animated transitions

src/private/MDMViewControllerTransitionContext.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333

3434
- (nonnull instancetype)init NS_UNAVAILABLE;
3535

36+
@property(nonatomic, strong, nullable) id<MDMTransition> transition;
37+
3638
@property(nonatomic, weak, nullable) id<MDMViewControllerTransitionContextDelegate> delegate;
3739

3840
@end

src/private/MDMViewControllerTransitionContext.m

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#import "MDMTransition.h"
2020

2121
@implementation MDMViewControllerTransitionContext {
22-
id<MDMTransition> _transition;
2322
id<UIViewControllerContextTransitioning> _transitionContext;
2423
UIPresentationController *_presentationController;
2524
}
@@ -43,6 +42,11 @@ - (nonnull instancetype)initWithTransition:(nonnull id<MDMTransition>)transition
4342
_backViewController = backViewController;
4443
_foreViewController = foreViewController;
4544
_presentationController = presentationController;
45+
46+
_transition = [self fallbackForTransition:_transition];
47+
}
48+
if (!_transition) {
49+
return nil;
4650
}
4751
return self;
4852
}
@@ -118,6 +122,11 @@ - (void)initiateTransition {
118122
[to.view layoutIfNeeded];
119123
}
120124

125+
id<MDMTransition> fallback = [self fallbackForTransition:_transition];
126+
if (fallback) {
127+
_transition = fallback;
128+
}
129+
121130
[self anticipateOnlyExplicitAnimations];
122131

123132
[CATransaction begin];
@@ -151,4 +160,17 @@ - (void)anticipateOnlyExplicitAnimations {
151160
}];
152161
}
153162

163+
- (id<MDMTransition>)fallbackForTransition:(id<MDMTransition>)transition {
164+
while ([transition respondsToSelector:@selector(fallbackTransitionWithContext:)]) {
165+
id<MDMTransitionWithFallback> withFallback = (id<MDMTransitionWithFallback>)transition;
166+
167+
id<MDMTransition> fallback = [withFallback fallbackTransitionWithContext:self];
168+
if (fallback == transition) {
169+
break;
170+
}
171+
transition = fallback;
172+
}
173+
return transition;
174+
}
175+
154176
@end

0 commit comments

Comments
 (0)