mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-25 21:05:40 +08:00
Updates from Thu Mar 5
- [react_native] JS files for D1885531 | Martin Konicek - Ported TabBarIOS to OSS and unified implementation | Nick Lockwood - [react-packager] Add minify option as query param | Amjad Masad - [ReactNative] Fix ExpandingText prop types | Christopher Chedeau - [react-packager] Make dev a query param option | Amjad Masad
This commit is contained in:
11
ReactKit/Views/RCTAnimationType.h
Normal file
11
ReactKit/Views/RCTAnimationType.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, RCTAnimationType) {
|
||||
RCTAnimationTypeSpring = 0,
|
||||
RCTAnimationTypeLinear,
|
||||
RCTAnimationTypeEaseIn,
|
||||
RCTAnimationTypeEaseOut,
|
||||
RCTAnimationTypeEaseInEaseOut,
|
||||
};
|
||||
13
ReactKit/Views/RCTAutoInsetsProtocol.h
Normal file
13
ReactKit/Views/RCTAutoInsetsProtocol.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
/**
|
||||
* Defines a View that wants to support auto insets adjustment
|
||||
*/
|
||||
@protocol RCTAutoInsetsProtocol
|
||||
|
||||
@property (nonatomic, assign, readwrite) UIEdgeInsets contentInset;
|
||||
@property (nonatomic, assign, readwrite) BOOL automaticallyAdjustContentInsets;
|
||||
|
||||
@end
|
||||
@@ -41,7 +41,6 @@ NSInteger kNeverProgressed = -10000;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
* In general, `RCTNavigator` examines `_currentViews` (which are React child
|
||||
* views), and compares them to `_navigationController.viewControllers` (which
|
||||
@@ -138,7 +137,6 @@ NSInteger kNeverProgressed = -10000;
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invoked when either a navigation item has been popped off, or when a
|
||||
* swipe-back gesture has began. The swipe-back gesture doesn't respect the
|
||||
@@ -184,7 +182,6 @@ NSInteger kNeverProgressed = -10000;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface RCTNavigator() <RCTWrapperViewControllerNavigationListener, UINavigationControllerDelegate>
|
||||
{
|
||||
RCTEventDispatcher *_eventDispatcher;
|
||||
@@ -204,7 +201,7 @@ NSInteger kNeverProgressed = -10000;
|
||||
*
|
||||
* - The run loop retains the displayLink.
|
||||
* - `displayLink` retains its target.
|
||||
* - We use `reactWillDestroy` to remove the `RCTNavigator`'s reference to the
|
||||
* - We use `invalidate` to remove the `RCTNavigator`'s reference to the
|
||||
* `displayLink` and remove the `displayLink` from the run loop.
|
||||
*
|
||||
*
|
||||
@@ -212,7 +209,7 @@ NSInteger kNeverProgressed = -10000;
|
||||
* --------------
|
||||
*
|
||||
* - Even though we could implement the `displayLink` cleanup without the
|
||||
* `reactWillDestroy` hook by adding and removing it from the run loop at the
|
||||
* `invalidate` hook by adding and removing it from the run loop at the
|
||||
* right times (begin/end animation), we need to account for the possibility
|
||||
* that the view itself is destroyed mid-interaction. So we always keep it
|
||||
* added to the run loop, but start/stop it with interactions/animations. We
|
||||
@@ -343,7 +340,7 @@ NSInteger kNeverProgressed = -10000;
|
||||
NSUInteger indexOfFrom = [_currentViews indexOfObject:fromController.navItem];
|
||||
NSUInteger indexOfTo = [_currentViews indexOfObject:toController.navItem];
|
||||
CGFloat destination = indexOfFrom < indexOfTo ? 1.0 : -1.0;
|
||||
_dummyView.frame = (CGRect){destination};
|
||||
_dummyView.frame = (CGRect){{destination}};
|
||||
_currentlyTransitioningFrom = indexOfFrom;
|
||||
_currentlyTransitioningTo = indexOfTo;
|
||||
if (indexOfFrom != indexOfTo) {
|
||||
@@ -450,24 +447,10 @@ NSInteger kNeverProgressed = -10000;
|
||||
return self.superview ? self.superview : self.reactNavSuperviewLink;
|
||||
}
|
||||
|
||||
- (void)addControllerToClosestParent:(UIViewController *)controller
|
||||
{
|
||||
if (!controller.parentViewController) {
|
||||
id responder = [self.superview nextResponder];
|
||||
while (responder && ![responder isKindOfClass:[UIViewController class]]) {
|
||||
responder = [responder nextResponder];
|
||||
}
|
||||
if (responder) {
|
||||
[responder addChildViewController:controller];
|
||||
[controller didMoveToParentViewController:responder];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reactBridgeDidFinishTransaction
|
||||
{
|
||||
// we can't hook up the VC hierarchy in 'init' because the subviews aren't hooked up yet,
|
||||
// so we do it on demand here
|
||||
// we can't hook up the VC hierarchy in 'init' because the subviews aren't
|
||||
// hooked up yet, so we do it on demand here
|
||||
[self addControllerToClosestParent:_navigationController];
|
||||
|
||||
NSInteger viewControllerCount = _navigationController.viewControllers.count;
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
#import "RCTNavigatorManager.h"
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTNavigator.h"
|
||||
#import "RCTShadowView.h"
|
||||
#import "RCTSparseArray.h"
|
||||
#import "RCTUIManager.h"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[RCTNavigator alloc] initWithEventDispatcher:self.eventDispatcher];
|
||||
return [[RCTNavigator alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(requestedTopOfStack)
|
||||
@@ -34,16 +34,12 @@ RCT_EXPORT_VIEW_PROPERTY(requestedTopOfStack)
|
||||
RCT_EXPORT();
|
||||
|
||||
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||
if (reactTag) {
|
||||
RCTNavigator *navigator = viewRegistry[reactTag];
|
||||
if ([navigator isKindOfClass:[RCTNavigator class]]) {
|
||||
BOOL wasAcquired = [navigator requestSchedulingJavaScriptNavigation];
|
||||
callback(@[@(wasAcquired)]);
|
||||
} else {
|
||||
RCTLogError(@"Cannot set lock: %@ (tag #%@) is not an RCTNavigator", navigator, reactTag);
|
||||
}
|
||||
RCTNavigator *navigator = viewRegistry[reactTag];
|
||||
if ([navigator isKindOfClass:[RCTNavigator class]]) {
|
||||
BOOL wasAcquired = [navigator requestSchedulingJavaScriptNavigation];
|
||||
callback(@[@(wasAcquired)]);
|
||||
} else {
|
||||
RCTLogError(@"Tag not specified for requestSchedulingJavaScriptNavigation");
|
||||
RCTLogError(@"Cannot set lock: %@ (tag #%@) is not an RCTNavigator", navigator, reactTag);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
10
ReactKit/Views/RCTPointerEvents.h
Normal file
10
ReactKit/Views/RCTPointerEvents.h
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, RCTPointerEvents) {
|
||||
RCTPointerEventsUnspecified = 0, // Default
|
||||
RCTPointerEventsNone,
|
||||
RCTPointerEventsBoxNone,
|
||||
RCTPointerEventsBoxOnly,
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#import "RCTScrollViewManager.h"
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTScrollView.h"
|
||||
|
||||
@@ -9,7 +10,7 @@
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[RCTScrollView alloc] initWithEventDispatcher:self.eventDispatcher];
|
||||
return [[RCTScrollView alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(alwaysBounceHorizontal)
|
||||
|
||||
18
ReactKit/Views/RCTScrollableProtocol.h
Normal file
18
ReactKit/Views/RCTScrollableProtocol.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
/**
|
||||
* Contains any methods related to scrolling. Any `RCTView` that has scrolling
|
||||
* features should implement these methods.
|
||||
*/
|
||||
@protocol RCTScrollableProtocol
|
||||
|
||||
@property (nonatomic, readwrite, weak) NSObject<UIScrollViewDelegate> *nativeMainScrollDelegate;
|
||||
@property (nonatomic, readonly) CGSize contentSize;
|
||||
|
||||
- (void)scrollToOffset:(CGPoint)offset;
|
||||
- (void)scrollToOffset:(CGPoint)offset animated:(BOOL)animated;
|
||||
- (void)zoomToRect:(CGRect)rect animated:(BOOL)animated;
|
||||
|
||||
@end
|
||||
@@ -325,6 +325,11 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
||||
return _reactSubviews;
|
||||
}
|
||||
|
||||
- (RCTShadowView *)reactSuperview
|
||||
{
|
||||
return _superview;
|
||||
}
|
||||
|
||||
- (NSNumber *)reactTagAtPoint:(CGPoint)point
|
||||
{
|
||||
for (RCTShadowView *shadowView in _reactSubviews) {
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface RCTStaticImage : UIImageView
|
||||
|
||||
@property (nonatomic, assign) UIEdgeInsets capInsets;
|
||||
@property (nonatomic, assign) UIImageRenderingMode renderingMode;
|
||||
|
||||
@end
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTStaticImage.h"
|
||||
|
||||
@implementation RCTStaticImage
|
||||
|
||||
- (void)_updateImage
|
||||
{
|
||||
UIImage *image = self.image;
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply rendering mode
|
||||
if (_renderingMode != image.renderingMode) {
|
||||
image = [image imageWithRenderingMode:_renderingMode];
|
||||
}
|
||||
|
||||
// Applying capInsets of 0 will switch the "resizingMode" of the image to "tile" which is undesired
|
||||
if (!UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero, _capInsets)) {
|
||||
image = [image resizableImageWithCapInsets:_capInsets resizingMode:UIImageResizingModeStretch];
|
||||
}
|
||||
|
||||
// Apply trilinear filtering to smooth out mis-sized images
|
||||
self.layer.minificationFilter = kCAFilterTrilinear;
|
||||
self.layer.magnificationFilter = kCAFilterTrilinear;
|
||||
|
||||
super.image = image;
|
||||
}
|
||||
|
||||
- (void)setImage:(UIImage *)image
|
||||
{
|
||||
if (image != super.image) {
|
||||
super.image = image;
|
||||
[self _updateImage];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setCapInsets:(UIEdgeInsets)capInsets
|
||||
{
|
||||
if (!UIEdgeInsetsEqualToEdgeInsets(_capInsets, capInsets)) {
|
||||
_capInsets = capInsets;
|
||||
[self _updateImage];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setRenderingMode:(UIImageRenderingMode)renderingMode
|
||||
{
|
||||
if (_renderingMode != renderingMode) {
|
||||
_renderingMode = renderingMode;
|
||||
[self _updateImage];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTStaticImageManager.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTStaticImage.h"
|
||||
#import "RCTConvert.h"
|
||||
|
||||
@implementation RCTStaticImageManager
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[RCTStaticImage alloc] init];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(capInsets)
|
||||
RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode)
|
||||
RCT_CUSTOM_VIEW_PROPERTY(src, RCTStaticImage *)
|
||||
{
|
||||
if (json) {
|
||||
if ([[[json description] pathExtension] caseInsensitiveCompare:@"gif"] == NSOrderedSame) {
|
||||
[view.layer addAnimation:[RCTConvert GIF:json] forKey:@"contents"];
|
||||
} else {
|
||||
view.image = [RCTConvert UIImage:json];
|
||||
}
|
||||
} else {
|
||||
view.image = defaultView.image;
|
||||
}
|
||||
}
|
||||
RCT_CUSTOM_VIEW_PROPERTY(tintColor, RCTStaticImage *)
|
||||
{
|
||||
if (json) {
|
||||
view.renderingMode = UIImageRenderingModeAlwaysTemplate;
|
||||
view.tintColor = [RCTConvert UIColor:json];
|
||||
} else {
|
||||
view.renderingMode = defaultView.renderingMode;
|
||||
view.tintColor = defaultView.tintColor;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
11
ReactKit/Views/RCTTabBar.h
Normal file
11
ReactKit/Views/RCTTabBar.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class RCTEventDispatcher;
|
||||
|
||||
@interface RCTTabBar : UIView
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
139
ReactKit/Views/RCTTabBar.m
Normal file
139
ReactKit/Views/RCTTabBar.m
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTTabBar.h"
|
||||
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTTabBarItem.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "RCTView.h"
|
||||
#import "RCTViewControllerProtocol.h"
|
||||
#import "RCTWrapperViewController.h"
|
||||
#import "UIView+ReactKit.h"
|
||||
|
||||
@interface RKCustomTabBarController : UITabBarController <RCTViewControllerProtocol>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RKCustomTabBarController
|
||||
|
||||
@synthesize currentTopLayoutGuide = _currentTopLayoutGuide;
|
||||
@synthesize currentBottomLayoutGuide = _currentBottomLayoutGuide;
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
{
|
||||
[super viewWillLayoutSubviews];
|
||||
_currentTopLayoutGuide = self.topLayoutGuide;
|
||||
_currentBottomLayoutGuide = self.bottomLayoutGuide;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTTabBar() <UITabBarControllerDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTTabBar
|
||||
{
|
||||
BOOL _tabsChanged;
|
||||
RCTEventDispatcher *_eventDispatcher;
|
||||
UITabBarController *_tabController;
|
||||
NSMutableArray *_tabViews;
|
||||
}
|
||||
|
||||
- (id)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
{
|
||||
if ((self = [super initWithFrame:CGRectZero])) {
|
||||
_eventDispatcher = eventDispatcher;
|
||||
_tabViews = [[NSMutableArray alloc] init];
|
||||
_tabController = [[RKCustomTabBarController alloc] init];
|
||||
_tabController.delegate = self;
|
||||
[self addSubview:_tabController.view];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (UIViewController *)backingViewController
|
||||
{
|
||||
return _tabController;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
_tabController.delegate = nil;
|
||||
}
|
||||
|
||||
- (NSArray *)reactSubviews
|
||||
{
|
||||
return _tabViews;
|
||||
}
|
||||
|
||||
- (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex
|
||||
{
|
||||
if (![view isKindOfClass:[RCTTabBarItem class]]) {
|
||||
RCTLogError(@"subview should be of type RCTTabBarItem");
|
||||
return;
|
||||
}
|
||||
[_tabViews insertObject:view atIndex:atIndex];
|
||||
_tabsChanged = YES;
|
||||
}
|
||||
|
||||
- (void)removeReactSubview:(UIView *)subview
|
||||
{
|
||||
if (_tabViews.count == 0) {
|
||||
RCTLogError(@"should have at least one view to remove a subview");
|
||||
return;
|
||||
}
|
||||
[_tabViews removeObject:subview];
|
||||
_tabsChanged = YES;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
_tabController.view.frame = self.bounds;
|
||||
}
|
||||
|
||||
- (void)reactBridgeDidFinishTransaction
|
||||
{
|
||||
// we can't hook up the VC hierarchy in 'init' because the subviews aren't
|
||||
// hooked up yet, so we do it on demand here whenever a transaction has finished
|
||||
[self addControllerToClosestParent:_tabController];
|
||||
//[RCTView addViewController:_tabController toBackingViewControllerForView:self];
|
||||
|
||||
if (_tabsChanged) {
|
||||
|
||||
NSMutableArray *viewControllers = [NSMutableArray array];
|
||||
for (RCTTabBarItem *tab in [self reactSubviews]) {
|
||||
UIViewController *controller = tab.backingViewController;
|
||||
if (!controller) {
|
||||
controller = [[RCTWrapperViewController alloc] initWithContentView:tab
|
||||
eventDispatcher:_eventDispatcher];
|
||||
}
|
||||
[viewControllers addObject:controller];
|
||||
}
|
||||
|
||||
_tabController.viewControllers = viewControllers;
|
||||
_tabsChanged = NO;
|
||||
}
|
||||
|
||||
[[self reactSubviews] enumerateObjectsUsingBlock:^(RCTTabBarItem *tab, NSUInteger index, BOOL *stop) {
|
||||
UIViewController *controller = _tabController.viewControllers[index];
|
||||
controller.tabBarItem = tab.barItem;
|
||||
if (tab.selected) {
|
||||
_tabController.selectedViewController = controller;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - UITabBarControllerDelegate
|
||||
|
||||
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
|
||||
{
|
||||
NSUInteger index = [tabBarController.viewControllers indexOfObject:viewController];
|
||||
RCTTabBarItem *tab = [self reactSubviews][index];
|
||||
[_eventDispatcher sendInputEventWithName:@"topTap" body:@{@"target": tab.reactTag}];
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
11
ReactKit/Views/RCTTabBarItem.h
Normal file
11
ReactKit/Views/RCTTabBarItem.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface RCTTabBarItem : UIView
|
||||
|
||||
@property (nonatomic, copy) NSString *icon;
|
||||
@property (nonatomic, assign, getter=isSelected) BOOL selected;
|
||||
@property (nonatomic, readonly) UITabBarItem *barItem;
|
||||
|
||||
@end
|
||||
83
ReactKit/Views/RCTTabBarItem.m
Normal file
83
ReactKit/Views/RCTTabBarItem.m
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTTabBarItem.h"
|
||||
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTLog.h"
|
||||
#import "UIView+ReactKit.h"
|
||||
|
||||
@implementation RCTTabBarItem
|
||||
|
||||
@synthesize barItem = _barItem;
|
||||
|
||||
- (UITabBarItem *)barItem
|
||||
{
|
||||
if (!_barItem) {
|
||||
_barItem = [[UITabBarItem alloc] init];
|
||||
}
|
||||
return _barItem;
|
||||
}
|
||||
|
||||
- (void)setIcon:(NSString *)icon
|
||||
{
|
||||
static NSDictionary *systemIcons;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
systemIcons = @{
|
||||
@"more": @(UITabBarSystemItemMore),
|
||||
@"favorites": @(UITabBarSystemItemFavorites),
|
||||
@"featured": @(UITabBarSystemItemFeatured),
|
||||
@"topRated": @(UITabBarSystemItemTopRated),
|
||||
@"recents": @(UITabBarSystemItemRecents),
|
||||
@"contacts": @(UITabBarSystemItemContacts),
|
||||
@"history": @(UITabBarSystemItemHistory),
|
||||
@"bookmarks": @(UITabBarSystemItemBookmarks),
|
||||
@"search": @(UITabBarSystemItemSearch),
|
||||
@"downloads": @(UITabBarSystemItemDownloads),
|
||||
@"mostRecent": @(UITabBarSystemItemMostRecent),
|
||||
@"mostViewed": @(UITabBarSystemItemMostViewed),
|
||||
};
|
||||
});
|
||||
|
||||
// Update icon
|
||||
BOOL wasSystemIcon = (systemIcons[_icon] != nil);
|
||||
_icon = [icon copy];
|
||||
|
||||
// Check if string matches any custom images first
|
||||
UIImage *image = [RCTConvert UIImage:_icon];
|
||||
UITabBarItem *oldItem = _barItem;
|
||||
if (image) {
|
||||
|
||||
// Recreate barItem if previous item was a system icon
|
||||
if (wasSystemIcon) {
|
||||
_barItem = nil;
|
||||
self.barItem.image = image;
|
||||
} else {
|
||||
self.barItem.image = image;
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Not a custom image, may be a system item?
|
||||
NSNumber *systemIcon = systemIcons[icon];
|
||||
if (!systemIcon) {
|
||||
RCTLogError(@"The tab bar icon '%@' did not match any known image or system icon", icon);
|
||||
return;
|
||||
}
|
||||
_barItem = [[UITabBarItem alloc] initWithTabBarSystemItem:[systemIcon integerValue] tag:oldItem.tag];
|
||||
}
|
||||
|
||||
// Reapply previous properties
|
||||
_barItem.title = oldItem.title;
|
||||
_barItem.imageInsets = oldItem.imageInsets;
|
||||
_barItem.selectedImage = oldItem.selectedImage;
|
||||
_barItem.badgeValue = oldItem.badgeValue;
|
||||
}
|
||||
|
||||
- (UIViewController *)backingViewController
|
||||
{
|
||||
return self.superview.backingViewController;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
@interface RCTStaticImageManager : RCTViewManager
|
||||
@interface RCTTabBarItemManager : RCTViewManager
|
||||
|
||||
@end
|
||||
25
ReactKit/Views/RCTTabBarItemManager.m
Normal file
25
ReactKit/Views/RCTTabBarItemManager.m
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTTabBarItemManager.h"
|
||||
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTTabBarItem.h"
|
||||
|
||||
@implementation RCTTabBarItemManager
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[RCTTabBarItem alloc] init];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(selected);
|
||||
RCT_EXPORT_VIEW_PROPERTY(icon);
|
||||
RCT_REMAP_VIEW_PROPERTY(selectedIcon, barItem.selectedImage);
|
||||
RCT_REMAP_VIEW_PROPERTY(badgeValue, barItem.badgeValue);
|
||||
RCT_CUSTOM_VIEW_PROPERTY(title, RCTTabBarItem *)
|
||||
{
|
||||
view.barItem.title = json ? [RCTConvert NSString:json] : defaultView.barItem.title;
|
||||
view.barItem.imageInsets = [view.barItem.title length] ? UIEdgeInsetsZero : (UIEdgeInsets){6, 0, -6, 0};
|
||||
}
|
||||
|
||||
@end
|
||||
7
ReactKit/Views/RCTTabBarManager.h
Normal file
7
ReactKit/Views/RCTTabBarManager.h
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
@interface RCTTabBarManager : RCTViewManager
|
||||
|
||||
@end
|
||||
17
ReactKit/Views/RCTTabBarManager.m
Normal file
17
ReactKit/Views/RCTTabBarManager.m
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTTabBarManager.h"
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTTabBar.h"
|
||||
|
||||
@implementation RCTTabBarManager
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[RCTTabBar alloc] initWithEventDispatcher:_bridge.eventDispatcher];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[RCTTextField alloc] initWithEventDispatcher:self.eventDispatcher];
|
||||
return [[RCTTextField alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(caretHidden)
|
||||
|
||||
@@ -10,14 +10,18 @@
|
||||
|
||||
@interface RCTView : UIView
|
||||
|
||||
/**
|
||||
* Used to control how touch events are processed.
|
||||
*/
|
||||
@property (nonatomic, assign) RCTPointerEvents pointerEvents;
|
||||
|
||||
+ (void)autoAdjustInsetsForView:(UIView<RCTAutoInsetsProtocol> *)parentView
|
||||
withScrollView:(UIScrollView *)scrollView
|
||||
updateOffset:(BOOL)updateOffset;
|
||||
|
||||
+ (UIViewController *)backingViewControllerForView:(UIView *)view;
|
||||
|
||||
/**
|
||||
* Find the first view controller whose view, or any subview is the specified view.
|
||||
*/
|
||||
+ (UIEdgeInsets)contentInsetsForView:(UIView *)curView;
|
||||
|
||||
@end
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#import "RCTAutoInsetsProtocol.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTLog.h"
|
||||
#import "UIView+ReactKit.h"
|
||||
|
||||
static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
||||
{
|
||||
@@ -23,15 +24,6 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
||||
|
||||
@implementation RCTView
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
_pointerEvents = RCTPointerEventsUnspecified;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)accessibilityLabel
|
||||
{
|
||||
if (super.accessibilityLabel) {
|
||||
@@ -108,19 +100,10 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
||||
}
|
||||
}
|
||||
|
||||
+ (UIViewController *)backingViewControllerForView:(UIView *)view
|
||||
{
|
||||
id responder = [view nextResponder];
|
||||
if ([responder isKindOfClass:[UIViewController class]]) {
|
||||
return responder;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (UIEdgeInsets)contentInsetsForView:(UIView *)view
|
||||
{
|
||||
while (view) {
|
||||
UIViewController *controller = [self backingViewControllerForView:view];
|
||||
UIViewController *controller = view.backingViewController;
|
||||
if (controller) {
|
||||
return (UIEdgeInsets){
|
||||
controller.topLayoutGuide.length, 0,
|
||||
|
||||
13
ReactKit/Views/RCTViewControllerProtocol.h
Normal file
13
ReactKit/Views/RCTViewControllerProtocol.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
/**
|
||||
* A simple protocol that any React-managed ViewControllers should implement.
|
||||
* We need all of our ViewControllers to cache layoutGuide changes so any View
|
||||
* in our View hierarchy can access accurate layoutGuide info at any time.
|
||||
*/
|
||||
@protocol RCTViewControllerProtocol <NSObject>
|
||||
|
||||
@property (nonatomic, readonly, strong) id<UILayoutSupport> currentTopLayoutGuide;
|
||||
@property (nonatomic, readonly, strong) id<UILayoutSupport> currentBottomLayoutGuide;
|
||||
|
||||
@end
|
||||
@@ -23,14 +23,6 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v
|
||||
*/
|
||||
@property (nonatomic, strong) RCTBridge *bridge;
|
||||
|
||||
/**
|
||||
* The event dispatcher is used to send events back to the JavaScript application.
|
||||
* It can either be used directly by the module, or passed on to instantiated
|
||||
* view subclasses so that they can handle their own events.
|
||||
*/
|
||||
// TODO: remove this, as it can be accessed directly from bridge
|
||||
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;
|
||||
|
||||
/**
|
||||
* The module name exposed to React JS. If omitted, this will be inferred
|
||||
* automatically by using the view module's class name. It is better to not
|
||||
|
||||
@@ -14,11 +14,6 @@
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
- (RCTEventDispatcher *)eventDispatcher
|
||||
{
|
||||
return _bridge.eventDispatcher;
|
||||
}
|
||||
|
||||
+ (NSString *)moduleName
|
||||
{
|
||||
// Default implementation, works in most cases
|
||||
|
||||
35
ReactKit/Views/RCTViewNodeProtocol.h
Normal file
35
ReactKit/Views/RCTViewNodeProtocol.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
/**
|
||||
|
||||
* Logical node in a tree of application components. Both `ShadowView`s and
|
||||
* `UIView+ReactKit`s conform to this. Allows us to write utilities that
|
||||
* reason about trees generally.
|
||||
*/
|
||||
@protocol RCTViewNodeProtocol <NSObject>
|
||||
|
||||
@property (nonatomic, copy) NSNumber *reactTag;
|
||||
|
||||
- (void)insertReactSubview:(id<RCTViewNodeProtocol>)subview atIndex:(NSInteger)atIndex;
|
||||
- (void)removeReactSubview:(id<RCTViewNodeProtocol>)subview;
|
||||
- (NSMutableArray *)reactSubviews;
|
||||
- (id<RCTViewNodeProtocol>)reactSuperview;
|
||||
- (NSNumber *)reactTagAtPoint:(CGPoint)point;
|
||||
|
||||
// View is an RCTRootView
|
||||
- (BOOL)isReactRootView;
|
||||
|
||||
@optional
|
||||
|
||||
// TODO: Deprecate this
|
||||
// This method is called after layout has been performed for all views known
|
||||
// to the RCTViewManager. It is only called on UIViews, not shadow views.
|
||||
- (void)reactBridgeDidFinishTransaction;
|
||||
|
||||
@end
|
||||
|
||||
// TODO: this is kinda dumb - let's come up with a
|
||||
// better way of identifying root react views please!
|
||||
static inline BOOL RCTIsReactRootView(NSNumber *reactTag) {
|
||||
return reactTag.integerValue % 10 == 1;
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTViewControllerProtocol.h"
|
||||
|
||||
@class RCTEventDispatcher;
|
||||
@class RCTNavItem;
|
||||
@class RCTWrapperViewController;
|
||||
@@ -13,7 +15,7 @@ didMoveToNavigationController:(UINavigationController *)navigationController;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTWrapperViewController : UIViewController
|
||||
@interface RCTWrapperViewController : UIViewController <RCTViewControllerProtocol>
|
||||
|
||||
- (instancetype)initWithContentView:(UIView *)contentView
|
||||
eventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
@@ -21,7 +23,7 @@ didMoveToNavigationController:(UINavigationController *)navigationController;
|
||||
- (instancetype)initWithNavItem:(RCTNavItem *)navItem
|
||||
eventDispatcher:(RCTEventDispatcher *)eventDispatcher;
|
||||
|
||||
@property (nonatomic, readwrite, weak) id<RCTWrapperViewControllerNavigationListener> navigationListener;
|
||||
@property (nonatomic, strong, readwrite) RCTNavItem *navItem;
|
||||
@property (nonatomic, weak) id<RCTWrapperViewControllerNavigationListener> navigationListener;
|
||||
@property (nonatomic, strong) RCTNavItem *navItem;
|
||||
|
||||
@end
|
||||
|
||||
@@ -2,22 +2,30 @@
|
||||
|
||||
#import "RCTWrapperViewController.h"
|
||||
|
||||
#import <UIKit/UIScrollView.h>
|
||||
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTNavItem.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "RCTViewControllerProtocol.h"
|
||||
#import "UIView+ReactKit.h"
|
||||
|
||||
@implementation RCTWrapperViewController
|
||||
{
|
||||
UIView *_wrapperView;
|
||||
UIView *_contentView;
|
||||
RCTEventDispatcher *_eventDispatcher;
|
||||
CGFloat _previousTopLayout;
|
||||
CGFloat _previousBottomLayout;
|
||||
}
|
||||
|
||||
- (instancetype)initWithContentView:(UIView *)contentView eventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
@synthesize currentTopLayoutGuide = _currentTopLayoutGuide;
|
||||
@synthesize currentBottomLayoutGuide = _currentBottomLayoutGuide;
|
||||
|
||||
- (instancetype)initWithContentView:(UIView *)contentView
|
||||
eventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
{
|
||||
if ((self = [super initWithNibName:nil bundle:nil])) {
|
||||
if (self = [super initWithNibName:nil bundle:nil]) {
|
||||
_contentView = contentView;
|
||||
_eventDispatcher = eventDispatcher;
|
||||
self.automaticallyAdjustsScrollViewInsets = NO;
|
||||
@@ -25,80 +33,91 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithNavItem:(RCTNavItem *)navItem eventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
- (instancetype)initWithNavItem:(RCTNavItem *)navItem
|
||||
eventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
{
|
||||
if ((self = [self initWithContentView:navItem eventDispatcher:eventDispatcher])) {
|
||||
if (self = [self initWithContentView:navItem eventDispatcher:eventDispatcher]) {
|
||||
_navItem = navItem;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
{
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
_currentTopLayoutGuide = self.topLayoutGuide;
|
||||
_currentBottomLayoutGuide = self.bottomLayoutGuide;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[self.navigationController setNavigationBarHidden:!_navItem animated:animated];
|
||||
if (!_navItem) {
|
||||
return;
|
||||
}
|
||||
// TODO: find a way to make this less-tightly coupled to navigation controller
|
||||
if ([self.parentViewController isKindOfClass:[UINavigationController class]])
|
||||
{
|
||||
|
||||
self.navigationItem.title = _navItem.title;
|
||||
|
||||
[self _configureNavBarStyle];
|
||||
|
||||
if (_navItem.rightButtonTitle.length > 0) {
|
||||
self.navigationItem.rightBarButtonItem =
|
||||
[[UIBarButtonItem alloc] initWithTitle:_navItem.rightButtonTitle
|
||||
style:UIBarButtonItemStyleDone
|
||||
target:self
|
||||
action:@selector(rightButtonTapped)];
|
||||
}
|
||||
|
||||
if (_navItem.backButtonTitle.length > 0) {
|
||||
self.navigationItem.backBarButtonItem =
|
||||
[[UIBarButtonItem alloc] initWithTitle:_navItem.backButtonTitle
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:nil
|
||||
action:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_configureNavBarStyle
|
||||
{
|
||||
UINavigationBar *bar = self.navigationController.navigationBar;
|
||||
if (_navItem.barTintColor) {
|
||||
bar.barTintColor = _navItem.barTintColor;
|
||||
}
|
||||
if (_navItem.tintColor) {
|
||||
BOOL canSetTintColor = _navItem.barTintColor == nil;
|
||||
if (canSetTintColor) {
|
||||
bar.tintColor = _navItem.tintColor;
|
||||
[self.navigationController setNavigationBarHidden:!_navItem animated:animated];
|
||||
if (!_navItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.navigationItem.title = _navItem.title;
|
||||
|
||||
UINavigationBar *bar = self.navigationController.navigationBar;
|
||||
if (_navItem.barTintColor) {
|
||||
bar.barTintColor = _navItem.barTintColor;
|
||||
}
|
||||
if (_navItem.tintColor) {
|
||||
BOOL canSetTintColor = _navItem.barTintColor == nil;
|
||||
if (canSetTintColor) {
|
||||
bar.tintColor = _navItem.tintColor;
|
||||
}
|
||||
}
|
||||
if (_navItem.titleTextColor) {
|
||||
[bar setTitleTextAttributes:@{NSForegroundColorAttributeName : _navItem.titleTextColor}];
|
||||
}
|
||||
|
||||
if (_navItem.rightButtonTitle.length > 0) {
|
||||
self.navigationItem.rightBarButtonItem =
|
||||
[[UIBarButtonItem alloc] initWithTitle:_navItem.rightButtonTitle
|
||||
style:UIBarButtonItemStyleDone
|
||||
target:self
|
||||
action:@selector(handleNavRightButtonTapped)];
|
||||
}
|
||||
|
||||
if (_navItem.backButtonTitle.length > 0) {
|
||||
self.navigationItem.backBarButtonItem =
|
||||
[[UIBarButtonItem alloc] initWithTitle:_navItem.backButtonTitle
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:nil
|
||||
action:nil];
|
||||
}
|
||||
}
|
||||
if (_navItem.titleTextColor) {
|
||||
[bar setTitleTextAttributes:@{NSForegroundColorAttributeName : _navItem.titleTextColor}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadView
|
||||
{
|
||||
// Add a wrapper so that UIViewControllerWrapperView (managed by the
|
||||
// add a wrapper so that UIViewControllerWrapperView (managed by the
|
||||
// UINavigationController) doesn't end up resetting the frames for
|
||||
// `contentView` which is a react-managed view.
|
||||
self.view = [[UIView alloc] init];
|
||||
[self.view addSubview:_contentView];
|
||||
//`contentView` which is a react-managed view.
|
||||
_wrapperView = [[UIView alloc] initWithFrame:_contentView.bounds];
|
||||
[_wrapperView addSubview:_contentView];
|
||||
self.view = _wrapperView;
|
||||
}
|
||||
|
||||
- (void)rightButtonTapped
|
||||
- (void)handleNavRightButtonTapped
|
||||
{
|
||||
[_eventDispatcher sendInputEventWithName:@"topNavRightButtonTap" body:@{@"target":_navItem.reactTag}];
|
||||
[_eventDispatcher sendInputEventWithName:@"topNavRightButtonTap"
|
||||
body:@{@"target":_navItem.reactTag}];
|
||||
}
|
||||
|
||||
- (void)didMoveToParentViewController:(UIViewController *)parent
|
||||
{
|
||||
// There's no clear setter for navigation controllers, but did move to parent view controller
|
||||
// provides the desired effect. This is called after a pop finishes, be it a swipe to go back
|
||||
// or a standard tap on the back button
|
||||
// There's no clear setter for navigation controllers, but did move to parent
|
||||
// view controller provides the desired effect. This is called after a pop
|
||||
// finishes, be it a swipe to go back or a standard tap on the back button
|
||||
[super didMoveToParentViewController:parent];
|
||||
if (parent == nil || [parent isKindOfClass:[UINavigationController class]]) {
|
||||
[self.navigationListener wrapperViewController:self didMoveToNavigationController:(UINavigationController *)parent];
|
||||
|
||||
@@ -8,4 +8,17 @@
|
||||
|
||||
@interface UIView (ReactKit) <RCTViewNodeProtocol>
|
||||
|
||||
/**
|
||||
* This method finds and returns the containing view controller for the view.
|
||||
*/
|
||||
- (UIViewController *)backingViewController;
|
||||
|
||||
/**
|
||||
* This method attaches the specified controller as a child of the
|
||||
* the owning view controller of this view. Returns NO if no view
|
||||
* controller is found (which may happen if the view is not currently
|
||||
* attached to the view hierarchy).
|
||||
*/
|
||||
- (void)addControllerToClosestParent:(UIViewController *)controller;
|
||||
|
||||
@end
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTWrapperViewController.h"
|
||||
|
||||
@implementation UIView (ReactKit)
|
||||
|
||||
@@ -20,7 +21,7 @@
|
||||
|
||||
- (BOOL)isReactRootView
|
||||
{
|
||||
return NO;
|
||||
return RCTIsReactRootView(self.reactTag);
|
||||
}
|
||||
|
||||
- (NSNumber *)reactTagAtPoint:(CGPoint)point
|
||||
@@ -39,7 +40,7 @@
|
||||
|
||||
- (void)removeReactSubview:(UIView *)subview
|
||||
{
|
||||
RCTAssert(subview.superview == self, @"");
|
||||
RCTAssert(subview.superview == self, @"%@ is a not a subview of %@", subview, self);
|
||||
[subview removeFromSuperview];
|
||||
}
|
||||
|
||||
@@ -48,4 +49,34 @@
|
||||
return self.subviews;
|
||||
}
|
||||
|
||||
- (UIView *)reactSuperview
|
||||
{
|
||||
return self.superview;
|
||||
}
|
||||
|
||||
- (UIViewController *)backingViewController
|
||||
{
|
||||
id responder = [self nextResponder];
|
||||
if ([responder isKindOfClass:[RCTWrapperViewController class]]) {
|
||||
return responder;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)addControllerToClosestParent:(UIViewController *)controller
|
||||
{
|
||||
if (!controller.parentViewController) {
|
||||
UIView *parentView = (UIView *)self.reactSuperview;
|
||||
while (parentView) {
|
||||
if (parentView.backingViewController) {
|
||||
[parentView.backingViewController addChildViewController:controller];
|
||||
[controller didMoveToParentViewController:parentView.backingViewController];
|
||||
break;
|
||||
}
|
||||
parentView = (UIView *)parentView.reactSuperview;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user