CWInteractiveTransition.m 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. //
  2. // CWInteractiveTransition.m
  3. // ViewControllerTransition
  4. //
  5. // Created by chavez on 2017/6/28.
  6. // Copyright © 2017年 chavez. All rights reserved.
  7. //
  8. #import "CWInteractiveTransition.h"
  9. @interface CWInteractiveTransition ()<UIGestureRecognizerDelegate>
  10. @property (nonatomic,weak) UIViewController *weakVC;
  11. @property (nonatomic,assign) CWDrawerTransitiontype type;
  12. @property (nonatomic,assign) BOOL openEdgeGesture;
  13. @property (nonatomic,assign) CWDrawerTransitionDirection direction;
  14. @property (nonatomic,strong) CADisplayLink *link;
  15. @property (nonatomic,copy) void(^transitionDirectionAutoBlock)(CWDrawerTransitionDirection direction);
  16. @end
  17. @implementation CWInteractiveTransition
  18. {
  19. CGFloat _percent;
  20. CGFloat _remaincount;
  21. BOOL _toFinish;
  22. CGFloat _oncePercent;
  23. }
  24. - (CADisplayLink *)link {
  25. if (!_link) {
  26. _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(cw_update)];
  27. [_link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
  28. }
  29. return _link;
  30. }
  31. - (instancetype)initWithTransitiontype:(CWDrawerTransitiontype)type {
  32. if (self = [super init]) {
  33. _type = type;
  34. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cw_singleTap) name:CWLateralSlideTapNoticationKey object:nil];
  35. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cw_handleHiddenPan:) name:CWLateralSlidePanNoticationKey object:nil];
  36. }
  37. return self;
  38. }
  39. + (instancetype)interactiveWithTransitiontype:(CWDrawerTransitiontype)type {
  40. return [[self alloc] initWithTransitiontype:type];
  41. }
  42. - (void)addPanGestureForViewController:(UIViewController *)viewController {
  43. self.weakVC = viewController;
  44. if (self.openEdgeGesture) {
  45. // 因为edges设置为(UIRectEdgeLeft | UIRectEdgeLeft)或者 UIRectEdgeAll都无效,所以增加左右两个边缘手势
  46. UIScreenEdgePanGestureRecognizer *edgePanFromLeft = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(cw_handleEdgePan:)];
  47. edgePanFromLeft.edges = UIRectEdgeLeft;
  48. edgePanFromLeft.delegate = self;
  49. [viewController.view addGestureRecognizer:edgePanFromLeft];
  50. UIScreenEdgePanGestureRecognizer *edgePanFromRight = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(cw_handleEdgePan:)];
  51. edgePanFromRight.edges = UIRectEdgeRight;
  52. edgePanFromRight.delegate = self;
  53. [viewController.view addGestureRecognizer:edgePanFromRight];
  54. }else {
  55. UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(cw_handleShowPan:)];
  56. pan.delegate = self;
  57. [viewController.view addGestureRecognizer:pan];
  58. }
  59. }
  60. - (UIViewController *)viewController:(UIView *)view{
  61. for (UIView* next = view; next; next = next.superview) {
  62. UIResponder* nextResponder = [next nextResponder];
  63. if ([nextResponder isKindOfClass:[UIViewController class]]) {
  64. return (UIViewController*)nextResponder;
  65. }
  66. }
  67. return nil;
  68. }
  69. #pragma mark -GestureRecognizer
  70. - (void)cw_singleTap {
  71. if (_type == CWDrawerTransitiontypeShow) return;
  72. [self.weakVC dismissViewControllerAnimated:YES completion:nil];
  73. }
  74. - (void)cw_handleHiddenPan:(NSNotification *)note {
  75. if (_type == CWDrawerTransitiontypeShow) return;
  76. UIPanGestureRecognizer *pan = note.object;
  77. [self handleGesture:pan];
  78. }
  79. - (void)cw_handleShowPan:(UIPanGestureRecognizer *)pan {
  80. if (_type == CWDrawerTransitiontypeHidden) return;
  81. [self handleGesture:pan];
  82. }
  83. - (void)hiddenBeganTranslationX:(CGFloat)x {
  84. if ((x > 0 && _direction == CWDrawerTransitionFromLeft ) ||
  85. (x < 0 && _direction == CWDrawerTransitionFromRight )) return;
  86. self.interacting = YES;
  87. [self.weakVC dismissViewControllerAnimated:YES completion:nil];
  88. }
  89. - (void)showBeganTranslationX:(CGFloat)x gesture:(UIPanGestureRecognizer *)pan {
  90. // NSLog(@"---->%f", x);
  91. if (x >= 0) _direction = CWDrawerTransitionFromLeft;
  92. else _direction = CWDrawerTransitionFromRight;
  93. if ((x < 0 && _direction == CWDrawerTransitionFromLeft) ||
  94. (x > 0 && _direction == CWDrawerTransitionFromRight)) return;
  95. self.interacting = YES;
  96. if (_transitionDirectionAutoBlock) {
  97. _transitionDirectionAutoBlock(_direction);
  98. }
  99. }
  100. - (void)cw_updateInteractiveTransition {
  101. _percent = fminf(fmaxf(_percent, 0.003), 0.97);
  102. [self updateInteractiveTransition:_percent];
  103. }
  104. - (void)cw_endInteractiveTransition {
  105. self.interacting = NO;
  106. [self startTimerAnimationWithFinishTransition:_percent > self.configuration.finishPercent];
  107. }
  108. - (void)handleGesture:(UIPanGestureRecognizer *)pan {
  109. CGFloat x = [pan translationInView:pan.view].x;
  110. _percent = 0;
  111. _percent = x / pan.view.frame.size.width;
  112. if ((_direction == CWDrawerTransitionFromRight && _type == CWDrawerTransitiontypeShow) || (_direction == CWDrawerTransitionFromLeft && _type == CWDrawerTransitiontypeHidden)) {
  113. _percent = -_percent;
  114. }
  115. switch (pan.state) {
  116. case UIGestureRecognizerStateBegan:
  117. break;
  118. case UIGestureRecognizerStateChanged: {
  119. if (!self.interacting) { // 保证present只调用一次
  120. if (_type == CWDrawerTransitiontypeShow) {
  121. // 必须最少有20个位移才进行抽屉显示
  122. if (fabs(x) > 20) [self showBeganTranslationX:x gesture:pan];
  123. }else {
  124. [self hiddenBeganTranslationX:x];
  125. }
  126. }else {
  127. [self cw_updateInteractiveTransition];
  128. }
  129. break;
  130. }
  131. case UIGestureRecognizerStateCancelled:
  132. case UIGestureRecognizerStateEnded:{
  133. [self cw_endInteractiveTransition];
  134. break;
  135. }
  136. default:
  137. break;
  138. }
  139. }
  140. #pragma mark edge gesture
  141. - (void)cw_handleEdgePan:(UIScreenEdgePanGestureRecognizer *)edgePan {
  142. if (_type == CWDrawerTransitiontypeHidden) return;
  143. CGFloat x = [edgePan translationInView:edgePan.view].x;
  144. _percent = 0;
  145. _percent = x / edgePan.view.frame.size.width;
  146. _direction = edgePan.edges == UIRectEdgeRight;
  147. if (_direction == CWDrawerTransitionFromRight) {
  148. _percent = -_percent;
  149. }
  150. switch (edgePan.state) {
  151. case UIGestureRecognizerStateBegan: {
  152. self.interacting = YES;
  153. if (_transitionDirectionAutoBlock) {
  154. _transitionDirectionAutoBlock(_direction);
  155. }
  156. break;
  157. }
  158. case UIGestureRecognizerStateChanged: {
  159. [self cw_updateInteractiveTransition];
  160. break;
  161. }
  162. case UIGestureRecognizerStateCancelled:
  163. case UIGestureRecognizerStateEnded:{
  164. [self cw_endInteractiveTransition];
  165. break;
  166. }
  167. default:
  168. break;
  169. }
  170. }
  171. - (void)startTimerAnimationWithFinishTransition:(BOOL)isFinish {
  172. if (isFinish && _percent >= 1) {
  173. [self finishInteractiveTransition];
  174. return;
  175. }else if (!isFinish && _percent <= 0) {
  176. [self cancelInteractiveTransition];
  177. return;
  178. }
  179. _toFinish = isFinish;
  180. CGFloat remainDuration = isFinish ? self.duration * (1 - _percent) : self.duration * _percent;
  181. _remaincount = 60 * remainDuration;
  182. _oncePercent = isFinish ? (1 - _percent) / _remaincount : _percent / _remaincount;
  183. [self starDisplayLink];
  184. }
  185. #pragma mark - displayerLink
  186. - (void)starDisplayLink {
  187. [self link];
  188. }
  189. - (void)stopDisplayerLink {
  190. [self.link invalidate];
  191. self.link = nil;
  192. }
  193. - (void)cw_update {
  194. if (_percent >= 0.97 && _toFinish) {
  195. [self stopDisplayerLink];
  196. [self finishInteractiveTransition];
  197. }else if (_percent <= 0.03 && !_toFinish) {
  198. [self stopDisplayerLink];
  199. [self cancelInteractiveTransition];
  200. }else {
  201. if (_toFinish) {
  202. _percent += _oncePercent;
  203. }else {
  204. _percent -= _oncePercent;
  205. }
  206. CGFloat percent = fminf(fmaxf(_percent, 0.03), 0.97);
  207. [self updateInteractiveTransition:percent];
  208. }
  209. }
  210. - (void)dealloc {
  211. [[NSNotificationCenter defaultCenter] removeObserver:self];
  212. }
  213. #pragma mark - UIGestureRecognizerDelegate
  214. - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
  215. #pragma clang diagnostic push
  216. #pragma clang diagnostic ignored "-Wundeclared-selector"
  217. SEL selector = @selector(cw_gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:);
  218. if ([self.weakVC respondsToSelector:selector]) {
  219. IMP imp = [self.weakVC methodForSelector:selector];
  220. BOOL (*func)(id, SEL, UIGestureRecognizer *, UIGestureRecognizer *) = (void *)imp;
  221. BOOL result = func(self.weakVC, selector, gestureRecognizer, otherGestureRecognizer);
  222. return result;
  223. }
  224. #pragma clang diagnostic pop
  225. // 没有实现对应方法直接走以下默认逻辑
  226. if ([[self viewController:otherGestureRecognizer.view] isKindOfClass:[UITableViewController class]]) {
  227. return YES;
  228. }
  229. return NO;
  230. }
  231. @end