Skip to content

Commit 0fefa33

Browse files
committed
Custom view controller transition
Former-commit-id: 61b0653
1 parent 54e414e commit 0fefa33

File tree

46 files changed

+219
-496
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+219
-496
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
df1673f4f12c5bce25f2c920d6b21a75d34ddc85
1+
031a4925d832b5a16f58128ba27388dd7fdbce1a

MVVMReactiveCocoa/MRCAppDelegate.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ - (void)configureFMDB {
109109
}
110110

111111
- (void)configureAppearance {
112-
self.window.backgroundColor = [UIColor whiteColor];
112+
// self.window.backgroundColor = [UIColor whiteColor];
113113

114114
// 0x2F434F
115115
[UINavigationBar appearance].barTintColor = [UIColor colorWithRed:(48 - 40) / 215.0 green:(67 - 40) / 215.0 blue:(78 - 40) / 215.0 alpha:1];

MVVMReactiveCocoa/MVVMReactiveCocoa-Prefix.pch

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
#import "UMSocialQQHandler.h"
5252
#import "UIImageView+UIActivityIndicatorForSDWebImage.h"
5353
#import "MRCMemoryCache.h"
54-
#import "UINavigationController+FDFullscreenPopGesture.h"
54+
// #import "UINavigationController+FDFullscreenPopGesture.h"
5555
#import "FMDatabaseQueue+MRCHelper.h"
5656
#import "MRCReactiveView.h"
5757
#import "NSString+Octicons.h"

MVVMReactiveCocoa/View/BaseClass/MRCNavigationControllerStack.m

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
#import "MRCRouter.h"
1111
#import "MRCNavigationController.h"
1212
#import "MRCTabBarController.h"
13+
#import "MRCViewControllerAnimatedTransition.h"
1314

14-
@interface MRCNavigationControllerStack ()
15+
@interface MRCNavigationControllerStack () <UINavigationControllerDelegate>
1516

1617
@property (nonatomic, strong) id<MRCViewModelServices> services;
1718
@property (nonatomic, strong) NSMutableArray *navigationControllers;
@@ -116,11 +117,31 @@ - (void)registerNavigationHooks {
116117

117118
if (![viewController isKindOfClass:[UINavigationController class]]/* && ![viewController isKindOfClass:[MRCTabBarController class]]*/) {
118119
viewController = [[MRCNavigationController alloc] initWithRootViewController:viewController];
120+
((UINavigationController *)viewController).delegate = self;
119121
[self pushNavigationController:(UINavigationController *)viewController];
120122
}
121123

122124
MRCSharedAppDelegate.window.rootViewController = viewController;
123125
}];
124126
}
125127

128+
#pragma mark - UINavigationControllerDelegate
129+
130+
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
131+
interactionControllerForAnimationController:(MRCViewControllerAnimatedTransition *)animationController {
132+
if (animationController.operation == UINavigationControllerOperationPop) {
133+
return animationController.fromViewController.interactivePopTransition;
134+
}
135+
return nil;
136+
}
137+
138+
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
139+
animationControllerForOperation:(UINavigationControllerOperation)operation
140+
fromViewController:(MRCViewController *)fromVC
141+
toViewController:(MRCViewController *)toVC {
142+
return [[MRCViewControllerAnimatedTransition alloc] initWithNavigationControllerOperation:operation
143+
fromViewController:fromVC
144+
toViewController:toVC];
145+
}
146+
126147
@end

MVVMReactiveCocoa/View/BaseClass/MRCTableViewController.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ - (void)viewDidLoad {
109109
self.tableView.tableFooterView = [[UIView alloc] init];
110110
}
111111

