123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- /*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
- #import "UIView+React.h"
- #import <objc/runtime.h>
- #import "RCTAssert.h"
- #import "RCTLog.h"
- #import "RCTShadowView.h"
- @implementation UIView (React)
- - (NSNumber *)reactTag
- {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void)setReactTag:(NSNumber *)reactTag
- {
- objc_setAssociatedObject(self, @selector(reactTag), reactTag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (NSNumber *)rootTag
- {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void)setRootTag:(NSNumber *)rootTag
- {
- objc_setAssociatedObject(self, @selector(rootTag), rootTag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (NSString *)nativeID
- {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void)setNativeID:(NSString *)nativeID
- {
- objc_setAssociatedObject(self, @selector(nativeID), nativeID, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (BOOL)shouldAccessibilityIgnoresInvertColors
- {
- #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
- if (@available(iOS 11.0, *)) {
- return self.accessibilityIgnoresInvertColors;
- }
- #endif
- return NO;
- }
- - (void)setShouldAccessibilityIgnoresInvertColors:(BOOL)shouldAccessibilityIgnoresInvertColors
- {
- #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
- if (@available(iOS 11.0, *)) {
- self.accessibilityIgnoresInvertColors = shouldAccessibilityIgnoresInvertColors;
- }
- #endif
- }
- - (BOOL)isReactRootView
- {
- return RCTIsReactRootView(self.reactTag);
- }
- - (NSNumber *)reactTagAtPoint:(CGPoint)point
- {
- UIView *view = [self hitTest:point withEvent:nil];
- while (view && !view.reactTag) {
- view = view.superview;
- }
- return view.reactTag;
- }
- - (NSArray<UIView *> *)reactSubviews
- {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (UIView *)reactSuperview
- {
- return self.superview;
- }
- - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
- {
- // We access the associated object directly here in case someone overrides
- // the `reactSubviews` getter method and returns an immutable array.
- NSMutableArray *subviews = objc_getAssociatedObject(self, @selector(reactSubviews));
- if (!subviews) {
- subviews = [NSMutableArray new];
- objc_setAssociatedObject(self, @selector(reactSubviews), subviews, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- [subviews insertObject:subview atIndex:atIndex];
- }
- - (void)removeReactSubview:(UIView *)subview
- {
- // We access the associated object directly here in case someone overrides
- // the `reactSubviews` getter method and returns an immutable array.
- NSMutableArray *subviews = objc_getAssociatedObject(self, @selector(reactSubviews));
- [subviews removeObject:subview];
- [subview removeFromSuperview];
- }
- #pragma mark - Display
- - (YGDisplay)reactDisplay
- {
- return self.isHidden ? YGDisplayNone : YGDisplayFlex;
- }
- - (void)setReactDisplay:(YGDisplay)display
- {
- self.hidden = display == YGDisplayNone;
- }
- #pragma mark - Layout Direction
- - (UIUserInterfaceLayoutDirection)reactLayoutDirection
- {
- if ([self respondsToSelector:@selector(semanticContentAttribute)]) {
- return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.semanticContentAttribute];
- } else {
- return [objc_getAssociatedObject(self, @selector(reactLayoutDirection)) integerValue];
- }
- }
- - (void)setReactLayoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
- {
- if ([self respondsToSelector:@selector(setSemanticContentAttribute:)]) {
- self.semanticContentAttribute = layoutDirection == UIUserInterfaceLayoutDirectionLeftToRight
- ? UISemanticContentAttributeForceLeftToRight
- : UISemanticContentAttributeForceRightToLeft;
- } else {
- objc_setAssociatedObject(
- self, @selector(reactLayoutDirection), @(layoutDirection), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- }
- #pragma mark - zIndex
- - (NSInteger)reactZIndex
- {
- return self.layer.zPosition;
- }
- - (void)setReactZIndex:(NSInteger)reactZIndex
- {
- self.layer.zPosition = reactZIndex;
- }
- - (NSArray<UIView *> *)reactZIndexSortedSubviews
- {
- // Check if sorting is required - in most cases it won't be.
- BOOL sortingRequired = NO;
- for (UIView *subview in self.subviews) {
- if (subview.reactZIndex != 0) {
- sortingRequired = YES;
- break;
- }
- }
- return sortingRequired ? [self.reactSubviews sortedArrayUsingComparator:^NSComparisonResult(UIView *a, UIView *b) {
- if (a.reactZIndex > b.reactZIndex) {
- return NSOrderedDescending;
- } else {
- // Ensure sorting is stable by treating equal zIndex as ascending so
- // that original order is preserved.
- return NSOrderedAscending;
- }
- }]
- : self.subviews;
- }
- - (void)didUpdateReactSubviews
- {
- for (UIView *subview in self.reactSubviews) {
- [self addSubview:subview];
- }
- }
- - (void)didSetProps:(__unused NSArray<NSString *> *)changedProps
- {
- // The default implementation does nothing.
- }
- - (void)reactSetFrame:(CGRect)frame
- {
- // These frames are in terms of anchorPoint = topLeft, but internally the
- // views are anchorPoint = center for easier scale and rotation animations.
- // Convert the frame so it works with anchorPoint = center.
- CGPoint position = {CGRectGetMidX(frame), CGRectGetMidY(frame)};
- CGRect bounds = {CGPointZero, frame.size};
- // Avoid crashes due to nan coords
- if (isnan(position.x) || isnan(position.y) || isnan(bounds.origin.x) || isnan(bounds.origin.y) ||
- isnan(bounds.size.width) || isnan(bounds.size.height)) {
- RCTLogError(
- @"Invalid layout for (%@)%@. position: %@. bounds: %@",
- self.reactTag,
- self,
- NSStringFromCGPoint(position),
- NSStringFromCGRect(bounds));
- return;
- }
- self.center = position;
- self.bounds = bounds;
- }
- - (UIViewController *)reactViewController
- {
- id responder = [self nextResponder];
- while (responder) {
- if ([responder isKindOfClass:[UIViewController class]]) {
- return responder;
- }
- responder = [responder nextResponder];
- }
- return nil;
- }
- - (void)reactAddControllerToClosestParent:(UIViewController *)controller
- {
- if (!controller.parentViewController) {
- UIView *parentView = (UIView *)self.reactSuperview;
- while (parentView) {
- if (parentView.reactViewController) {
- [parentView.reactViewController addChildViewController:controller];
- [controller didMoveToParentViewController:parentView.reactViewController];
- break;
- }
- parentView = (UIView *)parentView.reactSuperview;
- }
- return;
- }
- }
- /**
- * Focus manipulation.
- */
- - (BOOL)reactIsFocusNeeded
- {
- return [(NSNumber *)objc_getAssociatedObject(self, @selector(reactIsFocusNeeded)) boolValue];
- }
- - (void)setReactIsFocusNeeded:(BOOL)isFocusNeeded
- {
- objc_setAssociatedObject(self, @selector(reactIsFocusNeeded), @(isFocusNeeded), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (void)reactFocus
- {
- if (![self becomeFirstResponder]) {
- self.reactIsFocusNeeded = YES;
- }
- }
- - (void)reactFocusIfNeeded
- {
- if (self.reactIsFocusNeeded) {
- if ([self becomeFirstResponder]) {
- self.reactIsFocusNeeded = NO;
- }
- }
- }
- - (void)reactBlur
- {
- [self resignFirstResponder];
- }
- #pragma mark - Layout
- - (UIEdgeInsets)reactBorderInsets
- {
- CGFloat borderWidth = self.layer.borderWidth;
- return UIEdgeInsetsMake(borderWidth, borderWidth, borderWidth, borderWidth);
- }
- - (UIEdgeInsets)reactPaddingInsets
- {
- return UIEdgeInsetsZero;
- }
- - (UIEdgeInsets)reactCompoundInsets
- {
- UIEdgeInsets borderInsets = self.reactBorderInsets;
- UIEdgeInsets paddingInsets = self.reactPaddingInsets;
- return UIEdgeInsetsMake(
- borderInsets.top + paddingInsets.top,
- borderInsets.left + paddingInsets.left,
- borderInsets.bottom + paddingInsets.bottom,
- borderInsets.right + paddingInsets.right);
- }
- - (CGRect)reactContentFrame
- {
- return UIEdgeInsetsInsetRect(self.bounds, self.reactCompoundInsets);
- }
- #pragma mark - Accessibility
- - (UIView *)reactAccessibilityElement
- {
- return self;
- }
- - (NSArray<NSDictionary *> *)accessibilityActions
- {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void)setAccessibilityActions:(NSArray<NSDictionary *> *)accessibilityActions
- {
- objc_setAssociatedObject(
- self, @selector(accessibilityActions), accessibilityActions, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (NSString *)accessibilityRole
- {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void)setAccessibilityRole:(NSString *)accessibilityRole
- {
- objc_setAssociatedObject(self, @selector(accessibilityRole), accessibilityRole, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (NSDictionary<NSString *, id> *)accessibilityState
- {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void)setAccessibilityState:(NSDictionary<NSString *, id> *)accessibilityState
- {
- objc_setAssociatedObject(self, @selector(accessibilityState), accessibilityState, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (NSDictionary<NSString *, id> *)accessibilityValueInternal
- {
- return objc_getAssociatedObject(self, _cmd);
- }
- - (void)setAccessibilityValueInternal:(NSDictionary<NSString *, id> *)accessibilityValue
- {
- objc_setAssociatedObject(
- self, @selector(accessibilityValueInternal), accessibilityValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- #pragma mark - Debug
- - (void)react_addRecursiveDescriptionToString:(NSMutableString *)string atLevel:(NSUInteger)level
- {
- for (NSUInteger i = 0; i < level; i++) {
- [string appendString:@" | "];
- }
- [string appendString:self.description];
- [string appendString:@"\n"];
- for (UIView *subview in self.subviews) {
- [subview react_addRecursiveDescriptionToString:string atLevel:level + 1];
- }
- }
- - (NSString *)react_recursiveDescription
- {
- NSMutableString *description = [NSMutableString string];
- [self react_addRecursiveDescriptionToString:description atLevel:0];
- return description;
- }
- @end
|