UIView+React.m 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #import "UIView+React.h"
  8. #import <objc/runtime.h>
  9. #import "RCTAssert.h"
  10. #import "RCTLog.h"
  11. #import "RCTShadowView.h"
  12. @implementation UIView (React)
  13. - (NSNumber *)reactTag
  14. {
  15. return objc_getAssociatedObject(self, _cmd);
  16. }
  17. - (void)setReactTag:(NSNumber *)reactTag
  18. {
  19. objc_setAssociatedObject(self, @selector(reactTag), reactTag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  20. }
  21. - (NSNumber *)rootTag
  22. {
  23. return objc_getAssociatedObject(self, _cmd);
  24. }
  25. - (void)setRootTag:(NSNumber *)rootTag
  26. {
  27. objc_setAssociatedObject(self, @selector(rootTag), rootTag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  28. }
  29. - (NSString *)nativeID
  30. {
  31. return objc_getAssociatedObject(self, _cmd);
  32. }
  33. - (void)setNativeID:(NSString *)nativeID
  34. {
  35. objc_setAssociatedObject(self, @selector(nativeID), nativeID, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  36. }
  37. - (BOOL)shouldAccessibilityIgnoresInvertColors
  38. {
  39. #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
  40. if (@available(iOS 11.0, *)) {
  41. return self.accessibilityIgnoresInvertColors;
  42. }
  43. #endif
  44. return NO;
  45. }
  46. - (void)setShouldAccessibilityIgnoresInvertColors:(BOOL)shouldAccessibilityIgnoresInvertColors
  47. {
  48. #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
  49. if (@available(iOS 11.0, *)) {
  50. self.accessibilityIgnoresInvertColors = shouldAccessibilityIgnoresInvertColors;
  51. }
  52. #endif
  53. }
  54. - (BOOL)isReactRootView
  55. {
  56. return RCTIsReactRootView(self.reactTag);
  57. }
  58. - (NSNumber *)reactTagAtPoint:(CGPoint)point
  59. {
  60. UIView *view = [self hitTest:point withEvent:nil];
  61. while (view && !view.reactTag) {
  62. view = view.superview;
  63. }
  64. return view.reactTag;
  65. }
  66. - (NSArray<UIView *> *)reactSubviews
  67. {
  68. return objc_getAssociatedObject(self, _cmd);
  69. }
  70. - (UIView *)reactSuperview
  71. {
  72. return self.superview;
  73. }
  74. - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
  75. {
  76. // We access the associated object directly here in case someone overrides
  77. // the `reactSubviews` getter method and returns an immutable array.
  78. NSMutableArray *subviews = objc_getAssociatedObject(self, @selector(reactSubviews));
  79. if (!subviews) {
  80. subviews = [NSMutableArray new];
  81. objc_setAssociatedObject(self, @selector(reactSubviews), subviews, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  82. }
  83. [subviews insertObject:subview atIndex:atIndex];
  84. }
  85. - (void)removeReactSubview:(UIView *)subview
  86. {
  87. // We access the associated object directly here in case someone overrides
  88. // the `reactSubviews` getter method and returns an immutable array.
  89. NSMutableArray *subviews = objc_getAssociatedObject(self, @selector(reactSubviews));
  90. [subviews removeObject:subview];
  91. [subview removeFromSuperview];
  92. }
  93. #pragma mark - Display
  94. - (YGDisplay)reactDisplay
  95. {
  96. return self.isHidden ? YGDisplayNone : YGDisplayFlex;
  97. }
  98. - (void)setReactDisplay:(YGDisplay)display
  99. {
  100. self.hidden = display == YGDisplayNone;
  101. }
  102. #pragma mark - Layout Direction
  103. - (UIUserInterfaceLayoutDirection)reactLayoutDirection
  104. {
  105. if ([self respondsToSelector:@selector(semanticContentAttribute)]) {
  106. return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.semanticContentAttribute];
  107. } else {
  108. return [objc_getAssociatedObject(self, @selector(reactLayoutDirection)) integerValue];
  109. }
  110. }
  111. - (void)setReactLayoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
  112. {
  113. if ([self respondsToSelector:@selector(setSemanticContentAttribute:)]) {
  114. self.semanticContentAttribute = layoutDirection == UIUserInterfaceLayoutDirectionLeftToRight
  115. ? UISemanticContentAttributeForceLeftToRight
  116. : UISemanticContentAttributeForceRightToLeft;
  117. } else {
  118. objc_setAssociatedObject(
  119. self, @selector(reactLayoutDirection), @(layoutDirection), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  120. }
  121. }
  122. #pragma mark - zIndex
  123. - (NSInteger)reactZIndex
  124. {
  125. return self.layer.zPosition;
  126. }
  127. - (void)setReactZIndex:(NSInteger)reactZIndex
  128. {
  129. self.layer.zPosition = reactZIndex;
  130. }
  131. - (NSArray<UIView *> *)reactZIndexSortedSubviews
  132. {
  133. // Check if sorting is required - in most cases it won't be.
  134. BOOL sortingRequired = NO;
  135. for (UIView *subview in self.subviews) {
  136. if (subview.reactZIndex != 0) {
  137. sortingRequired = YES;
  138. break;
  139. }
  140. }
  141. return sortingRequired ? [self.reactSubviews sortedArrayUsingComparator:^NSComparisonResult(UIView *a, UIView *b) {
  142. if (a.reactZIndex > b.reactZIndex) {
  143. return NSOrderedDescending;
  144. } else {
  145. // Ensure sorting is stable by treating equal zIndex as ascending so
  146. // that original order is preserved.
  147. return NSOrderedAscending;
  148. }
  149. }]
  150. : self.subviews;
  151. }
  152. - (void)didUpdateReactSubviews
  153. {
  154. for (UIView *subview in self.reactSubviews) {
  155. [self addSubview:subview];
  156. }
  157. }
  158. - (void)didSetProps:(__unused NSArray<NSString *> *)changedProps
  159. {
  160. // The default implementation does nothing.
  161. }
  162. - (void)reactSetFrame:(CGRect)frame
  163. {
  164. // These frames are in terms of anchorPoint = topLeft, but internally the
  165. // views are anchorPoint = center for easier scale and rotation animations.
  166. // Convert the frame so it works with anchorPoint = center.
  167. CGPoint position = {CGRectGetMidX(frame), CGRectGetMidY(frame)};
  168. CGRect bounds = {CGPointZero, frame.size};
  169. // Avoid crashes due to nan coords
  170. if (isnan(position.x) || isnan(position.y) || isnan(bounds.origin.x) || isnan(bounds.origin.y) ||
  171. isnan(bounds.size.width) || isnan(bounds.size.height)) {
  172. RCTLogError(
  173. @"Invalid layout for (%@)%@. position: %@. bounds: %@",
  174. self.reactTag,
  175. self,
  176. NSStringFromCGPoint(position),
  177. NSStringFromCGRect(bounds));
  178. return;
  179. }
  180. self.center = position;
  181. self.bounds = bounds;
  182. }
  183. - (UIViewController *)reactViewController
  184. {
  185. id responder = [self nextResponder];
  186. while (responder) {
  187. if ([responder isKindOfClass:[UIViewController class]]) {
  188. return responder;
  189. }
  190. responder = [responder nextResponder];
  191. }
  192. return nil;
  193. }
  194. - (void)reactAddControllerToClosestParent:(UIViewController *)controller
  195. {
  196. if (!controller.parentViewController) {
  197. UIView *parentView = (UIView *)self.reactSuperview;
  198. while (parentView) {
  199. if (parentView.reactViewController) {
  200. [parentView.reactViewController addChildViewController:controller];
  201. [controller didMoveToParentViewController:parentView.reactViewController];
  202. break;
  203. }
  204. parentView = (UIView *)parentView.reactSuperview;
  205. }
  206. return;
  207. }
  208. }
  209. /**
  210. * Focus manipulation.
  211. */
  212. - (BOOL)reactIsFocusNeeded
  213. {
  214. return [(NSNumber *)objc_getAssociatedObject(self, @selector(reactIsFocusNeeded)) boolValue];
  215. }
  216. - (void)setReactIsFocusNeeded:(BOOL)isFocusNeeded
  217. {
  218. objc_setAssociatedObject(self, @selector(reactIsFocusNeeded), @(isFocusNeeded), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  219. }
  220. - (void)reactFocus
  221. {
  222. if (![self becomeFirstResponder]) {
  223. self.reactIsFocusNeeded = YES;
  224. }
  225. }
  226. - (void)reactFocusIfNeeded
  227. {
  228. if (self.reactIsFocusNeeded) {
  229. if ([self becomeFirstResponder]) {
  230. self.reactIsFocusNeeded = NO;
  231. }
  232. }
  233. }
  234. - (void)reactBlur
  235. {
  236. [self resignFirstResponder];
  237. }
  238. #pragma mark - Layout
  239. - (UIEdgeInsets)reactBorderInsets
  240. {
  241. CGFloat borderWidth = self.layer.borderWidth;
  242. return UIEdgeInsetsMake(borderWidth, borderWidth, borderWidth, borderWidth);
  243. }
  244. - (UIEdgeInsets)reactPaddingInsets
  245. {
  246. return UIEdgeInsetsZero;
  247. }
  248. - (UIEdgeInsets)reactCompoundInsets
  249. {
  250. UIEdgeInsets borderInsets = self.reactBorderInsets;
  251. UIEdgeInsets paddingInsets = self.reactPaddingInsets;
  252. return UIEdgeInsetsMake(
  253. borderInsets.top + paddingInsets.top,
  254. borderInsets.left + paddingInsets.left,
  255. borderInsets.bottom + paddingInsets.bottom,
  256. borderInsets.right + paddingInsets.right);
  257. }
  258. - (CGRect)reactContentFrame
  259. {
  260. return UIEdgeInsetsInsetRect(self.bounds, self.reactCompoundInsets);
  261. }
  262. #pragma mark - Accessibility
  263. - (UIView *)reactAccessibilityElement
  264. {
  265. return self;
  266. }
  267. - (NSArray<NSDictionary *> *)accessibilityActions
  268. {
  269. return objc_getAssociatedObject(self, _cmd);
  270. }
  271. - (void)setAccessibilityActions:(NSArray<NSDictionary *> *)accessibilityActions
  272. {
  273. objc_setAssociatedObject(
  274. self, @selector(accessibilityActions), accessibilityActions, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  275. }
  276. - (NSString *)accessibilityRole
  277. {
  278. return objc_getAssociatedObject(self, _cmd);
  279. }
  280. - (void)setAccessibilityRole:(NSString *)accessibilityRole
  281. {
  282. objc_setAssociatedObject(self, @selector(accessibilityRole), accessibilityRole, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  283. }
  284. - (NSDictionary<NSString *, id> *)accessibilityState
  285. {
  286. return objc_getAssociatedObject(self, _cmd);
  287. }
  288. - (void)setAccessibilityState:(NSDictionary<NSString *, id> *)accessibilityState
  289. {
  290. objc_setAssociatedObject(self, @selector(accessibilityState), accessibilityState, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  291. }
  292. - (NSDictionary<NSString *, id> *)accessibilityValueInternal
  293. {
  294. return objc_getAssociatedObject(self, _cmd);
  295. }
  296. - (void)setAccessibilityValueInternal:(NSDictionary<NSString *, id> *)accessibilityValue
  297. {
  298. objc_setAssociatedObject(
  299. self, @selector(accessibilityValueInternal), accessibilityValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  300. }
  301. #pragma mark - Debug
  302. - (void)react_addRecursiveDescriptionToString:(NSMutableString *)string atLevel:(NSUInteger)level
  303. {
  304. for (NSUInteger i = 0; i < level; i++) {
  305. [string appendString:@" | "];
  306. }
  307. [string appendString:self.description];
  308. [string appendString:@"\n"];
  309. for (UIView *subview in self.subviews) {
  310. [subview react_addRecursiveDescriptionToString:string atLevel:level + 1];
  311. }
  312. }
  313. - (NSString *)react_recursiveDescription
  314. {
  315. NSMutableString *description = [NSMutableString string];
  316. [self react_addRecursiveDescriptionToString:description atLevel:0];
  317. return description;
  318. }
  319. @end