112+
- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer {
113+
[self.tableView addGestureRecognizer:gestureRecognizer];
114+
}
115+
112116
- (void)dealloc {
113117
_tableView.dataSource = nil;
114118
_tableView.delegate = nil;

MVVMReactiveCocoa/View/BaseClass/MRCViewController.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,21 @@
88

99
@interface MRCViewController : UIViewController
1010

11+
/// The `viewModel` parameter in `-initWithViewModel:` method.
12+
@property (nonatomic, strong, readonly) MRCViewModel *viewModel;
13+
14+
@property (nonatomic, strong, readonly) UIPercentDrivenInteractiveTransition *interactivePopTransition;
15+
1116
/// Initialization method. This is the preferred way to create a new view.
1217
///
1318
/// viewModel - corresponding view model
1419
///
1520
/// Returns a new view.
1621
- (instancetype)initWithViewModel:(MRCViewModel *)viewModel;
1722

18-
/// The `viewModel` parameter in `-initWithViewModel:` method.
19-
@property (nonatomic, strong, readonly) MRCViewModel *viewModel;
20-
2123
/// Binds the corresponding view model to the view.
2224
- (void)bindViewModel;
2325

26+
- (void)addGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer;
27+
2428
@end

MVVMReactiveCocoa/View/BaseClass/MRCViewController.m

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
@interface MRCViewController ()
1616

1717
@property (nonatomic, strong, readwrite) MRCViewModel *viewModel;
18+
@property (nonatomic, strong, readwrite) UIPercentDrivenInteractiveTransition *interactivePopTransition;
1819

1920
@end
2021

@@ -30,7 +31,7 @@ + (instancetype)allocWithZone:(struct _NSZone *)zone {
3031
@strongify(viewController)
3132
[viewController bindViewModel];
3233
}];
33-
34+
3435
return viewController;
3536
}
3637

@@ -44,23 +45,31 @@ - (MRCViewController *)initWithViewModel:(id)viewModel {
4445

4546
- (void)viewDidLoad {
4647
[super viewDidLoad];
47-
48+
4849
self.automaticallyAdjustsScrollViewInsets = NO;
4950
self.extendedLayoutIncludesOpaqueBars = YES;
51+
52+
UIScreenEdgePanGestureRecognizer *popRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePopRecognizer:)];
53+
popRecognizer.edges = UIRectEdgeLeft;
54+
[self addGestureRecognizer:popRecognizer];
55+
}
56+
57+
- (void)addGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer {
58+
[self.view addGestureRecognizer:gestureRecognizer];
5059
}
5160

