HWPanModalContentView.m 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. //
  2. // HWPanModalContentView.m
  3. // Pods
  4. //
  5. // Created by heath wang on 2019/10/17.
  6. //
  7. #import "HWPanModalContentView.h"
  8. #import "HWPanModalContainerView.h"
  9. @interface HWPanModalContentView ()
  10. @property (nonatomic, weak) HWPanModalContainerView *containerView;
  11. @end
  12. @implementation HWPanModalContentView
  13. #pragma mark - public method
  14. - (void)presentInView:(UIView *)view {
  15. if (!view) {
  16. view = [self findKeyWindow];
  17. }
  18. HWPanModalContainerView *containerView = [[HWPanModalContainerView alloc] initWithPresentingView:view contentView:self];
  19. [containerView show];
  20. }
  21. - (void)dismissAnimated:(BOOL)flag completion:(void (^)(void))completion {
  22. [self.containerView dismissAnimated:flag completion:completion];
  23. }
  24. #pragma mark - HWPanModalPresentationUpdateProtocol
  25. - (void)hw_panModalTransitionTo:(PresentationState)state {
  26. [self.containerView transitionToState:state animated:YES];
  27. }
  28. - (void)hw_panModalSetContentOffset:(CGPoint)offset {
  29. [self.containerView setScrollableContentOffset:offset animated:YES];
  30. }
  31. - (void)hw_panModalSetNeedsLayoutUpdate {
  32. [self.containerView setNeedsLayoutUpdate];
  33. }
  34. - (void)hw_panModalUpdateUserHitBehavior {
  35. [self.containerView updateUserHitBehavior];
  36. }
  37. - (void)hw_panModalTransitionTo:(PresentationState)state animated:(BOOL)animated {
  38. [self.containerView transitionToState:state animated:animated];
  39. }
  40. - (void)hw_panModalSetContentOffset:(CGPoint)offset animated:(BOOL)animated {
  41. [self.containerView setScrollableContentOffset:offset animated:animated];
  42. }
  43. - (void)hw_dismissAnimated:(BOOL)animated completion:(void (^)(void))completion {
  44. [self dismissAnimated:animated completion:completion];
  45. }
  46. - (HWDimmedView *)hw_dimmedView {
  47. return self.containerView.backgroundView;
  48. }
  49. - (UIView *)hw_rootContainerView {
  50. return self.containerView;
  51. }
  52. - (UIView *)hw_contentView {
  53. return (UIView *)self.containerView.panContainerView;
  54. }
  55. - (PresentationState)hw_presentationState {
  56. return self.containerView.currentPresentationState;
  57. }
  58. #pragma mark - HWPanModalPresentable
  59. - (UIScrollView *)panScrollable {
  60. return nil;
  61. }
  62. - (CGFloat)topOffset {
  63. return self.topLayoutOffset + 21.f;
  64. }
  65. - (PanModalHeight)shortFormHeight {
  66. return [self longFormHeight];
  67. }
  68. - (PanModalHeight)mediumFormHeight {
  69. return [self longFormHeight];
  70. }
  71. - (PanModalHeight)longFormHeight {
  72. if ([self panScrollable]) {
  73. [[self panScrollable] layoutIfNeeded];
  74. return PanModalHeightMake(PanModalHeightTypeContent, MAX([self panScrollable].contentSize.height, [self panScrollable].bounds.size.height));
  75. } else {
  76. return PanModalHeightMake(PanModalHeightTypeMax, 0);
  77. }
  78. }
  79. - (PresentationState)originPresentationState {
  80. return PresentationStateShort;
  81. }
  82. - (CGFloat)springDamping {
  83. return 0.8;
  84. }
  85. - (NSTimeInterval)transitionDuration {
  86. return 0.5;
  87. }
  88. - (NSTimeInterval)dismissalDuration {
  89. return [self transitionDuration];
  90. }
  91. - (UIViewAnimationOptions)transitionAnimationOptions {
  92. return UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState;
  93. }
  94. - (CGFloat)backgroundAlpha {
  95. return 0.7;
  96. }
  97. - (CGFloat)backgroundBlurRadius {
  98. return 0;
  99. }
  100. - (nonnull UIColor *)backgroundBlurColor {
  101. return [UIColor whiteColor];
  102. }
  103. - (HWBackgroundConfig *)backgroundConfig {
  104. return [HWBackgroundConfig configWithBehavior:HWBackgroundBehaviorDefault];
  105. }
  106. - (UIEdgeInsets)scrollIndicatorInsets {
  107. CGFloat top = [self shouldRoundTopCorners] ? [self cornerRadius] : 0;
  108. return UIEdgeInsetsMake(top, 0, self.bottomLayoutOffset, 0);
  109. }
  110. - (BOOL)showsScrollableVerticalScrollIndicator {
  111. return YES;
  112. }
  113. - (BOOL)shouldAutoSetPanScrollContentInset {
  114. return YES;
  115. }
  116. - (BOOL)anchorModalToLongForm {
  117. return YES;
  118. }
  119. - (BOOL)allowsExtendedPanScrolling {
  120. if ([self panScrollable]) {
  121. UIScrollView *scrollable = [self panScrollable];
  122. [scrollable layoutIfNeeded];
  123. return scrollable.contentSize.height > (scrollable.frame.size.height - self.bottomLayoutOffset);
  124. } else {
  125. return NO;
  126. }
  127. }
  128. - (BOOL)allowsDragToDismiss {
  129. return YES;
  130. }
  131. - (CGFloat)minVerticalVelocityToTriggerDismiss {
  132. return 300;
  133. }
  134. - (BOOL)allowsTapBackgroundToDismiss {
  135. return YES;
  136. }
  137. - (BOOL)allowsPullDownWhenShortState {
  138. return YES;
  139. }
  140. - (BOOL)allowScreenEdgeInteractive {
  141. return NO;
  142. }
  143. - (CGFloat)maxAllowedDistanceToLeftScreenEdgeForPanInteraction {
  144. return 0;
  145. }
  146. - (CGFloat)minHorizontalVelocityToTriggerScreenEdgeDismiss {
  147. return 500;
  148. }
  149. - (PresentingViewControllerAnimationStyle)presentingVCAnimationStyle {
  150. return PresentingViewControllerAnimationStyleNone;
  151. }
  152. - (BOOL)shouldAnimatePresentingVC {
  153. return NO;
  154. }
  155. - (id <HWPresentingViewControllerAnimatedTransitioning>)customPresentingVCAnimation {
  156. return nil;
  157. }
  158. - (BOOL)isPanScrollEnabled {
  159. return YES;
  160. }
  161. - (BOOL)isUserInteractionEnabled {
  162. return YES;
  163. }
  164. - (BOOL)isHapticFeedbackEnabled {
  165. return YES;
  166. }
  167. - (BOOL)allowsTouchEventsPassingThroughTransitionView {
  168. return NO;
  169. }
  170. - (BOOL)shouldRoundTopCorners {
  171. return YES;
  172. }
  173. - (CGFloat)cornerRadius {
  174. return 8;
  175. }
  176. - (HWPanModalShadow *)contentShadow {
  177. return [HWPanModalShadow panModalShadowNil];
  178. }
  179. - (BOOL)showDragIndicator {
  180. if ([self allowsTouchEventsPassingThroughTransitionView]) {
  181. return NO;
  182. }
  183. return [self shouldRoundTopCorners];
  184. }
  185. - (nullable UIView <HWPanModalIndicatorProtocol> *)customIndicatorView {
  186. return nil;
  187. }
  188. - (BOOL)isAutoHandleKeyboardEnabled {
  189. return YES;
  190. }
  191. - (CGFloat)keyboardOffsetFromInputView {
  192. return 5;
  193. }
  194. - (BOOL)shouldRespondToPanModalGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer {
  195. return YES;
  196. }
  197. - (void)willRespondToPanModalGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer {
  198. }
  199. - (void)didRespondToPanModalGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer {
  200. }
  201. - (void)didEndRespondToPanModalGestureRecognizer:(nonnull UIPanGestureRecognizer *)panGestureRecognizer {
  202. }
  203. - (BOOL)shouldPrioritizePanModalGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer {
  204. return NO;
  205. }
  206. - (BOOL)shouldTransitionToState:(PresentationState)state {
  207. return YES;
  208. }
  209. - (void)willTransitionToState:(PresentationState)state {
  210. }
  211. - (void)didChangeTransitionToState:(PresentationState)state {
  212. }
  213. - (void)panModalGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer dismissPercent:(CGFloat)percent {
  214. }
  215. - (void)panModalWillDismiss {
  216. }
  217. - (void)panModalDidDismissed {
  218. }
  219. - (void)panModalTransitionWillBegin {
  220. }
  221. - (void)panModalTransitionDidFinish {
  222. }
  223. - (void)presentedViewDidMoveToSuperView {
  224. }
  225. - (BOOL)shouldEnableAppearanceTransition {
  226. return YES;
  227. }
  228. #pragma mark - HWPanModalPresentableLayoutProtocol
  229. - (CGFloat)topLayoutOffset {
  230. return 0;
  231. }
  232. - (CGFloat)bottomLayoutOffset {
  233. return 0;
  234. }
  235. - (CGFloat)shortFormYPos {
  236. CGFloat shortFormYPos = [self topMarginFromPanModalHeight:[self shortFormHeight]] + [self topOffset];
  237. return MAX(shortFormYPos, self.longFormYPos);
  238. }
  239. - (CGFloat)mediumFormYPos {
  240. CGFloat mediumFormYPos = [self topMarginFromPanModalHeight:[self mediumFormHeight]] + [self topOffset];
  241. return MAX(mediumFormYPos, self.longFormYPos);
  242. }
  243. - (CGFloat)longFormYPos {
  244. CGFloat longFrom = MAX([self topMarginFromPanModalHeight:[self longFormHeight]], [self topMarginFromPanModalHeight:PanModalHeightMake(PanModalHeightTypeMax, 0)]) + [self topOffset];
  245. return longFrom;
  246. }
  247. - (CGFloat)bottomYPos {
  248. if (self.containerView) {
  249. return self.containerView.bounds.size.height - [self topOffset];
  250. }
  251. return self.bounds.size.height;
  252. }
  253. - (CGFloat)topMarginFromPanModalHeight:(PanModalHeight)panModalHeight {
  254. switch (panModalHeight.heightType) {
  255. case PanModalHeightTypeMax:
  256. return 0.0f;
  257. case PanModalHeightTypeMaxTopInset:
  258. return panModalHeight.height;
  259. case PanModalHeightTypeContent:
  260. return self.bottomYPos - (panModalHeight.height + self.bottomLayoutOffset);
  261. case PanModalHeightTypeContentIgnoringSafeArea:
  262. return self.bottomYPos - panModalHeight.height;
  263. case PanModalHeightTypeIntrinsic: {
  264. [self layoutIfNeeded];
  265. CGSize targetSize = CGSizeMake(self.containerView ? self.containerView.bounds.size.width : [UIScreen mainScreen].bounds.size.width, UILayoutFittingCompressedSize.height);
  266. CGFloat intrinsicHeight = [self systemLayoutSizeFittingSize:targetSize].height;
  267. return self.bottomYPos - (intrinsicHeight + self.bottomLayoutOffset);
  268. }
  269. default:
  270. return 0;
  271. }
  272. }
  273. #pragma mark - Getter
  274. - (HWPanModalContainerView *)containerView {
  275. // we assume the container view will not change after we got it.
  276. if (!_containerView) {
  277. UIView *fatherView = self.superview;
  278. while (fatherView) {
  279. if ([fatherView isKindOfClass:HWPanModalContainerView.class]) {
  280. _containerView = (HWPanModalContainerView *) fatherView;
  281. break;
  282. }
  283. fatherView = fatherView.superview;
  284. }
  285. }
  286. return _containerView;
  287. }
  288. - (UIView *)findKeyWindow {
  289. if (@available(iOS 13.0, *)) {
  290. NSSet<UIScene *> *connectedScenes = [UIApplication sharedApplication].connectedScenes;
  291. for (UIScene *scene in connectedScenes) {
  292. if ([scene isKindOfClass:UIWindowScene.class]) {
  293. UIWindowScene *windowScene = (UIWindowScene *)scene;
  294. for (UIWindow *tmpWindow in windowScene.windows) {
  295. if ([tmpWindow isKeyWindow]) {
  296. return tmpWindow;
  297. }
  298. }
  299. }
  300. }
  301. } else {
  302. NSArray *windows = [UIApplication sharedApplication].windows;
  303. for (UIWindow *window in windows) {
  304. if ([window isKeyWindow]) {
  305. return window;
  306. }
  307. }
  308. }
  309. return nil;
  310. }
  311. @end