TBKeyboardToolbar.m 6.9 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. @interface TBKeyboardToolbar ()
  10. /// The fake top border to replicate the toolbar.
  11. @property (nonatomic) CALayer *topBorder;
  12. @property (nonatomic) UIView *toolbarView;
  13. @property (nonatomic) UIScrollView *scrollView;
  14. @property (nonatomic) UIVisualEffectView *blurView;
  15. /// YES if appearance is set to `default`
  16. @property (nonatomic, readonly) BOOL useSystemAppearance;
  17. /// YES if the current trait collection is set to dark mode and \c useSystemAppearance is YES
  18. @property (nonatomic, readonly) BOOL usingDarkMode;
  19. @end
  20. @implementation TBKeyboardToolbar
  21. + (instancetype)toolbarWithButtons:(NSArray *)buttons {
  22. return [[self alloc] initWithButtons:buttons];
  23. }
  24. - (id)initWithButtons:(NSArray *)buttons {
  25. self = [super initWithFrame:CGRectMake(0, 0, self.window.rootViewController.view.bounds.size.width, kToolbarHeight)];
  26. if (self) {
  27. _buttons = [buttons copy];
  28. self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  29. if (@available(iOS 13, *)) {
  30. self.appearance = UIKeyboardTypeDefault;
  31. } else {
  32. self.appearance = UIKeyboardAppearanceLight;
  33. }
  34. }
  35. return self;
  36. }
  37. - (void)setAppearance:(UIKeyboardAppearance)appearance {
  38. _appearance = appearance;
  39. // Remove toolbar if it exits because it will be recreated below
  40. if (self.toolbarView) {
  41. [self.toolbarView removeFromSuperview];
  42. }
  43. [self addSubview:self.inputAccessoryView];
  44. }
  45. - (void)layoutSubviews {
  46. [super layoutSubviews];
  47. CGRect frame = _toolbarView.bounds;
  48. frame.size.height = 0.5f;
  49. _topBorder.frame = frame;
  50. }
  51. - (UIView *)inputAccessoryView {
  52. _topBorder = [CALayer layer];
  53. _topBorder.frame = CGRectMake(0.0f, 0.0f, self.bounds.size.width, 0.5f);
  54. UIColor *borderColor = nil;
  55. UIBlurEffectStyle style;
  56. switch (_appearance) {
  57. case UIKeyboardAppearanceDefault:
  58. #if FLEX_AT_LEAST_IOS13_SDK
  59. if (@available(iOS 13, *)) {
  60. borderColor = [UIColor systemBackgroundColor];
  61. if (self.usingDarkMode) {
  62. style = UIBlurEffectStyleSystemThickMaterial;
  63. self.backgroundColor = nil;
  64. } else {
  65. style = UIBlurEffectStyleSystemUltraThinMaterialLight;
  66. self.backgroundColor = [UIColor colorWithWhite:0.700 alpha:0.750];
  67. }
  68. break;
  69. }
  70. #endif
  71. case UIKeyboardAppearanceLight: {
  72. style = UIBlurEffectStyleLight;
  73. borderColor = [UIColor clearColor];
  74. break;
  75. }
  76. case UIKeyboardAppearanceDark: {
  77. style = UIBlurEffectStyleDark;
  78. borderColor = [UIColor colorWithWhite:0.100 alpha:1.000];
  79. break;
  80. }
  81. }
  82. UIVisualEffect *blur = [UIBlurEffect effectWithStyle:style];
  83. self.blurView = [[UIVisualEffectView alloc] initWithEffect:blur];
  84. [self.blurView.contentView.layer addSublayer:self.topBorder];
  85. [self.blurView.contentView addSubview:[self fakeToolbar]];
  86. self.toolbarView = self.blurView;
  87. self.toolbarView.frame = CGRectMake(0, 0, self.bounds.size.width, kToolbarHeight);
  88. self.toolbarView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  89. self.topBorder.backgroundColor = borderColor.CGColor;
  90. return self.toolbarView;
  91. }
  92. - (UIScrollView *)fakeToolbar {
  93. _scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, kToolbarHeight)];
  94. _scrollView.backgroundColor = [UIColor clearColor];
  95. _scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  96. _scrollView.contentInset = UIEdgeInsetsMake(8.0f, 0.0f, 4.0f, 6.0f);
  97. _scrollView.showsHorizontalScrollIndicator = NO;
  98. [self addButtons];
  99. return _scrollView;
  100. }
  101. - (void)addButtons {
  102. NSUInteger spacing = 6;
  103. NSUInteger originX = spacing;
  104. CGRect originFrame;
  105. CGFloat top = _scrollView.contentInset.top;
  106. CGFloat bottom = _scrollView.contentInset.bottom;
  107. for (TBToolbarButton *button in _buttons) {
  108. button.appearance = self.appearance;
  109. originFrame = button.frame;
  110. originFrame.origin.x = originX;
  111. originFrame.origin.y = 0;
  112. originFrame.size.height = kToolbarHeight - (top + bottom);
  113. button.frame = originFrame;
  114. [_scrollView addSubview:button];
  115. originX += button.bounds.size.width + spacing;
  116. }
  117. CGSize contentSize = _scrollView.contentSize;
  118. contentSize.width = originX - spacing;
  119. _scrollView.contentSize = contentSize;
  120. }
  121. - (void)setButtons:(NSArray<TBToolbarButton *> *)buttons {
  122. [_buttons makeObjectsPerformSelector:@selector(removeFromSuperview)];
  123. _buttons = buttons.copy;
  124. [self addButtons];
  125. }
  126. - (void)setButtons:(NSArray<TBToolbarButton *> *)buttons animated:(BOOL)animated {
  127. if (!animated) {
  128. self.buttons = buttons;
  129. return;
  130. }
  131. NSMutableSet *buttonstoRemove = [NSMutableSet setWithArray:_buttons];
  132. [buttonstoRemove minusSet:[NSSet setWithArray:buttons]];
  133. NSMutableSet *buttonsToAdd = [NSMutableSet setWithArray:buttons];
  134. [buttonsToAdd minusSet:[NSSet setWithArray:_buttons]];
  135. if (!buttonstoRemove.count && !buttonsToAdd.count) {
  136. return;
  137. }
  138. // New buttons are invisible at first
  139. for (TBToolbarButton *button in buttons) {
  140. button.alpha = 0;
  141. }
  142. [UIView animateWithDuration:0.1 animations:^{
  143. // Fade out old buttons
  144. for (TBToolbarButton *button in _buttons) {
  145. button.alpha = 0;
  146. }
  147. } completion:^(BOOL finished) {
  148. // Remove old, add new
  149. self.buttons = buttons;
  150. [UIView animateWithDuration:0.1 animations:^{
  151. // Fade in new buttons
  152. for (TBToolbarButton *button in buttons) {
  153. button.alpha = 1;
  154. }
  155. }];
  156. }];
  157. }
  158. - (BOOL)useSystemAppearance {
  159. return self.appearance == UIKeyboardAppearanceDefault;
  160. }
  161. - (BOOL)usingDarkMode {
  162. if (@available(iOS 12, *)) {
  163. return self.useSystemAppearance && self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark;
  164. }
  165. return self.appearance == UIKeyboardAppearanceDark;
  166. }
  167. - (void)traitCollectionDidChange:(UITraitCollection *)previous {
  168. if (@available(iOS 12, *)) {
  169. // Was darkmode toggled?
  170. if (previous.userInterfaceStyle != self.traitCollection.userInterfaceStyle) {
  171. if (self.useSystemAppearance) {
  172. // Recreate the background view with the proper colors
  173. self.appearance = self.appearance;
  174. }
  175. }
  176. }
  177. }
  178. @end