5261
- (void)bindViewModel {
5362
// System title view
5463
RAC(self, title) = RACObserve(self.viewModel, title);
55-
64+
5665
UIView *titleView = self.navigationItem.titleView;
57-
66+
5867
// Double title view
5968
MRCDoubleTitleView *doubleTitleView = [[MRCDoubleTitleView alloc] init];
60-
69+
6170
RAC(doubleTitleView.titleLabel, text) = RACObserve(self.viewModel, title);
6271
RAC(doubleTitleView.subtitleLabel, text) = RACObserve(self.viewModel, subtitle);
63-
72+
6473
@weakify(self)
6574
[[self
6675
rac_signalForSelector:@selector(viewWillTransitionToSize:withTransitionCoordinator:)]
@@ -69,11 +78,11 @@ - (void)bindViewModel {
6978
doubleTitleView.titleLabel.text = self.viewModel.title;
7079
doubleTitleView.subtitleLabel.text = self.viewModel.subtitle;
7180
}];
72-
81+
7382
// Loading title view
7483
MRCLoadingTitleView *loadingTitleView = [[NSBundle mainBundle] loadNibNamed:@"MRCLoadingTitleView" owner:nil options:nil].firstObject;
7584
loadingTitleView.frame = CGRectMake((SCREEN_WIDTH - CGRectGetWidth(loadingTitleView.frame)) / 2.0, 0, CGRectGetWidth(loadingTitleView.frame), CGRectGetHeight(loadingTitleView.frame));
76-
85+
7786
RAC(self.navigationItem, titleView) = [RACObserve(self.viewModel, titleViewType).distinctUntilChanged map:^(NSNumber *value) {
7887
MRCTitleViewType titleViewType = value.unsignedIntegerValue;
7988
switch (titleViewType) {
@@ -85,25 +94,25 @@ - (void)bindViewModel {
8594
return (UIView *)loadingTitleView;
8695
}
8796
}];
88-
97+
8998
[self.viewModel.errors subscribeNext:^(NSError *error) {
9099
@strongify(self)
91-
100+
92101
MRCLogError(error);
93-
102+
94103
if ([error.domain isEqual:OCTClientErrorDomain] && error.code == OCTClientErrorAuthenticationFailed) {
95104
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:MRC_ALERT_TITLE
96105
message:@"Your authorization has expired, please login again"
97106
preferredStyle:UIAlertControllerStyleAlert];
98-
107+
99108
[alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
100109
@strongify(self)
101110
[SSKeychain deleteAccessToken];
102-
103-
MRCLoginViewModel *loginViewModel = [[MRCLoginViewModel alloc] initWithServices:self.viewModel.services params:nil];
111+
112+
MRCLoginViewModel *loginViewModel = [[MRCLoginViewModel alloc] initWithServices:self.viewModel.services params:nil];
104113
[self.viewModel.services resetRootViewModel:loginViewModel];
105114
}]];
106-
115+
107116
[self presentViewController:alertController animated:YES completion:NULL];
108117
} else if (error.code != OCTClientErrorTwoFactorAuthenticationOneTimePasswordRequired && error.code != OCTClientErrorConnectionFailed) {
109118
MRCError(error.localizedDescription);
@@ -128,4 +137,29 @@ - (UIStatusBarStyle)preferredStatusBarStyle {
128137
return UIStatusBarStyleLightContent;
129138
}
130139

140+
#pragma mark - UIScreenEdgePanGestureRecognizer handlers
141+
142+
- (void)handlePopRecognizer:(UIScreenEdgePanGestureRecognizer *)recognizer {
143+
CGFloat progress = [recognizer translationInView:self.view].x / CGRectGetWidth(self.view.frame);
144+
progress = MIN(1.0, MAX(0.0, progress));
145+
146+
if (recognizer.state == UIGestureRecognizerStateBegan) {
147+
// Create a interactive transition and pop the view controller
148+
self.interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
149+
[self.navigationController popViewControllerAnimated:YES];
150+
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
151+
// Update the interactive transition's progress
152+
[self.interactivePopTransition updateInteractiveTransition:progress];
153+
} else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled) {
154+
// Finish or cancel the interactive transition
155+
if (progress > 0.5) {
156+
[self.interactivePopTransition finishInteractiveTransition];
157+
} else {
158+
[self.interactivePopTransition cancelInteractiveTransition];
159+
}
160+
161+
self.interactivePopTransition = nil;
162+
}
163+
}
164+
131165
@end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// MRCViewControllerAnimatedTransition.h
3+
// MVVMReactiveCocoa
4+
//
5+
// Created by leichunfeng on 15/12/8.
6+
// Copyright © 2015年 leichunfeng. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
#import "MRCViewController.h"
11+
12+
@interface MRCViewControllerAnimatedTransition : NSObject <UIViewControllerAnimatedTransitioning>
13+
14+
@property (nonatomic, assign, readonly) UINavigationControllerOperation operation;
15+
@property (nonatomic, weak, readonly) MRCViewController *fromViewController;
16+
@property (nonatomic, weak, readonly) MRCViewController *toViewController;
17+
18+
- (instancetype)initWithNavigationControllerOperation:(UINavigationControllerOperation)operation
19+
fromViewController:(MRCViewController *)fromViewController
20+
toViewController:(MRCViewController *)toViewController;
21+
22+
@end
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//
2+
// MRCViewControllerAnimatedTransition.m
3+
// MVVMReactiveCocoa
4+
//
5+
// Created by leichunfeng on 15/12/8.
6+
// Copyright © 2015年 leichunfeng. All rights reserved.
7+
//
8+
9+
#import "MRCViewControllerAnimatedTransition.h"
10+
11+
@interface MRCViewControllerAnimatedTransition ()
12+
13+
@property (nonatomic, assign, readwrite) UINavigationControllerOperation operation;
14+
@property (nonatomic, weak, readwrite) MRCViewController *fromViewController;
15+
@property (nonatomic, weak, readwrite) MRCViewController *toViewController;
16+
17+
@end
18+
19+
@implementation MRCViewControllerAnimatedTransition
20+
21+
- (instancetype)initWithNavigationControllerOperation:(UINavigationControllerOperation)operation
22+
fromViewController:(MRCViewController *)fromViewController
23+
toViewController:(MRCViewController *)toViewController {
24+
self = [super init];
25+
if (self) {
26+
self.operation = operation;
27+
self.fromViewController = fromViewController;
28+
self.toViewController = toViewController;
29+
}
30+
return self;
31+
}
32+
33+
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
34+
return 0.3;
35+
}
36+
37+
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
38+
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
39+
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
40+
41+
CGRect initialFrameForFromViewController = [transitionContext initialFrameForViewController:fromViewController];
42+
CGRect finalFrameForFromViewController = [transitionContext finalFrameForViewController:fromViewController];
43+
44+
NSLog(@"initialFrameForFromViewController: %@", NSStringFromCGRect(initialFrameForFromViewController));
45+
NSLog(@"finalFrameForFromViewController: %@", NSStringFromCGRect(finalFrameForFromViewController));
46+
47+
CGRect initialFrameForToViewController = [transitionContext initialFrameForViewController:toViewController];
48+
CGRect finalFrameForToViewController = [transitionContext finalFrameForViewController:toViewController];
49+
50+
NSLog(@"initialFrameForToViewController: %@", NSStringFromCGRect(initialFrameForToViewController));
51+
NSLog(@"finalFrameForToViewController: %@", NSStringFromCGRect(finalFrameForToViewController));
52+
53+
NSTimeInterval duration = [self transitionDuration:transitionContext];
54+
55+
if (self.operation == UINavigationControllerOperationPush) { // push
56+
CGRect frame = [transitionContext finalFrameForViewController:toViewController];
57+
toViewController.view.frame = CGRectOffset(frame, CGRectGetWidth(frame), 0);
58+
[[transitionContext containerView] addSubview:toViewController.view];
59+
60+
[UIView animateWithDuration:duration
61+
animations:^{
62+
fromViewController.view.alpha = 0;
63+
// fromViewController.view.frame = CGRectInset(fromViewController.view.frame, 10, 10);
64+
toViewController.view.frame = CGRectOffset(toViewController.view.frame, -CGRectGetWidth(toViewController.view.frame), 0);
65+
}
66+
completion:^(BOOL finished) {
67+
[transitionContext completeTransition:YES];
68+
}];
69+
} else if (self.operation == UINavigationControllerOperationPop) { // pop
70+
[[transitionContext containerView] addSubview:toViewController.view];
71+
[[transitionContext containerView] sendSubviewToBack:toViewController.view];
72+
73+
[UIView animateWithDuration:duration
74+
animations:^{
75+
fromViewController.view.frame = CGRectOffset(fromViewController.view.frame, CGRectGetWidth(fromViewController.view.frame), 0);
76+
toViewController.view.alpha = 1;
77+
// toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController];
78+
}
79+
completion:^(BOOL finished) {
80+
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
81+
}];
82+
}
83+
}
84+
85+
@end

Podfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pod 'DZNEmptyDataSet', '~> 1.5.1'
1818
pod 'Ono', '~> 1.2.0'
1919
pod 'FMDB'
2020
pod 'UMengSocial', '~> 4.3'
21-
pod 'FDFullscreenPopGesture', '~> 1.1'
21+
#pod 'FDFullscreenPopGesture', '~> 1.1'
2222
pod 'GPUImage', '~> 0.1.7'
2323
pod 'Reveal-iOS-SDK', '~> 1.6.0'
2424
pod 'Appirater'

0 commit comments

Comments
 (0)