FLEXKeyboardToolbar.m 7.5 KB

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