TUIFloatViewController.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. //
  2. // TUIFloatTitleView.m
  3. // TUI
  4. //
  5. // Created by wyl on 2023/1/16.
  6. // Copyright © 2023 Tencent. All rights reserved.
  7. //
  8. #import "TUIFloatViewController.h"
  9. #import <TIMCommon/TIMDefine.h>
  10. typedef enum : NSUInteger {
  11. FLEX_TOP,
  12. FLEX_Bottom,
  13. } FLEX_Location;
  14. @interface TUIFloatTitleView ()
  15. @end
  16. @implementation TUIFloatTitleView
  17. - (instancetype)initWithFrame:(CGRect)frame {
  18. self = [super initWithFrame:frame];
  19. if (self) {
  20. [self setupView];
  21. }
  22. return self;
  23. }
  24. - (void)setupView {
  25. self.titleLabel = [[UILabel alloc] init];
  26. self.titleLabel.text = @"";
  27. self.titleLabel.font = [UIFont boldSystemFontOfSize:kScale390(20)];
  28. [self addSubview:self.titleLabel];
  29. self.subTitleLabel = [[UILabel alloc] init];
  30. self.subTitleLabel.text = @"";
  31. self.subTitleLabel.font = [UIFont systemFontOfSize:kScale390(12)];
  32. self.subTitleLabel.tintColor = [UIColor grayColor];
  33. [self addSubview:self.subTitleLabel];
  34. self.leftButton = [UIButton buttonWithType:UIButtonTypeCustom];
  35. [self addSubview:self.leftButton];
  36. [self.leftButton setTitle:TIMCommonLocalizableString(TUIKitCreateCancel) forState:UIControlStateNormal];
  37. self.leftButton.titleLabel.font = [UIFont systemFontOfSize:kScale390(16)];
  38. [self.leftButton addTarget:self action:@selector(leftButtonClick) forControlEvents:UIControlEventTouchUpInside];
  39. [self.leftButton setTitleColor:[UIColor tui_colorWithHex:@"#0365F9"] forState:UIControlStateNormal];
  40. self.rightButton = [UIButton buttonWithType:UIButtonTypeCustom];
  41. [self addSubview:self.rightButton];
  42. [self.rightButton setTitle:TIMCommonLocalizableString(TUIKitCreateNext) forState:UIControlStateNormal];
  43. self.rightButton.titleLabel.font = [UIFont systemFontOfSize:kScale390(16)];
  44. [self.rightButton addTarget:self action:@selector(rightButtonClick) forControlEvents:UIControlEventTouchUpInside];
  45. [self.rightButton setTitleColor:[UIColor tui_colorWithHex:@"#0365F9"] forState:UIControlStateNormal];
  46. }
  47. - (void)layoutSubviews {
  48. [super layoutSubviews];
  49. [self.titleLabel sizeToFit];
  50. [self.subTitleLabel sizeToFit];
  51. if (self.subTitleLabel.isHidden || self.subTitleLabel.text.length == 0) {
  52. self.titleLabel.frame = CGRectMake((self.frame.size.width - self.titleLabel.frame.size.width) * 0.5, kScale390(23.5), self.titleLabel.frame.size.width,
  53. self.titleLabel.frame.size.height);
  54. } else {
  55. self.titleLabel.frame = CGRectMake((self.frame.size.width - self.titleLabel.frame.size.width) * 0.5, kScale390(17.5), self.titleLabel.frame.size.width,
  56. self.titleLabel.frame.size.height);
  57. self.subTitleLabel.frame = CGRectMake((self.frame.size.width - self.subTitleLabel.frame.size.width) * 0.5,
  58. self.titleLabel.frame.origin.y + self.titleLabel.frame.size.height + kScale390(1),
  59. self.subTitleLabel.frame.size.width, self.subTitleLabel.frame.size.height);
  60. }
  61. [self.leftButton sizeToFit];
  62. self.leftButton.frame = CGRectMake(kScale390(15), kScale390(23.5), self.leftButton.frame.size.width, self.leftButton.frame.size.height);
  63. [self.rightButton sizeToFit];
  64. self.rightButton.frame = CGRectMake(self.frame.size.width - self.rightButton.frame.size.width - kScale390(14), kScale390(23.5),
  65. self.rightButton.frame.size.width, self.rightButton.frame.size.height);
  66. if (isRTL()){
  67. [self.leftButton resetFrameToFitRTL];
  68. [self.rightButton resetFrameToFitRTL];
  69. }
  70. }
  71. - (void)leftButtonClick {
  72. if (self.leftButtonClickCallback) {
  73. self.leftButtonClickCallback();
  74. }
  75. }
  76. - (void)rightButtonClick {
  77. if (self.rightButtonClickCallback) {
  78. self.rightButtonClickCallback();
  79. }
  80. }
  81. - (void)setTitleText:(NSString *)mainText subTitleText:(NSString *)secondText leftBtnText:(NSString *)leftBtnText rightBtnText:(NSString *)rightBtnText {
  82. self.titleLabel.text = mainText;
  83. self.subTitleLabel.text = secondText;
  84. [self.leftButton setTitle:leftBtnText forState:UIControlStateNormal];
  85. [self.rightButton setTitle:rightBtnText forState:UIControlStateNormal];
  86. }
  87. @end
  88. @interface TUIFloatViewController ()
  89. @property(nonatomic, assign) CGFloat topMargin;
  90. @property(nonatomic, assign) FLEX_Location currentLoaction;
  91. @property(nonatomic, strong) UIPanGestureRecognizer *panCover;
  92. @property(nonatomic, strong) UITapGestureRecognizer *singleTap;
  93. @end
  94. @implementation TUIFloatViewController
  95. - (void)appendChildViewController:(UIViewController<TUIFloatSubViewControllerProtocol> *)vc topMargin:(CGFloat)topMargin {
  96. self.childVC = vc;
  97. self.topMargin = topMargin;
  98. [self addChildViewController:vc];
  99. [self.containerView addSubview:vc.view];
  100. }
  101. - (void)viewDidLoad {
  102. [super viewDidLoad];
  103. self.view.backgroundColor = [UIColor tui_colorWithHex:@"#000000" alpha:0.6];
  104. self.modalPresentationStyle = UIModalPresentationCustom;
  105. self.containerView.backgroundColor = [UIColor whiteColor];
  106. self.topImgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:TIMCommonImagePath(@"icon_flex_arrow")]];
  107. [self.topGestureView addSubview:self.topImgView];
  108. self.topImgView.hidden = YES;
  109. @weakify(self);
  110. self.topGestureView.leftButtonClickCallback = ^{
  111. @strongify(self);
  112. if ([self.childVC respondsToSelector:@selector(floatControllerLeftButtonClick)]) {
  113. [self.childVC performSelector:@selector(floatControllerLeftButtonClick)];
  114. }
  115. };
  116. self.topGestureView.rightButtonClickCallback = ^{
  117. @strongify(self);
  118. if ([self.childVC respondsToSelector:@selector(floatControllerRightButtonClick)]) {
  119. [self.childVC performSelector:@selector(floatControllerRightButtonClick)];
  120. }
  121. };
  122. [self addSingleTapGesture];
  123. if (!_currentLoaction) {
  124. self.currentLoaction = FLEX_TOP;
  125. }
  126. [self updateSubContainerView];
  127. }
  128. - (void)addSingleTapGesture {
  129. // When clicking on the shadow, the page disappears
  130. self.view.userInteractionEnabled = YES;
  131. UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
  132. singleTap.cancelsTouchesInView = NO;
  133. [self.view addGestureRecognizer:singleTap];
  134. }
  135. - (void)singleTap:(UITapGestureRecognizer *)tap {
  136. CGPoint translation = [tap locationInView:self.containerView];
  137. if (translation.x < 0 || translation.y < 0) {
  138. [self dismissViewControllerAnimated:YES completion:nil];
  139. } else if (translation.x > self.containerView.frame.size.width || translation.y > self.containerView.frame.size.height) {
  140. [self dismissViewControllerAnimated:YES completion:nil];
  141. }
  142. }
  143. - (void)setnormalTop {
  144. self.currentLoaction = FLEX_TOP;
  145. }
  146. - (void)setNormalBottom {
  147. self.currentLoaction = FLEX_Bottom;
  148. }
  149. - (void)setCurrentLoaction:(FLEX_Location)currentLoaction {
  150. _currentLoaction = currentLoaction;
  151. if (currentLoaction == FLEX_TOP) {
  152. self.containerView.frame = CGRectMake(0, self.topMargin, self.view.frame.size.width, self.view.frame.size.height - self.topMargin);
  153. } else if (currentLoaction == FLEX_Bottom) {
  154. self.containerView.frame = CGRectMake(0, self.view.frame.size.height - kScale390(393), self.view.frame.size.width, kScale390(393));
  155. }
  156. }
  157. - (void)floatDismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
  158. [self dismissViewControllerAnimated:flag completion:completion];
  159. }
  160. #pragma mark - lazy
  161. - (UIView *)containerView {
  162. if (_containerView == nil) {
  163. _containerView = [[UIView alloc] init];
  164. _containerView.layer.cornerRadius = kScale390(12);
  165. [self.view addSubview:_containerView];
  166. }
  167. return _containerView;
  168. }
  169. - (UIView *)topGestureView {
  170. if (_topGestureView == nil) {
  171. _topGestureView = [[TUIFloatTitleView alloc] init];
  172. // [_topGestureView addGestureRecognizer:self.panCover];
  173. [self.containerView addSubview:_topGestureView];
  174. }
  175. return _topGestureView;
  176. }
  177. - (UIPanGestureRecognizer *)panCover {
  178. if (_panCover == nil) {
  179. _panCover = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onPanCover:)];
  180. }
  181. return _panCover;
  182. }
  183. - (void)onPanCover:(UIPanGestureRecognizer *)pan {
  184. CGPoint translation = [pan translationInView:self.topGestureView];
  185. CGFloat absX = fabs(translation.x);
  186. CGFloat absY = fabs(translation.y);
  187. if (MAX(absX, absY) < 2) return;
  188. if (absX > absY) {
  189. if (translation.x < 0) {
  190. // scroll left
  191. } else {
  192. // scroll right
  193. }
  194. } else if (absY > absX) {
  195. if (translation.y < 0) {
  196. // scroll up
  197. [self.topGestureView removeGestureRecognizer:self.panCover];
  198. [UIView animateWithDuration:0.3
  199. animations:^{
  200. self.currentLoaction = FLEX_TOP;
  201. [self.topGestureView addGestureRecognizer:self.panCover];
  202. }
  203. completion:^(BOOL finished) {
  204. if (finished) {
  205. [self updateSubContainerView];
  206. }
  207. }];
  208. } else {
  209. // scroll down
  210. if (self.currentLoaction == FLEX_Bottom) {
  211. [self dismissViewControllerAnimated:YES completion:nil];
  212. }
  213. [self.topGestureView removeGestureRecognizer:self.panCover];
  214. [UIView animateWithDuration:0.3
  215. animations:^{
  216. self.currentLoaction = FLEX_Bottom;
  217. [self.topGestureView addGestureRecognizer:self.panCover];
  218. }
  219. completion:^(BOOL finished) {
  220. if (finished) {
  221. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  222. [self updateSubContainerView];
  223. });
  224. }
  225. }];
  226. }
  227. }
  228. }
  229. - (void)updateSubContainerView {
  230. self.topGestureView.frame = CGRectMake(0, 0, self.containerView.frame.size.width, kScale390(68.5));
  231. self.topImgView.frame = CGRectMake((self.topGestureView.frame.size.width - kScale390(24)) * 0.5, kScale390(22), kScale390(24), kScale390(6));
  232. self.childVC.view.frame = CGRectMake(0, self.topGestureView.frame.origin.y + self.topGestureView.frame.size.height, self.containerView.frame.size.width,
  233. self.containerView.frame.size.height - self.topGestureView.frame.size.height);
  234. }
  235. @end