UIViewController+LayoutHelper.m 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. //
  2. // UIViewController+LayoutHelper.m
  3. // HWPanModal
  4. //
  5. // Created by heath wang on 2019/4/26.
  6. //
  7. #import "UIViewController+LayoutHelper.h"
  8. #import "HWPanModalPresentationController.h"
  9. #import "UIViewController+PanModalDefault.h"
  10. @implementation UIViewController (LayoutHelper)
  11. - (CGFloat)topLayoutOffset {
  12. if (@available(iOS 11, *)) {
  13. return [UIApplication sharedApplication].keyWindow.safeAreaInsets.top;
  14. } else {
  15. return [UIApplication sharedApplication].keyWindow.rootViewController.topLayoutGuide.length;
  16. }
  17. }
  18. - (CGFloat)bottomLayoutOffset {
  19. if (@available(iOS 11, *)) {
  20. return [UIApplication sharedApplication].keyWindow.safeAreaInsets.bottom;
  21. } else {
  22. return [UIApplication sharedApplication].keyWindow.rootViewController.bottomLayoutGuide.length;
  23. }
  24. }
  25. - (HWPanModalPresentationController *)hw_presentedVC {
  26. /*
  27. * Fix iOS13 bug: if we access presentationController before present VC, this will lead `modalPresentationStyle` not working.
  28. * refer to: https://github.com/HeathWang/HWPanModal/issues/27
  29. * Apple Doc: If you have not yet presented the current view controller, accessing this property creates a presentation controller based on the current value in the modalPresentationStyle property.
  30. */
  31. /**
  32. * fix bug: https://github.com/HeathWang/HWPanModal/issues/37
  33. */
  34. if (self.presentingViewController) {
  35. return [self hw_getPanModalPresentationController];
  36. } else {
  37. return nil;
  38. }
  39. }
  40. - (HWPanModalPresentationController *)hw_getPanModalPresentationController {
  41. UIViewController *ancestorsVC;
  42. // seeking for the root presentation VC.
  43. if (self.splitViewController) {
  44. ancestorsVC = self.splitViewController;
  45. } else if (self.navigationController) {
  46. ancestorsVC = self.navigationController;
  47. } else if (self.tabBarController) {
  48. ancestorsVC = self.tabBarController;
  49. } else {
  50. ancestorsVC = self;
  51. }
  52. if ([ancestorsVC.presentationController isMemberOfClass:HWPanModalPresentationController.class]) {
  53. return (HWPanModalPresentationController *) ancestorsVC.presentationController;
  54. }
  55. return nil;
  56. }
  57. /**
  58. Returns the short form Y postion
  59. - Note: If voiceover is on, the `longFormYPos` is returned.
  60. We do not support short form when voiceover is on as it would make it difficult for user to navigate.
  61. */
  62. - (CGFloat)shortFormYPos {
  63. if (UIAccessibilityIsVoiceOverRunning()) {
  64. return self.longFormYPos;
  65. } else {
  66. CGFloat shortFormYPos = [self topMarginFromPanModalHeight:[self shortFormHeight]] + [self topOffset];
  67. return MAX(shortFormYPos, self.longFormYPos);
  68. }
  69. }
  70. - (CGFloat)mediumFormYPos {
  71. if (UIAccessibilityIsVoiceOverRunning()) {
  72. return self.longFormYPos;
  73. } else {
  74. CGFloat mediumFormYPos = [self topMarginFromPanModalHeight:[self mediumFormHeight]] + [self topOffset];
  75. return MAX(mediumFormYPos, self.longFormYPos);
  76. }
  77. }
  78. - (CGFloat)longFormYPos {
  79. return MAX([self topMarginFromPanModalHeight:[self longFormHeight]], [self topMarginFromPanModalHeight:PanModalHeightMake(PanModalHeightTypeMax, 0)]) + [self topOffset];
  80. }
  81. /**
  82. * Use the container view for relative positioning as this view's frame
  83. is adjusted in PanModalPresentationController
  84. */
  85. - (CGFloat)bottomYPos {
  86. if (self.hw_presentedVC.containerView) {
  87. return self.hw_presentedVC.containerView.bounds.size.height - [self topOffset];
  88. }
  89. return self.view.bounds.size.height;
  90. }
  91. - (CGFloat)topMarginFromPanModalHeight:(PanModalHeight)panModalHeight {
  92. switch (panModalHeight.heightType) {
  93. case PanModalHeightTypeMax:
  94. return 0.0f;
  95. case PanModalHeightTypeMaxTopInset:
  96. return panModalHeight.height;
  97. case PanModalHeightTypeContent:
  98. return self.bottomYPos - (panModalHeight.height + self.bottomLayoutOffset);
  99. case PanModalHeightTypeContentIgnoringSafeArea:
  100. return self.bottomYPos - panModalHeight.height;
  101. case PanModalHeightTypeIntrinsic:
  102. {
  103. [self.view layoutIfNeeded];
  104. CGSize targetSize = CGSizeMake(self.hw_presentedVC.containerView ? self.hw_presentedVC.containerView.bounds.size.width : [UIScreen mainScreen].bounds.size.width, UILayoutFittingCompressedSize.height);
  105. CGFloat intrinsicHeight = [self.view systemLayoutSizeFittingSize:targetSize].height;
  106. return self.bottomYPos - (intrinsicHeight + self.bottomLayoutOffset);
  107. }
  108. default:
  109. return 0;
  110. }
  111. }
  112. @end