TBKeyboardToolbar.m 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. //
  2. // FLEXKeyboardToolbar.m
  3. //
  4. // Created by Tanner on 6/11/17.
  5. //
  6. #import "TBKeyboardToolbar.h"
  7. #import "FLEXUtility.h"
  8. #define kToolbarHeight 44
  9. #define kButtonSpacing 6
  10. #define kScrollViewHorizontalMargins 3
  11. @interface TBKeyboardToolbar ()
  12. /// The fake top border to replicate the toolbar.
  13. @property (nonatomic) CALayer *topBorder;
  14. @property (nonatomic) UIView *toolbarView;
  15. @property (nonatomic) UIScrollView *scrollView;
  16. @property (nonatomic) UIVisualEffectView *blurView;
  17. /// YES if appearance is set to `default`
  18. @property (nonatomic, readonly) BOOL useSystemAppearance;
  19. /// YES if the current trait collection is set to dark mode and \c useSystemAppearance is YES
  20. @property (nonatomic, readonly) BOOL usingDarkMode;
  21. @end
  22. @implementation TBKeyboardToolbar
  23. + (instancetype)toolbarWithButtons:(NSArray *)buttons {
  24. return [[self alloc] initWithButtons:buttons];
  25. }
  26. - (id)initWithButtons:(NSArray *)buttons {
  27. self = [super initWithFrame:CGRectMake(0, 0, self.window.rootViewController.view.bounds.size.width, kToolbarHeight)];
  28. if (self) {
  29. _buttons = [buttons copy];
  30. self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  31. if (@available(iOS 13, *)) {
  32. self.appearance = UIKeyboardTypeDefault;
  33. } else {
  34. self.appearance = UIKeyboardAppearanceLight;
  35. }
  36. }
  37. return self;
  38. }
  39. - (void)setAppearance:(UIKeyboardAppearance)appearance {
  40. _appearance = appearance;
  41. // Remove toolbar if it exits because it will be recreated below
  42. if (self.toolbarView) {
  43. [self.toolbarView removeFromSuperview];
  44. }
  45. [self addSubview:self.inputAccessoryView];
  46. }
  47. - (void)layoutSubviews {
  48. [super layoutSubviews];
  49. // Layout top border
  50. CGRect frame = _toolbarView.bounds;
  51. frame.size.height = 0.5;
  52. _topBorder.frame = frame;
  53. // Scroll view //
  54. frame = CGRectMake(0, 0, self.bounds.size.width, kToolbarHeight);
  55. CGSize contentSize = self.scrollView.contentSize;
  56. CGFloat scrollViewWidth = frame.size.width;
  57. // If our content size is smaller than the scroll view,
  58. // we want to right-align all the content
  59. if (contentSize.width < scrollViewWidth) {
  60. // Compute the content size to scroll view size difference
  61. UIEdgeInsets insets = self.scrollView.contentInset;
  62. CGFloat margin = insets.left + insets.right;
  63. CGFloat difference = scrollViewWidth - contentSize.width - margin;
  64. // Update the content size to be the full width of the scroll view
  65. contentSize.width += difference;
  66. self.scrollView.contentSize = contentSize;
  67. // Offset every button by the difference above
  68. // so that every button appears right-aligned
  69. for (UIView *button in self.scrollView.subviews) {
  70. CGRect f = button.frame;
  71. f.origin.x += difference;
  72. button.frame = f;
  73. }
  74. }
  75. }
  76. - (UIView *)inputAccessoryView {
  77. _topBorder = [CALayer layer];
  78. _topBorder.frame = CGRectMake(0.0, 0.0, self.bounds.size.width, 0.5);
  79. [self makeScrollView];
  80. UIColor *borderColor = nil, *backgroundColor = nil;
  81. UIColor *lightColor = [UIColor colorWithHue:216.0/360.0 saturation:0.05 brightness:0.85 alpha:1];
  82. UIColor *darkColor = [UIColor colorWithHue:220.0/360.0 saturation:0.07 brightness:0.16 alpha:1];
  83. switch (_appearance) {
  84. case UIKeyboardAppearanceDefault:
  85. #if FLEX_AT_LEAST_IOS13_SDK
  86. if (@available(iOS 13, *)) {
  87. borderColor = UIColor.systemBackgroundColor;
  88. if (self.usingDarkMode) {
  89. // style = UIBlurEffectStyleSystemThickMaterial;
  90. backgroundColor = darkColor;
  91. } else {
  92. // style = UIBlurEffectStyleSystemUltraThinMaterialLight;
  93. backgroundColor = lightColor;
  94. }
  95. break;
  96. }
  97. #endif
  98. case UIKeyboardAppearanceLight: {
  99. borderColor = UIColor.clearColor;
  100. backgroundColor = lightColor;
  101. break;
  102. }
  103. case UIKeyboardAppearanceDark: {
  104. borderColor = [UIColor colorWithWhite:0.100 alpha:1.000];
  105. backgroundColor = darkColor;
  106. break;
  107. }
  108. }
  109. self.toolbarView = [UIView new];
  110. [self.toolbarView addSubview:self.scrollView];
  111. [self.toolbarView.layer addSublayer:self.topBorder];
  112. self.toolbarView.frame = CGRectMake(0, 0, self.bounds.size.width, kToolbarHeight);
  113. self.toolbarView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  114. self.backgroundColor = backgroundColor;
  115. self.topBorder.backgroundColor = borderColor.CGColor;
  116. return self.toolbarView;
  117. }
  118. - (UIScrollView *)makeScrollView {
  119. UIScrollView *scrollView = [UIScrollView new];
  120. scrollView.backgroundColor = UIColor.clearColor;
  121. scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  122. scrollView.contentInset = UIEdgeInsetsMake(
  123. 8.f, kScrollViewHorizontalMargins, 4.f, kScrollViewHorizontalMargins
  124. );
  125. scrollView.showsHorizontalScrollIndicator = NO;
  126. self.scrollView = scrollView;
  127. [self addButtons];
  128. return scrollView;
  129. }
  130. - (void)addButtons {
  131. NSUInteger originX = 0.f;
  132. CGRect originFrame;
  133. CGFloat top = self.scrollView.contentInset.top;
  134. CGFloat bottom = self.scrollView.contentInset.bottom;
  135. for (TBToolbarButton *button in self.buttons) {
  136. button.appearance = self.appearance;
  137. originFrame = button.frame;
  138. originFrame.origin.x = originX;
  139. originFrame.origin.y = 0.f;
  140. originFrame.size.height = kToolbarHeight - (top + bottom);
  141. button.frame = originFrame;
  142. [self.scrollView addSubview:button];
  143. // originX tracks the origin of the next button to be added,
  144. // so at the end of each iteration of this loop we increment
  145. // it by the size of the last button with some padding
  146. originX += button.bounds.size.width + kButtonSpacing;
  147. }
  148. // Update contentSize,
  149. // set to the max x value of the last button added
  150. CGSize contentSize = self.scrollView.contentSize;
  151. contentSize.width = originX - kButtonSpacing;
  152. self.scrollView.contentSize = contentSize;
  153. // Needed to potentially right-align buttons
  154. [self setNeedsLayout];
  155. }
  156. - (void)setButtons:(NSArray<TBToolbarButton *> *)buttons {
  157. [_buttons makeObjectsPerformSelector:@selector(removeFromSuperview)];
  158. _buttons = buttons.copy;
  159. [self addButtons];
  160. }
  161. - (BOOL)useSystemAppearance {
  162. return self.appearance == UIKeyboardAppearanceDefault;
  163. }
  164. - (BOOL)usingDarkMode {
  165. if (@available(iOS 12, *)) {
  166. return self.useSystemAppearance && self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark;
  167. }
  168. return self.appearance == UIKeyboardAppearanceDark;
  169. }
  170. - (void)traitCollectionDidChange:(UITraitCollection *)previous {
  171. if (@available(iOS 12, *)) {
  172. // Was darkmode toggled?
  173. if (previous.userInterfaceStyle != self.traitCollection.userInterfaceStyle) {
  174. if (self.useSystemAppearance) {
  175. // Recreate the background view with the proper colors
  176. self.appearance = self.appearance;
  177. }
  178. }
  179. }
  180. }
  181. @end