HWPanModalPresentationController.m 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. //
  2. // HWPanModalPresentationController.m
  3. // HWPanModal
  4. //
  5. // Created by heath wang on 2019/4/26.
  6. //
  7. #import "HWPanModalPresentationController.h"
  8. #import "HWDimmedView.h"
  9. #import "HWPanContainerView.h"
  10. #import "UIViewController+LayoutHelper.h"
  11. #import "HWPanModalAnimator.h"
  12. #import "HWPanModalInteractiveAnimator.h"
  13. #import "HWPanModalPresentationDelegate.h"
  14. #import "UIViewController+PanModalPresenter.h"
  15. #import "HWPanIndicatorView.h"
  16. #import "UIView+HW_Frame.h"
  17. #import "HWPanModalPresentableHandler.h"
  18. @interface HWPanModalPresentationController () <UIGestureRecognizerDelegate, HWPanModalPresentableHandlerDelegate, HWPanModalPresentableHandlerDataSource>
  19. // 判断弹出的view是否在做动画
  20. @property (nonatomic, assign) BOOL isPresentedViewAnimating;
  21. @property (nonatomic, assign) PresentationState currentPresentationState;
  22. @property (nonatomic, strong) id<HWPanModalPresentable> presentable;
  23. // view
  24. @property (nonatomic, strong) HWDimmedView *backgroundView;
  25. @property (nonatomic, strong) HWPanContainerView *panContainerView;
  26. @property (nonatomic, strong) UIView<HWPanModalIndicatorProtocol> *dragIndicatorView;
  27. @property (nonatomic, strong) HWPanModalPresentableHandler *handler;
  28. @end
  29. @implementation HWPanModalPresentationController
  30. - (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(nullable UIViewController *)presentingViewController {
  31. self = [super initWithPresentedViewController:presentedViewController presentingViewController:presentingViewController];
  32. if (self) {
  33. _handler = [[HWPanModalPresentableHandler alloc] initWithPresentable:[self presentable]];
  34. _handler.delegate = self;
  35. _handler.dataSource = self;
  36. }
  37. return self;
  38. }
  39. #pragma mark - overridden
  40. - (UIView *)presentedView {
  41. return self.panContainerView;
  42. }
  43. - (void)containerViewWillLayoutSubviews {
  44. [super containerViewWillLayoutSubviews];
  45. [self configureViewLayout];
  46. }
  47. #pragma mark - Tracking the Transition Start and End
  48. - (void)presentationTransitionWillBegin {
  49. [[self presentable] panModalTransitionWillBegin];
  50. if (!self.containerView)
  51. return;
  52. [self layoutBackgroundView:self.containerView];
  53. if ([[self presentable] originPresentationState] == PresentationStateLong) {
  54. self.currentPresentationState = PresentationStateLong;
  55. } else if ([[self presentable] originPresentationState] == PresentationStateMedium) {
  56. self.currentPresentationState = PresentationStateMedium;
  57. }
  58. [self layoutPresentedView:self.containerView];
  59. [self.handler configureScrollViewInsets];
  60. if (!self.presentedViewController.transitionCoordinator) {
  61. self.backgroundView.dimState = DimStateMax;
  62. return;
  63. }
  64. __weak typeof(self) wkSelf = self;
  65. __block BOOL isAnimated = NO;
  66. [self.presentedViewController.transitionCoordinator animateAlongsideTransition:^(id <UIViewControllerTransitionCoordinatorContext> context) {
  67. wkSelf.backgroundView.dimState = DimStateMax;
  68. [wkSelf.presentedViewController setNeedsStatusBarAppearanceUpdate];
  69. isAnimated = YES;
  70. } completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
  71. if (!isAnimated) {
  72. /// In some cases, for example, present a `hw` when a navigation controller is pushing a new vc, `animateAlongsideTransition` will not call.
  73. /// If not called, call it here.
  74. wkSelf.backgroundView.dimState = DimStateMax;
  75. [wkSelf.presentedViewController setNeedsStatusBarAppearanceUpdate];
  76. }
  77. if ([[wkSelf presentable] allowsTouchEventsPassingThroughTransitionView]) {
  78. // hack TransitionView
  79. [wkSelf.containerView setValue:@(YES) forKey:@"ignoreDirectTouchEvents"];
  80. }
  81. }];
  82. }
  83. - (void)presentationTransitionDidEnd:(BOOL)completed {
  84. [[self presentable] panModalTransitionDidFinish];
  85. if (completed)
  86. return;
  87. [self.backgroundView removeFromSuperview];
  88. [self.presentedView endEditing:YES];
  89. }
  90. - (void)dismissalTransitionWillBegin {
  91. id <UIViewControllerTransitionCoordinator> transitionCoordinator = self.presentedViewController.transitionCoordinator;
  92. if (!transitionCoordinator) {
  93. self.backgroundView.dimState = DimStateOff;
  94. return;
  95. }
  96. __weak typeof(self) wkSelf = self;
  97. [transitionCoordinator animateAlongsideTransition:^(id <UIViewControllerTransitionCoordinatorContext> context) {
  98. wkSelf.dragIndicatorView.alpha = 0;
  99. wkSelf.backgroundView.dimState = DimStateOff;
  100. [wkSelf.presentedViewController setNeedsStatusBarAppearanceUpdate];
  101. } completion:^(id <UIViewControllerTransitionCoordinatorContext> context) {
  102. }];
  103. }
  104. - (void)dismissalTransitionDidEnd:(BOOL)completed {
  105. if (completed) {
  106. // break the delegate
  107. self.delegate = nil;
  108. }
  109. }
  110. #pragma mark - UIContentContainer protocol
  111. - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator {
  112. [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
  113. [coordinator animateAlongsideTransition:^(id <UIViewControllerTransitionCoordinatorContext> context) {
  114. if (self && [self presentable]) {
  115. [self adjustPresentedViewFrame];
  116. if ([self.presentable shouldRoundTopCorners]) {
  117. [self addRoundedCornersToView:self.panContainerView.contentView];
  118. }
  119. [self updateDragIndicatorView];
  120. }
  121. } completion:^(id <UIViewControllerTransitionCoordinatorContext> context) {
  122. [self transitionToState:self.currentPresentationState animated:NO];
  123. }];
  124. }
  125. #pragma mark - public method
  126. - (void)setNeedsLayoutUpdate {
  127. [self configureViewLayout];
  128. [self adjustPresentedViewFrame];
  129. [self updateBackgroundColor];
  130. [self updateContainerViewShadow];
  131. [self updateDragIndicatorView];
  132. [self updateRoundedCorners];
  133. [self.handler observeScrollable];
  134. [self.handler configureScrollViewInsets];
  135. [self checkEdgeInteractive];
  136. }
  137. - (void)transitionToState:(PresentationState)state animated:(BOOL)animated {
  138. if (![self.presentable shouldTransitionToState:state])
  139. return;
  140. [self.dragIndicatorView didChangeToState:HWIndicatorStateNormal];
  141. [self.presentable willTransitionToState:state];
  142. switch (state) {
  143. case PresentationStateLong: {
  144. [self snapToYPos:self.handler.longFormYPosition animated:animated];
  145. }
  146. break;
  147. case PresentationStateMedium: {
  148. [self snapToYPos:self.handler.mediumFormYPosition animated:animated];
  149. }
  150. break;
  151. case PresentationStateShort: {
  152. [self snapToYPos:self.handler.shortFormYPosition animated:animated];
  153. }
  154. break;
  155. }
  156. self.currentPresentationState = state;
  157. [[self presentable] didChangeTransitionToState:state];
  158. }
  159. - (void)setScrollableContentOffset:(CGPoint)offset animated:(BOOL)animated {
  160. [self.handler setScrollableContentOffset:offset animated:animated];
  161. }
  162. - (void)updateUserHitBehavior {
  163. [self checkVCContainerEventPass];
  164. [self checkBackgroundViewEventPass];
  165. }
  166. #pragma mark - layout
  167. - (void)adjustPresentedViewFrame {
  168. if (!self.containerView)
  169. return;
  170. CGRect frame = self.containerView.frame;
  171. CGSize size = CGSizeMake(CGRectGetWidth(frame), CGRectGetHeight(frame) - self.handler.anchoredYPosition);
  172. self.presentedView.hw_size = frame.size;
  173. self.panContainerView.contentView.frame = CGRectMake(0, 0, size.width, size.height);
  174. self.presentedViewController.view.frame = self.panContainerView.contentView.bounds;
  175. [self.presentedViewController.view setNeedsLayout];
  176. [self.presentedViewController.view layoutIfNeeded];
  177. }
  178. /**
  179. * add backGroundView并设置约束
  180. */
  181. - (void)layoutBackgroundView:(UIView *)containerView {
  182. [containerView addSubview:self.backgroundView];
  183. [self updateBackgroundColor];
  184. self.backgroundView.translatesAutoresizingMaskIntoConstraints = NO;
  185. NSArray *hCons = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[backgroundView]|" options:0 metrics:nil views:@{@"backgroundView": self.backgroundView}];
  186. NSArray *vCons = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[backgroundView]|" options:0 metrics:nil views:@{@"backgroundView": self.backgroundView}];
  187. [NSLayoutConstraint activateConstraints:hCons];
  188. [NSLayoutConstraint activateConstraints:vCons];
  189. }
  190. - (void)updateBackgroundColor {
  191. self.backgroundView.blurTintColor = [self.presentable backgroundConfig].blurTintColor;
  192. }
  193. - (void)layoutPresentedView:(UIView *)containerView {
  194. if (!self.presentable)
  195. return;
  196. self.handler.presentedView = self.presentedView;
  197. [containerView addSubview:self.presentedView];
  198. [containerView addGestureRecognizer:self.handler.panGestureRecognizer];
  199. if ([self.presentable allowScreenEdgeInteractive]) {
  200. [containerView addGestureRecognizer:self.handler.screenEdgeGestureRecognizer];
  201. [self.handler.screenEdgeGestureRecognizer addTarget:self action:@selector(screenEdgeInteractiveAction:)];
  202. }
  203. [self setNeedsLayoutUpdate];
  204. [self adjustPanContainerBackgroundColor];
  205. [[self presentable] presentedViewDidMoveToSuperView];
  206. }
  207. - (void)adjustPanContainerBackgroundColor {
  208. self.panContainerView.contentView.backgroundColor = self.presentedViewController.view.backgroundColor ? : [self.presentable panScrollable].backgroundColor;
  209. }
  210. - (void)updateDragIndicatorView {
  211. if ([self.presentable showDragIndicator]) {
  212. [self addDragIndicatorViewToView:self.panContainerView];
  213. } else {
  214. self.dragIndicatorView.hidden = YES;
  215. }
  216. }
  217. - (void)addDragIndicatorViewToView:(UIView *)view {
  218. // if has been add, won't update it.
  219. self.dragIndicatorView.hidden = NO;
  220. CGSize indicatorSize = [self.dragIndicatorView indicatorSize];
  221. if (self.dragIndicatorView.superview == view) {
  222. self.dragIndicatorView.frame = CGRectMake((view.hw_width - indicatorSize.width) / 2, -kIndicatorYOffset - indicatorSize.height, indicatorSize.width, indicatorSize.height);
  223. [self.dragIndicatorView didChangeToState:HWIndicatorStateNormal];
  224. return;
  225. }
  226. self.handler.dragIndicatorView = self.dragIndicatorView;
  227. [view addSubview:self.dragIndicatorView];
  228. self.dragIndicatorView.frame = CGRectMake((view.hw_width - indicatorSize.width) / 2, -kIndicatorYOffset - indicatorSize.height, indicatorSize.width, indicatorSize.height);
  229. [self.dragIndicatorView setupSubviews];
  230. [self.dragIndicatorView didChangeToState:HWIndicatorStateNormal];
  231. }
  232. - (void)updateRoundedCorners {
  233. if ([self.presentable shouldRoundTopCorners]) {
  234. [self addRoundedCornersToView:self.panContainerView.contentView];
  235. } else {
  236. [self resetRoundedCornersToView:self.panContainerView.contentView];
  237. }
  238. }
  239. - (void)addRoundedCornersToView:(UIView *)view {
  240. CGFloat radius = [self.presentable cornerRadius];
  241. UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:UIRectCornerTopRight | UIRectCornerTopLeft cornerRadii:CGSizeMake(radius, radius)];
  242. CAShapeLayer *mask = [CAShapeLayer new];
  243. mask.path = bezierPath.CGPath;
  244. view.layer.mask = mask;
  245. // 提高性能
  246. view.layer.shouldRasterize = YES;
  247. view.layer.rasterizationScale = [UIScreen mainScreen].scale;
  248. }
  249. - (void)resetRoundedCornersToView:(UIView *)view {
  250. view.layer.mask = nil;
  251. view.layer.shouldRasterize = NO;
  252. }
  253. - (void)updateContainerViewShadow {
  254. HWPanModalShadow *shadow = [[self presentable] contentShadow];
  255. if (shadow.shadowColor) {
  256. [self.panContainerView updateShadow:shadow.shadowColor shadowRadius:shadow.shadowRadius shadowOffset:shadow.shadowOffset shadowOpacity:shadow.shadowOpacity];
  257. } else {
  258. [self.panContainerView clearShadow];
  259. }
  260. }
  261. /**
  262. * Calculates & stores the layout anchor points & options
  263. */
  264. - (void)configureViewLayout {
  265. [self.handler configureViewLayout];
  266. self.containerView.userInteractionEnabled = [[self presentable] isUserInteractionEnabled];
  267. }
  268. #pragma mark - event passing through
  269. - (void)checkVCContainerEventPass {
  270. BOOL eventPassValue = [[self presentable] allowsTouchEventsPassingThroughTransitionView];
  271. // hack TransitionView
  272. [self.containerView setValue:@(eventPassValue) forKey:@"ignoreDirectTouchEvents"];
  273. }
  274. - (void)checkBackgroundViewEventPass {
  275. if ([[self presentable] allowsTouchEventsPassingThroughTransitionView]) {
  276. self.backgroundView.userInteractionEnabled = NO;
  277. self.backgroundView.tapBlock = nil;
  278. } else {
  279. self.backgroundView.userInteractionEnabled = YES;
  280. __weak typeof(self) wkSelf = self;
  281. self.backgroundView.tapBlock = ^(UITapGestureRecognizer *recognizer) {
  282. if ([[wkSelf presentable] allowsTapBackgroundToDismiss]) {
  283. [wkSelf dismiss:NO mode:PanModalInteractiveModeNone];
  284. }
  285. };
  286. }
  287. }
  288. #pragma mark - y position update
  289. - (void)snapToYPos:(CGFloat)yPos animated:(BOOL)animated {
  290. if (animated) {
  291. [HWPanModalAnimator animate:^{
  292. self.isPresentedViewAnimating = YES;
  293. [self adjustToYPos:yPos];
  294. } config:self.presentable completion:^(BOOL completion) {
  295. self.isPresentedViewAnimating = NO;
  296. }];
  297. } else {
  298. [self adjustToYPos:yPos];
  299. }
  300. }
  301. - (void)adjustToYPos:(CGFloat)yPos {
  302. self.presentedView.hw_top = MAX(yPos, self.handler.anchoredYPosition);
  303. // change dim background starting from shortFormYPosition.
  304. if (self.presentedView.frame.origin.y >= self.handler.shortFormYPosition) {
  305. CGFloat yDistanceFromShortForm = self.presentedView.frame.origin.y - self.handler.shortFormYPosition;
  306. CGFloat bottomHeight = self.containerView.hw_height - self.handler.shortFormYPosition;
  307. CGFloat percent = yDistanceFromShortForm / bottomHeight;
  308. self.backgroundView.dimState = DimStatePercent;
  309. self.backgroundView.percent = 1 - percent;
  310. [self.presentable panModalGestureRecognizer:self.handler.panGestureRecognizer dismissPercent:MIN(percent, 1)];
  311. if (self.presentedViewController.isBeingDismissed) {
  312. [[self interactiveAnimator] updateInteractiveTransition:MIN(percent, 1)];
  313. }
  314. } else {
  315. self.backgroundView.dimState = DimStateMax;
  316. }
  317. }
  318. #pragma mark - HWPanModalPresentableHandlerDelegate
  319. - (void)adjustPresentableYPos:(CGFloat)yPos {
  320. [self adjustToYPos:yPos];
  321. }
  322. - (void)dismiss:(BOOL)isInteractive mode:(PanModalInteractiveMode)mode {
  323. [self dismiss:isInteractive mode:mode animated:YES completion:nil];
  324. }
  325. - (void)dismiss:(BOOL)isInteractive mode:(PanModalInteractiveMode)mode animated:(BOOL)animated completion:(void (^)(void))completion {
  326. self.presentedViewController.hw_panModalPresentationDelegate.interactive = isInteractive;
  327. self.presentedViewController.hw_panModalPresentationDelegate.interactiveMode = mode;
  328. [self.presentable panModalWillDismiss];
  329. [self.presentedViewController dismissViewControllerAnimated:animated completion:^{
  330. if (completion) completion();
  331. [self.presentable panModalDidDismissed];
  332. }];
  333. }
  334. - (void)dismissAnimated:(BOOL)animated completion:(nonnull void (^)(void))completion {
  335. [self dismiss:NO mode:PanModalInteractiveModeNone animated:animated completion:completion];
  336. }
  337. - (void)presentableTransitionToState:(PresentationState)state {
  338. [self transitionToState:state animated:YES];
  339. }
  340. - (PresentationState)getCurrentPresentationState {
  341. return self.currentPresentationState;
  342. }
  343. #pragma mark - interactive handle
  344. - (void)finishInteractiveTransition {
  345. if (self.presentedViewController.isBeingDismissed) {
  346. // make the containerView can not response event action.
  347. self.containerView.userInteractionEnabled = NO;
  348. [[self interactiveAnimator] finishInteractiveTransition];
  349. if (self.presentedViewController.hw_panModalPresentationDelegate.interactiveMode != PanModalInteractiveModeDragDown)
  350. return;
  351. if ([[self presentable] presentingVCAnimationStyle] > PresentingViewControllerAnimationStyleNone) {
  352. [HWPanModalAnimator animate:^{
  353. [self presentedView].hw_top = self.containerView.frame.size.height;
  354. self.dragIndicatorView.alpha = 0;
  355. self.backgroundView.dimState = DimStateOff;
  356. } config:[self presentable] completion:^(BOOL completion) {
  357. }];
  358. }
  359. }
  360. }
  361. - (void)cancelInteractiveTransition {
  362. if (self.presentedViewController.isBeingDismissed) {
  363. [[self interactiveAnimator] cancelInteractiveTransition];
  364. self.presentedViewController.hw_panModalPresentationDelegate.interactiveMode = PanModalInteractiveModeNone;
  365. self.presentedViewController.hw_panModalPresentationDelegate.interactive = NO;
  366. }
  367. }
  368. #pragma mark - HWPanModalPresentableHandlerDataSource
  369. - (CGSize)containerSize {
  370. return self.containerView.bounds.size;
  371. }
  372. - (BOOL)isBeingDismissed {
  373. return self.presentedViewController.isBeingDismissed;
  374. }
  375. - (BOOL)isBeingPresented {
  376. return self.presentedViewController.isBeingPresented;
  377. }
  378. - (BOOL)isPresentedViewAnchored {
  379. if (![[self presentable] shouldRespondToPanModalGestureRecognizer:self.handler.panGestureRecognizer]) {
  380. return YES;
  381. }
  382. if (!self.isPresentedViewAnimating && self.handler.extendsPanScrolling && (CGRectGetMinY(self.presentedView.frame) <= self.handler.anchoredYPosition || HW_TWO_FLOAT_IS_EQUAL(CGRectGetMinY(self.presentedView.frame), self.handler.anchoredYPosition))) {
  383. return YES;
  384. }
  385. return NO;
  386. }
  387. - (BOOL)isPresentedControllerInteractive {
  388. return self.presentedViewController.hw_panModalPresentationDelegate.interactive;
  389. }
  390. - (BOOL)isFormPositionAnimating {
  391. return self.isPresentedViewAnimating;
  392. }
  393. #pragma mark - Screen Gesture enevt
  394. - (void)screenEdgeInteractiveAction:(UIPanGestureRecognizer *)recognizer {
  395. CGPoint translation = [recognizer translationInView:recognizer.view];
  396. CGFloat percent = translation.x / CGRectGetWidth(recognizer.view.bounds);
  397. CGPoint velocity = [recognizer velocityInView:recognizer.view];
  398. switch (recognizer.state) {
  399. case UIGestureRecognizerStateBegan: {
  400. [self dismiss:YES mode:PanModalInteractiveModeSideslip];
  401. }
  402. break;
  403. case UIGestureRecognizerStateCancelled:
  404. case UIGestureRecognizerStateEnded: {
  405. if (percent > 0.5 || velocity.x >= [[self presentable] minHorizontalVelocityToTriggerScreenEdgeDismiss]) {
  406. [self finishInteractiveTransition];
  407. } else {
  408. [self cancelInteractiveTransition];
  409. }
  410. }
  411. break;
  412. case UIGestureRecognizerStateChanged: {
  413. [[self interactiveAnimator] updateInteractiveTransition:percent];
  414. }
  415. break;
  416. default:
  417. break;
  418. }
  419. }
  420. - (void)checkEdgeInteractive {
  421. //TODO: changed the user interactive, if someone else has different requirements, change it.
  422. self.handler.screenEdgeGestureRecognizer.enabled = [[self presentable] allowScreenEdgeInteractive];
  423. }
  424. #pragma mark - Getter
  425. - (id <HWPanModalPresentable>)presentable {
  426. if ([self.presentedViewController conformsToProtocol:@protocol(HWPanModalPresentable)]) {
  427. return (id <HWPanModalPresentable>) self.presentedViewController;
  428. }
  429. return nil;
  430. }
  431. - (HWPanModalInteractiveAnimator *)interactiveAnimator {
  432. HWPanModalPresentationDelegate *presentationDelegate = self.presentedViewController.hw_panModalPresentationDelegate;
  433. return presentationDelegate.interactiveDismissalAnimator;
  434. }
  435. - (HWDimmedView *)backgroundView {
  436. if (!_backgroundView) {
  437. if (self.presentable) {
  438. _backgroundView = [[HWDimmedView alloc] initWithBackgroundConfig:[self.presentable backgroundConfig]];
  439. } else {
  440. _backgroundView = [[HWDimmedView alloc] init];
  441. }
  442. if ([[self presentable] allowsTouchEventsPassingThroughTransitionView]) {
  443. _backgroundView.userInteractionEnabled = NO;
  444. } else {
  445. __weak typeof(self) wkSelf = self;
  446. _backgroundView.tapBlock = ^(UITapGestureRecognizer *recognizer) {
  447. if ([[wkSelf presentable] allowsTapBackgroundToDismiss]) {
  448. [wkSelf dismiss:NO mode:PanModalInteractiveModeNone];
  449. }
  450. };
  451. }
  452. }
  453. return _backgroundView;
  454. }
  455. - (HWPanContainerView *)panContainerView {
  456. if (!_panContainerView) {
  457. _panContainerView = [[HWPanContainerView alloc] initWithPresentedView:self.presentedViewController.view frame:self.containerView.frame];
  458. }
  459. return _panContainerView;
  460. }
  461. - (UIView<HWPanModalIndicatorProtocol> *)dragIndicatorView {
  462. if (!_dragIndicatorView) {
  463. if ([self presentable] &&
  464. [[self presentable] respondsToSelector:@selector(customIndicatorView)] &&
  465. [[self presentable] customIndicatorView] != nil) {
  466. _dragIndicatorView = [[self presentable] customIndicatorView];
  467. // set the indicator size first in case `setupSubviews` can Not get the right size.
  468. _dragIndicatorView.hw_size = [[[self presentable] customIndicatorView] indicatorSize];
  469. } else {
  470. _dragIndicatorView = [HWPanIndicatorView new];
  471. }
  472. }
  473. return _dragIndicatorView;
  474. }
  475. @end