From 61b8c61903c95dbd38281f45fc16a4fd67fa32ca Mon Sep 17 00:00:00 2001 From: Christopher Chedeau Date: Fri, 6 Mar 2015 09:54:10 -0800 Subject: [PATCH] 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 --- .../Movies/Movies.xcodeproj/project.pbxproj | 98 +++++----- .../TicTacToe.xcodeproj/project.pbxproj | 78 +++----- Examples/UIExplorer/TabBarExample.js | 101 ++++++++++ .../UIExplorer.xcodeproj/project.pbxproj | 176 +++++++++--------- Examples/UIExplorer/UIExplorerList.js | 1 + .../Components/TabBarIOS/TabBarIOS.android.js | 29 +++ .../Components/TabBarIOS/TabBarIOS.ios.js | 36 ++++ .../TabBarIOS/TabBarItemIOS.android.js | 38 ++++ .../Components/TabBarIOS/TabBarItemIOS.ios.js | 94 ++++++++++ Libraries/Components/View/View.js | 2 +- Libraries/Image/RCTGIFImage.h | 8 + Libraries/Image/RCTGIFImage.m | 91 +++++++++ .../project.pbxproj | 50 +++-- Libraries/Image/RCTImageDownloader.m | 28 ++- Libraries/Image/RCTNetworkImageView.m | 5 +- Libraries/Image/RCTNetworkImageViewManager.m | 1 - .../Image}/RCTStaticImage.h | 0 .../Image}/RCTStaticImage.m | 0 .../Image}/RCTStaticImageManager.h | 0 .../Image}/RCTStaticImageManager.m | 6 +- .../project.pbxproj | 24 +-- Libraries/Text/ExpandingText.js | 46 +++-- Libraries/Text/Text.js | 4 + ReactKit/Base/RCTConvert.h | 15 +- ReactKit/Base/RCTConvert.m | 91 +-------- ReactKit/Modules/RCTUIManager.m | 4 +- ReactKit/ReactKit.xcodeproj/project.pbxproj | 58 +++--- ReactKit/{Base => Views}/RCTAnimationType.h | 0 .../{Base => Views}/RCTAutoInsetsProtocol.h | 0 ReactKit/Views/RCTNavigator.m | 27 +-- ReactKit/Views/RCTNavigatorManager.m | 18 +- ReactKit/{Base => Views}/RCTPointerEvents.h | 0 ReactKit/Views/RCTScrollViewManager.m | 3 +- .../{Base => Views}/RCTScrollableProtocol.h | 0 ReactKit/Views/RCTShadowView.m | 5 + ReactKit/Views/RCTTabBar.h | 11 ++ ReactKit/Views/RCTTabBar.m | 139 ++++++++++++++ ReactKit/Views/RCTTabBarItem.h | 11 ++ ReactKit/Views/RCTTabBarItem.m | 83 +++++++++ ReactKit/Views/RCTTabBarItemManager.h | 7 + ReactKit/Views/RCTTabBarItemManager.m | 25 +++ ReactKit/Views/RCTTabBarManager.h | 7 + ReactKit/Views/RCTTabBarManager.m | 17 ++ ReactKit/Views/RCTTextFieldManager.m | 2 +- ReactKit/Views/RCTView.h | 8 +- ReactKit/Views/RCTView.m | 21 +-- ReactKit/Views/RCTViewControllerProtocol.h | 13 ++ ReactKit/Views/RCTViewManager.h | 8 - ReactKit/Views/RCTViewManager.m | 5 - .../{Base => Views}/RCTViewNodeProtocol.h | 7 + ReactKit/Views/RCTWrapperViewController.h | 8 +- ReactKit/Views/RCTWrapperViewController.m | 121 +++++++----- ReactKit/Views/UIView+ReactKit.h | 13 ++ ReactKit/Views/UIView+ReactKit.m | 35 +++- packager/packager.js | 7 +- .../__tests__/HasteDependencyResolver-test.js | 10 +- .../src/DependencyResolver/haste/index.js | 68 +++---- .../react-packager/src/JSTransformer/index.js | 4 - .../react-packager/src/Packager/Package.js | 55 +++++- packager/react-packager/src/Packager/index.js | 14 +- packager/react-packager/src/Server/index.js | 31 +-- .../react-packager/src/lib/declareOpts.js | 2 + 62 files changed, 1290 insertions(+), 579 deletions(-) create mode 100644 Examples/UIExplorer/TabBarExample.js create mode 100644 Libraries/Components/TabBarIOS/TabBarIOS.android.js create mode 100644 Libraries/Components/TabBarIOS/TabBarIOS.ios.js create mode 100644 Libraries/Components/TabBarIOS/TabBarItemIOS.android.js create mode 100644 Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js create mode 100644 Libraries/Image/RCTGIFImage.h create mode 100644 Libraries/Image/RCTGIFImage.m rename Libraries/Image/{RCTNetworkImage.xcodeproj => RCTImage.xcodeproj}/project.pbxproj (74%) rename {ReactKit/Views => Libraries/Image}/RCTStaticImage.h (100%) rename {ReactKit/Views => Libraries/Image}/RCTStaticImage.m (100%) rename {ReactKit/Views => Libraries/Image}/RCTStaticImageManager.h (100%) rename {ReactKit/Views => Libraries/Image}/RCTStaticImageManager.m (88%) rename Libraries/Network/{RCTDataManager.xcodeproj => RCTNetwork.xcodeproj}/project.pbxproj (91%) rename ReactKit/{Base => Views}/RCTAnimationType.h (100%) rename ReactKit/{Base => Views}/RCTAutoInsetsProtocol.h (100%) rename ReactKit/{Base => Views}/RCTPointerEvents.h (100%) rename ReactKit/{Base => Views}/RCTScrollableProtocol.h (100%) create mode 100644 ReactKit/Views/RCTTabBar.h create mode 100644 ReactKit/Views/RCTTabBar.m create mode 100644 ReactKit/Views/RCTTabBarItem.h create mode 100644 ReactKit/Views/RCTTabBarItem.m create mode 100644 ReactKit/Views/RCTTabBarItemManager.h create mode 100644 ReactKit/Views/RCTTabBarItemManager.m create mode 100644 ReactKit/Views/RCTTabBarManager.h create mode 100644 ReactKit/Views/RCTTabBarManager.m create mode 100644 ReactKit/Views/RCTViewControllerProtocol.h rename ReactKit/{Base => Views}/RCTViewNodeProtocol.h (77%) diff --git a/Examples/Movies/Movies.xcodeproj/project.pbxproj b/Examples/Movies/Movies.xcodeproj/project.pbxproj index 00dd93007..5d450af8b 100644 --- a/Examples/Movies/Movies.xcodeproj/project.pbxproj +++ b/Examples/Movies/Movies.xcodeproj/project.pbxproj @@ -7,23 +7,30 @@ objects = { /* Begin PBXBuildFile section */ + 1341801E1AA91750003F314A /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341801B1AA91740003F314A /* libRCTNetwork.a */; }; + 13442C061AA90EA00037E5B0 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13442C051AA90E7D0037E5B0 /* libRCTImage.a */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 58C572691AA6239800CDF9C8 /* libRCTDataManager.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C572631AA6236600CDF9C8 /* libRCTDataManager.a */; }; - 58C5726A1AA6239B00CDF9C8 /* libRCTNetworkImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C572561AA6236500CDF9C8 /* libRCTNetworkImage.a */; }; 58C5726B1AA6239E00CDF9C8 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C5725B1AA6236500CDF9C8 /* libRCTText.a */; }; 58C5726C1AA623A200CDF9C8 /* libReactKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C572681AA6236600CDF9C8 /* libReactKit.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 58C572551AA6236500CDF9C8 /* PBXContainerItemProxy */ = { + 1341801A1AA91740003F314A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 587650F31A9EB120008B8F17 /* RCTNetworkImage.xcodeproj */; + containerPortal = 134180151AA91740003F314A /* RCTNetwork.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B511DB1A9E6C8500147676; + remoteInfo = RCTNetwork; + }; + 13442C041AA90E7D0037E5B0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 13442C001AA90E7D0037E5B0 /* RCTImage.xcodeproj */; proxyType = 2; remoteGlobalIDString = 58B5115D1A9E6B3D00147676; - remoteInfo = RCTNetworkImage; + remoteInfo = RCTImage; }; 58C5725A1AA6236500CDF9C8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -32,13 +39,6 @@ remoteGlobalIDString = 58B5119B1A9E6C1200147676; remoteInfo = RCTText; }; - 58C572621AA6236600CDF9C8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 587650F01A9EB120008B8F17 /* RCTDataManager.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B511DB1A9E6C8500147676; - remoteInfo = RCTDataManager; - }; 58C572671AA6236600CDF9C8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 587650F91A9EB120008B8F17 /* ReactKit.xcodeproj */; @@ -49,6 +49,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 134180151AA91740003F314A /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = ""; }; + 13442C001AA90E7D0037E5B0 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../Libraries/Image/RCTImage.xcodeproj; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* Movies.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Movies.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -56,8 +58,6 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 587650F01A9EB120008B8F17 /* RCTDataManager.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTDataManager.xcodeproj; path = ../../Libraries/Network/RCTDataManager.xcodeproj; sourceTree = SOURCE_ROOT; }; - 587650F31A9EB120008B8F17 /* RCTNetworkImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetworkImage.xcodeproj; path = ../../Libraries/Image/RCTNetworkImage.xcodeproj; sourceTree = SOURCE_ROOT; }; 587650F61A9EB120008B8F17 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = SOURCE_ROOT; }; 587650F91A9EB120008B8F17 /* ReactKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactKit.xcodeproj; path = ../../ReactKit/ReactKit.xcodeproj; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ @@ -67,8 +67,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 58C572691AA6239800CDF9C8 /* libRCTDataManager.a in Frameworks */, - 58C5726A1AA6239B00CDF9C8 /* libRCTNetworkImage.a in Frameworks */, + 1341801E1AA91750003F314A /* libRCTNetwork.a in Frameworks */, + 13442C061AA90EA00037E5B0 /* libRCTImage.a in Frameworks */, 58C5726B1AA6239E00CDF9C8 /* libRCTText.a in Frameworks */, 58C5726C1AA623A200CDF9C8 /* libReactKit.a in Frameworks */, ); @@ -77,6 +77,22 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 134180161AA91740003F314A /* Products */ = { + isa = PBXGroup; + children = ( + 1341801B1AA91740003F314A /* libRCTNetwork.a */, + ); + name = Products; + sourceTree = ""; + }; + 13442C011AA90E7D0037E5B0 /* Products */ = { + isa = PBXGroup; + children = ( + 13442C051AA90E7D0037E5B0 /* libRCTImage.a */, + ); + name = Products; + sourceTree = ""; + }; 13B07FAE1A68108700A75B9A /* Movies */ = { isa = PBXGroup; children = ( @@ -93,22 +109,14 @@ 58C571FC1AA6124500CDF9C8 /* Libraries */ = { isa = PBXGroup; children = ( - 587650F01A9EB120008B8F17 /* RCTDataManager.xcodeproj */, - 587650F31A9EB120008B8F17 /* RCTNetworkImage.xcodeproj */, + 134180151AA91740003F314A /* RCTNetwork.xcodeproj */, + 13442C001AA90E7D0037E5B0 /* RCTImage.xcodeproj */, 587650F61A9EB120008B8F17 /* RCTText.xcodeproj */, 587650F91A9EB120008B8F17 /* ReactKit.xcodeproj */, ); name = Libraries; sourceTree = ""; }; - 58C572521AA6236500CDF9C8 /* Products */ = { - isa = PBXGroup; - children = ( - 58C572561AA6236500CDF9C8 /* libRCTNetworkImage.a */, - ); - name = Products; - sourceTree = ""; - }; 58C572571AA6236500CDF9C8 /* Products */ = { isa = PBXGroup; children = ( @@ -125,14 +133,6 @@ name = Products; sourceTree = ""; }; - 58C5725E1AA6236500CDF9C8 /* Products */ = { - isa = PBXGroup; - children = ( - 58C572631AA6236600CDF9C8 /* libRCTDataManager.a */, - ); - name = Products; - sourceTree = ""; - }; 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( @@ -192,12 +192,12 @@ projectDirPath = ""; projectReferences = ( { - ProductGroup = 58C5725E1AA6236500CDF9C8 /* Products */; - ProjectRef = 587650F01A9EB120008B8F17 /* RCTDataManager.xcodeproj */; + ProductGroup = 13442C011AA90E7D0037E5B0 /* Products */; + ProjectRef = 13442C001AA90E7D0037E5B0 /* RCTImage.xcodeproj */; }, { - ProductGroup = 58C572521AA6236500CDF9C8 /* Products */; - ProjectRef = 587650F31A9EB120008B8F17 /* RCTNetworkImage.xcodeproj */; + ProductGroup = 134180161AA91740003F314A /* Products */; + ProjectRef = 134180151AA91740003F314A /* RCTNetwork.xcodeproj */; }, { ProductGroup = 58C572571AA6236500CDF9C8 /* Products */; @@ -216,11 +216,18 @@ /* End PBXProject section */ /* Begin PBXReferenceProxy section */ - 58C572561AA6236500CDF9C8 /* libRCTNetworkImage.a */ = { + 1341801B1AA91740003F314A /* libRCTNetwork.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = libRCTNetworkImage.a; - remoteRef = 58C572551AA6236500CDF9C8 /* PBXContainerItemProxy */; + path = libRCTNetwork.a; + remoteRef = 1341801A1AA91740003F314A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 13442C051AA90E7D0037E5B0 /* libRCTImage.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTImage.a; + remoteRef = 13442C041AA90E7D0037E5B0 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 58C5725B1AA6236500CDF9C8 /* libRCTText.a */ = { @@ -230,13 +237,6 @@ remoteRef = 58C5725A1AA6236500CDF9C8 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 58C572631AA6236600CDF9C8 /* libRCTDataManager.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTDataManager.a; - remoteRef = 58C572621AA6236600CDF9C8 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 58C572681AA6236600CDF9C8 /* libReactKit.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -295,6 +295,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", + "/Users/nicklockwood/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/Libraries/Network/build/Debug-iphoneos", ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = Movies; @@ -314,6 +315,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", + "/Users/nicklockwood/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/Libraries/Network/build/Debug-iphoneos", ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = Movies; diff --git a/Examples/TicTacToe/TicTacToe.xcodeproj/project.pbxproj b/Examples/TicTacToe/TicTacToe.xcodeproj/project.pbxproj index 4d0d681de..7cc9c357b 100644 --- a/Examples/TicTacToe/TicTacToe.xcodeproj/project.pbxproj +++ b/Examples/TicTacToe/TicTacToe.xcodeproj/project.pbxproj @@ -7,29 +7,22 @@ objects = { /* Begin PBXBuildFile section */ + 1341803E1AA91802003F314A /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341803D1AA917ED003F314A /* libRCTImage.a */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 58C571F31AA611C900CDF9C8 /* libRCTNetworkImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C571ED1AA611BA00CDF9C8 /* libRCTNetworkImage.a */; }; 58C572501AA6229900CDF9C8 /* libReactKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C572471AA6224300CDF9C8 /* libReactKit.a */; }; 58C572511AA6229D00CDF9C8 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C5724D1AA6224400CDF9C8 /* libRCTText.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 58C571EC1AA611BA00CDF9C8 /* PBXContainerItemProxy */ = { + 1341803C1AA917ED003F314A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 58C571E71AA611BA00CDF9C8 /* RCTNetworkImage.xcodeproj */; + containerPortal = 134180381AA917ED003F314A /* RCTImage.xcodeproj */; proxyType = 2; remoteGlobalIDString = 58B5115D1A9E6B3D00147676; - remoteInfo = RCTNetworkImage; - }; - 58C571EE1AA611BA00CDF9C8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 58C571E71AA611BA00CDF9C8 /* RCTNetworkImage.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B511681A9E6B3D00147676; - remoteInfo = RCTNetworkImageTests; + remoteInfo = RCTImage; }; 58C572461AA6224300CDF9C8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -45,16 +38,10 @@ remoteGlobalIDString = 58B5119B1A9E6C1200147676; remoteInfo = RCTText; }; - 58C5724E1AA6224400CDF9C8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 587650DA1A9EB0DB008B8F17 /* RCTText.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B511A61A9E6C1300147676; - remoteInfo = RCTTextTests; - }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 134180381AA917ED003F314A /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../Libraries/Image/RCTImage.xcodeproj; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* TicTacToe.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TicTacToe.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -64,7 +51,6 @@ 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 587650DA1A9EB0DB008B8F17 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = ""; }; 587650E31A9EB0DF008B8F17 /* ReactKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactKit.xcodeproj; path = ../../ReactKit/ReactKit.xcodeproj; sourceTree = ""; }; - 58C571E71AA611BA00CDF9C8 /* RCTNetworkImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetworkImage.xcodeproj; path = ../../Libraries/Image/RCTNetworkImage.xcodeproj; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -72,7 +58,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 58C571F31AA611C900CDF9C8 /* libRCTNetworkImage.a in Frameworks */, + 1341803E1AA91802003F314A /* libRCTImage.a in Frameworks */, 58C572501AA6229900CDF9C8 /* libReactKit.a in Frameworks */, 58C572511AA6229D00CDF9C8 /* libRCTText.a in Frameworks */, ); @@ -81,6 +67,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 134180391AA917ED003F314A /* Products */ = { + isa = PBXGroup; + children = ( + 1341803D1AA917ED003F314A /* libRCTImage.a */, + ); + name = Products; + sourceTree = ""; + }; 13B07FAE1A68108700A75B9A /* TicTacToe */ = { isa = PBXGroup; children = ( @@ -94,19 +88,10 @@ name = TicTacToe; sourceTree = ""; }; - 58C571E81AA611BA00CDF9C8 /* Products */ = { - isa = PBXGroup; - children = ( - 58C571ED1AA611BA00CDF9C8 /* libRCTNetworkImage.a */, - 58C571EF1AA611BA00CDF9C8 /* RCTNetworkImageTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; 58C572071AA6126D00CDF9C8 /* Libraries */ = { isa = PBXGroup; children = ( - 58C571E71AA611BA00CDF9C8 /* RCTNetworkImage.xcodeproj */, + 134180381AA917ED003F314A /* RCTImage.xcodeproj */, 587650DA1A9EB0DB008B8F17 /* RCTText.xcodeproj */, 587650E31A9EB0DF008B8F17 /* ReactKit.xcodeproj */, ); @@ -125,7 +110,6 @@ isa = PBXGroup; children = ( 58C5724D1AA6224400CDF9C8 /* libRCTText.a */, - 58C5724F1AA6224400CDF9C8 /* RCTTextTests.xctest */, ); name = Products; sourceTree = ""; @@ -189,8 +173,8 @@ projectDirPath = ""; projectReferences = ( { - ProductGroup = 58C571E81AA611BA00CDF9C8 /* Products */; - ProjectRef = 58C571E71AA611BA00CDF9C8 /* RCTNetworkImage.xcodeproj */; + ProductGroup = 134180391AA917ED003F314A /* Products */; + ProjectRef = 134180381AA917ED003F314A /* RCTImage.xcodeproj */; }, { ProductGroup = 58C572481AA6224300CDF9C8 /* Products */; @@ -209,18 +193,11 @@ /* End PBXProject section */ /* Begin PBXReferenceProxy section */ - 58C571ED1AA611BA00CDF9C8 /* libRCTNetworkImage.a */ = { + 1341803D1AA917ED003F314A /* libRCTImage.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = libRCTNetworkImage.a; - remoteRef = 58C571EC1AA611BA00CDF9C8 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 58C571EF1AA611BA00CDF9C8 /* RCTNetworkImageTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = RCTNetworkImageTests.xctest; - remoteRef = 58C571EE1AA611BA00CDF9C8 /* PBXContainerItemProxy */; + path = libRCTImage.a; + remoteRef = 1341803C1AA917ED003F314A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 58C572471AA6224300CDF9C8 /* libReactKit.a */ = { @@ -237,13 +214,6 @@ remoteRef = 58C5724C1AA6224400CDF9C8 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 58C5724F1AA6224400CDF9C8 /* RCTTextTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = RCTTextTests.xctest; - remoteRef = 58C5724E1AA6224400CDF9C8 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ @@ -288,9 +258,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = TicTacToe; }; @@ -302,9 +270,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = TicTacToe; }; diff --git a/Examples/UIExplorer/TabBarExample.js b/Examples/UIExplorer/TabBarExample.js new file mode 100644 index 000000000..22dc86a8b --- /dev/null +++ b/Examples/UIExplorer/TabBarExample.js @@ -0,0 +1,101 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule TabBarExample + */ +'use strict'; + +var React = require('React'); +var TabBarIOS = require('TabBarIOS'); +var TabBarItemIOS = require('TabBarItemIOS'); +var StyleSheet = require('StyleSheet'); +var Text = require('Text'); +var View = require('View'); + +var ix = require('ix'); + +var TabBarExample = React.createClass({ + + statics: { + title: '', + description: 'Tab-based navigation.' + }, + + getInitialState: function() { + return { + selectedTab: 'redTab', + notifCount: 0, + presses: 0, + }; + }, + + _renderContent: function(color, pageText) { + return ( + + {pageText} + {this.state.presses} re-renders of this tab + + ); + }, + + render: function() { + return ( + + { + this.setState({ + selectedTab: 'blueTab', + }); + }}> + {this._renderContent('#414A8C', 'Blue Tab')} + + { + this.setState({ + selectedTab: 'redTab', + notifCount: this.state.notifCount + 1, + }); + }}> + {this._renderContent('#783E33', 'Red Tab')} + + { + this.setState({ + selectedTab: 'greenTab', + presses: this.state.presses + 1 + }); + }}> + {this._renderContent('#21551C', 'Green Tab')} + + + ); + }, + +}); + +var styles = StyleSheet.create({ + tabContent: { + flex: 1, + alignItems: 'center', + }, + tabText: { + color: 'white', + margin: 50, + }, +}); + +module.exports = TabBarExample; diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj index daa467234..942ec24b0 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj @@ -7,48 +7,52 @@ objects = { /* Begin PBXBuildFile section */ - 1316A21A1AA397CA00C0188E /* libRCTNetworkImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1316A2101AA3871A00C0188E /* libRCTNetworkImage.a */; }; - 1316A21B1AA397CA00C0188E /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1316A2081AA386C700C0188E /* libRCTText.a */; }; - 1316A21C1AA397CA00C0188E /* libReactKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1316A2171AA3875D00C0188E /* libReactKit.a */; }; + 13417FE91AA91432003F314A /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FE81AA91428003F314A /* libRCTImage.a */; }; + 134180011AA9153C003F314A /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FEF1AA914B8003F314A /* libRCTText.a */; }; + 134180021AA9153C003F314A /* libReactKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FFF1AA91531003F314A /* libReactKit.a */; }; + 1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341802B1AA91779003F314A /* libRCTNetwork.a */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 58C572891AA624A900CDF9C8 /* libRCTDataManager.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C572851AA6249E00CDF9C8 /* libRCTDataManager.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 1316A2071AA386C700C0188E /* PBXContainerItemProxy */ = { + 13417FE71AA91428003F314A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 5876511F1A9EB168008B8F17 /* RCTText.xcodeproj */; + containerPortal = 13417FE31AA91428003F314A /* RCTImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B5115D1A9E6B3D00147676; + remoteInfo = RCTImage; + }; + 13417FEE1AA914B8003F314A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 13417FEA1AA914B8003F314A /* RCTText.xcodeproj */; proxyType = 2; remoteGlobalIDString = 58B5119B1A9E6C1200147676; remoteInfo = RCTText; }; - 1316A20F1AA3871A00C0188E /* PBXContainerItemProxy */ = { + 13417FFE1AA91531003F314A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 5876511C1A9EB168008B8F17 /* RCTNetworkImage.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5115D1A9E6B3D00147676; - remoteInfo = RCTNetworkImage; - }; - 1316A2161AA3875D00C0188E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 587651221A9EB168008B8F17 /* ReactKit.xcodeproj */; + containerPortal = 13417FFA1AA91531003F314A /* ReactKit.xcodeproj */; proxyType = 2; remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; remoteInfo = ReactKit; }; - 58C572841AA6249E00CDF9C8 /* PBXContainerItemProxy */ = { + 1341802A1AA91779003F314A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 587651491A9F8619008B8F17 /* RCTDataManager.xcodeproj */; + containerPortal = 134180261AA91779003F314A /* RCTNetwork.xcodeproj */; proxyType = 2; remoteGlobalIDString = 58B511DB1A9E6C8500147676; - remoteInfo = RCTDataManager; + remoteInfo = RCTNetwork; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 13417FE31AA91428003F314A /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../Libraries/Image/RCTImage.xcodeproj; sourceTree = ""; }; + 13417FEA1AA914B8003F314A /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = ""; }; + 13417FFA1AA91531003F314A /* ReactKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactKit.xcodeproj; path = ../../ReactKit/ReactKit.xcodeproj; sourceTree = ""; }; + 134180261AA91779003F314A /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* UIExplorer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UIExplorer.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -56,10 +60,6 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 5876511C1A9EB168008B8F17 /* RCTNetworkImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetworkImage.xcodeproj; path = ../../Libraries/Image/RCTNetworkImage.xcodeproj; sourceTree = SOURCE_ROOT; }; - 5876511F1A9EB168008B8F17 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = SOURCE_ROOT; }; - 587651221A9EB168008B8F17 /* ReactKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactKit.xcodeproj; path = ../../ReactKit/ReactKit.xcodeproj; sourceTree = SOURCE_ROOT; }; - 587651491A9F8619008B8F17 /* RCTDataManager.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTDataManager.xcodeproj; path = ../../Libraries/Network/RCTDataManager.xcodeproj; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -67,51 +67,59 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1316A21A1AA397CA00C0188E /* libRCTNetworkImage.a in Frameworks */, - 1316A21B1AA397CA00C0188E /* libRCTText.a in Frameworks */, - 58C572891AA624A900CDF9C8 /* libRCTDataManager.a in Frameworks */, - 1316A21C1AA397CA00C0188E /* libReactKit.a in Frameworks */, + 1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */, + 134180011AA9153C003F314A /* libRCTText.a in Frameworks */, + 134180021AA9153C003F314A /* libReactKit.a in Frameworks */, + 13417FE91AA91432003F314A /* libRCTImage.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 1316A2031AA386C700C0188E /* Products */ = { - isa = PBXGroup; - children = ( - 1316A2081AA386C700C0188E /* libRCTText.a */, - ); - name = Products; - sourceTree = ""; - }; - 1316A20B1AA3871A00C0188E /* Products */ = { - isa = PBXGroup; - children = ( - 1316A2101AA3871A00C0188E /* libRCTNetworkImage.a */, - ); - name = Products; - sourceTree = ""; - }; - 1316A2131AA3875D00C0188E /* Products */ = { - isa = PBXGroup; - children = ( - 1316A2171AA3875D00C0188E /* libReactKit.a */, - ); - name = Products; - sourceTree = ""; - }; 1316A21D1AA397F400C0188E /* Libraries */ = { isa = PBXGroup; children = ( - 587651221A9EB168008B8F17 /* ReactKit.xcodeproj */, - 587651491A9F8619008B8F17 /* RCTDataManager.xcodeproj */, - 5876511C1A9EB168008B8F17 /* RCTNetworkImage.xcodeproj */, - 5876511F1A9EB168008B8F17 /* RCTText.xcodeproj */, + 13417FFA1AA91531003F314A /* ReactKit.xcodeproj */, + 134180261AA91779003F314A /* RCTNetwork.xcodeproj */, + 13417FEA1AA914B8003F314A /* RCTText.xcodeproj */, + 13417FE31AA91428003F314A /* RCTImage.xcodeproj */, ); name = Libraries; sourceTree = ""; }; + 13417FE41AA91428003F314A /* Products */ = { + isa = PBXGroup; + children = ( + 13417FE81AA91428003F314A /* libRCTImage.a */, + ); + name = Products; + sourceTree = ""; + }; + 13417FEB1AA914B8003F314A /* Products */ = { + isa = PBXGroup; + children = ( + 13417FEF1AA914B8003F314A /* libRCTText.a */, + ); + name = Products; + sourceTree = ""; + }; + 13417FFB1AA91531003F314A /* Products */ = { + isa = PBXGroup; + children = ( + 13417FFF1AA91531003F314A /* libReactKit.a */, + ); + name = Products; + sourceTree = ""; + }; + 134180271AA91779003F314A /* Products */ = { + isa = PBXGroup; + children = ( + 1341802B1AA91779003F314A /* libRCTNetwork.a */, + ); + name = Products; + sourceTree = ""; + }; 13B07FAE1A68108700A75B9A /* UIExplorer */ = { isa = PBXGroup; children = ( @@ -125,14 +133,6 @@ name = UIExplorer; sourceTree = ""; }; - 58C572811AA6249E00CDF9C8 /* Products */ = { - isa = PBXGroup; - children = ( - 58C572851AA6249E00CDF9C8 /* libRCTDataManager.a */, - ); - name = Products; - sourceTree = ""; - }; 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( @@ -192,20 +192,20 @@ projectDirPath = ""; projectReferences = ( { - ProductGroup = 58C572811AA6249E00CDF9C8 /* Products */; - ProjectRef = 587651491A9F8619008B8F17 /* RCTDataManager.xcodeproj */; + ProductGroup = 13417FE41AA91428003F314A /* Products */; + ProjectRef = 13417FE31AA91428003F314A /* RCTImage.xcodeproj */; }, { - ProductGroup = 1316A20B1AA3871A00C0188E /* Products */; - ProjectRef = 5876511C1A9EB168008B8F17 /* RCTNetworkImage.xcodeproj */; + ProductGroup = 134180271AA91779003F314A /* Products */; + ProjectRef = 134180261AA91779003F314A /* RCTNetwork.xcodeproj */; }, { - ProductGroup = 1316A2031AA386C700C0188E /* Products */; - ProjectRef = 5876511F1A9EB168008B8F17 /* RCTText.xcodeproj */; + ProductGroup = 13417FEB1AA914B8003F314A /* Products */; + ProjectRef = 13417FEA1AA914B8003F314A /* RCTText.xcodeproj */; }, { - ProductGroup = 1316A2131AA3875D00C0188E /* Products */; - ProjectRef = 587651221A9EB168008B8F17 /* ReactKit.xcodeproj */; + ProductGroup = 13417FFB1AA91531003F314A /* Products */; + ProjectRef = 13417FFA1AA91531003F314A /* ReactKit.xcodeproj */; }, ); projectRoot = ""; @@ -216,32 +216,32 @@ /* End PBXProject section */ /* Begin PBXReferenceProxy section */ - 1316A2081AA386C700C0188E /* libRCTText.a */ = { + 13417FE81AA91428003F314A /* libRCTImage.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTImage.a; + remoteRef = 13417FE71AA91428003F314A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 13417FEF1AA914B8003F314A /* libRCTText.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTText.a; - remoteRef = 1316A2071AA386C700C0188E /* PBXContainerItemProxy */; + remoteRef = 13417FEE1AA914B8003F314A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 1316A2101AA3871A00C0188E /* libRCTNetworkImage.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTNetworkImage.a; - remoteRef = 1316A20F1AA3871A00C0188E /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 1316A2171AA3875D00C0188E /* libReactKit.a */ = { + 13417FFF1AA91531003F314A /* libReactKit.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libReactKit.a; - remoteRef = 1316A2161AA3875D00C0188E /* PBXContainerItemProxy */; + remoteRef = 13417FFE1AA91531003F314A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 58C572851AA6249E00CDF9C8 /* libRCTDataManager.a */ = { + 1341802B1AA91779003F314A /* libRCTNetwork.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = libRCTDataManager.a; - remoteRef = 58C572841AA6249E00CDF9C8 /* PBXContainerItemProxy */; + path = libRCTNetwork.a; + remoteRef = 1341802A1AA91779003F314A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXReferenceProxy section */ @@ -293,9 +293,7 @@ ); INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = UIExplorer; }; @@ -312,9 +310,7 @@ ); INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = UIExplorer; }; diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js index f6d55db36..606840567 100644 --- a/Examples/UIExplorer/UIExplorerList.js +++ b/Examples/UIExplorer/UIExplorerList.js @@ -32,6 +32,7 @@ var EXAMPLES = [ require('./ActivityIndicatorExample'), require('./ScrollViewExample'), require('./GeoLocationExample'), + require('./TabBarExample'), ]; var UIExplorerList = React.createClass({ diff --git a/Libraries/Components/TabBarIOS/TabBarIOS.android.js b/Libraries/Components/TabBarIOS/TabBarIOS.android.js new file mode 100644 index 000000000..7bddc1e92 --- /dev/null +++ b/Libraries/Components/TabBarIOS/TabBarIOS.android.js @@ -0,0 +1,29 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule TabBarIOS + */ + +'use strict'; + +var React = require('React'); +var View = require('View'); +var StyleSheet = require('StyleSheet'); + +var DummyTabBarIOS = React.createClass({ + render: function() { + return ( + + {this.props.children} + + ); + } +}); + +var styles = StyleSheet.create({ + tabGroup: { + flex: 1, + } +}); + +module.exports = DummyTabBarIOS; diff --git a/Libraries/Components/TabBarIOS/TabBarIOS.ios.js b/Libraries/Components/TabBarIOS/TabBarIOS.ios.js new file mode 100644 index 000000000..e3021b9b7 --- /dev/null +++ b/Libraries/Components/TabBarIOS/TabBarIOS.ios.js @@ -0,0 +1,36 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule TabBarIOS + */ +'use strict'; + +var React = require('React'); +var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); +var StyleSheet = require('StyleSheet'); + +var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass'); + +var TabBarIOS = React.createClass({ + render: function() { + return ( + + {this.props.children} + + ); + } +}); + +var styles = StyleSheet.create({ + tabGroup: { + flex: 1, + } +}); + +var config = { + validAttributes: ReactIOSViewAttributes.UIView, + uiViewClassName: 'RCTTabBar', +}; +var RKTabBar = createReactIOSNativeComponentClass(config); + +module.exports = TabBarIOS; diff --git a/Libraries/Components/TabBarIOS/TabBarItemIOS.android.js b/Libraries/Components/TabBarIOS/TabBarItemIOS.android.js new file mode 100644 index 000000000..634cc06c4 --- /dev/null +++ b/Libraries/Components/TabBarIOS/TabBarItemIOS.android.js @@ -0,0 +1,38 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule TabBarItemIOS + */ + +'use strict'; + +var Dimensions = require('Dimensions'); +var React = require('React'); +var View = require('View'); +var StyleSheet = require('StyleSheet'); + +var DummyTab = React.createClass({ + render: function() { + if (!this.props.selected) { + return ; + } + return ( + + {this.props.children} + + ); + } +}); + +var styles = StyleSheet.create({ + tab: { + // TODO(5405356): Implement overflow: visible so position: absolute isn't useless + // position: 'absolute', + width: Dimensions.get('window').width, + height: Dimensions.get('window').height, + borderColor: 'red', + borderWidth: 1, + } +}); + +module.exports = DummyTab; diff --git a/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js new file mode 100644 index 000000000..5e6cc5307 --- /dev/null +++ b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js @@ -0,0 +1,94 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule TabBarItemIOS + */ +'use strict'; + +var Image = require('Image'); +var React = require('React'); +var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); +var Dimensions = require('Dimensions'); +var StaticContainer = require('StaticContainer.react'); +var StyleSheet = require('StyleSheet'); +var View = require('View'); + +var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass'); +var merge = require('merge'); + +var TabBarItemIOS = React.createClass({ + propTypes: { + icon: Image.sourcePropType.isRequired, + onPress: React.PropTypes.func.isRequired, + selected: React.PropTypes.bool.isRequired, + badgeValue: React.PropTypes.string, + title: React.PropTypes.string, + style: View.stylePropType, + }, + + getInitialState: function() { + return { + hasBeenSelected: false, + }; + }, + + componentWillMount: function() { + if (this.props.selected) { + this.setState({hasBeenSelected: true}); + } + }, + + componentWillReceiveProps: function(nextProps) { + if (this.state.hasBeenSelected || nextProps.selected) { + this.setState({hasBeenSelected: true}); + } + }, + + render: function() { + var tabContents = null; + // if the tab has already been shown once, always continue to show it so we + // preserve state between tab transitions + if (this.state.hasBeenSelected) { + tabContents = + + {this.props.children} + ; + } else { + tabContents = ; + } + + return ( + + {tabContents} + + ); + } +}); + +var styles = StyleSheet.create({ + tab: { + position: 'absolute', + width: Dimensions.get('window').width, + height: Dimensions.get('window').height, + } +}); + +var RKTabBarItem = createReactIOSNativeComponentClass({ + validAttributes: merge(ReactIOSViewAttributes.UIView, { + title: true, + icon: true, + selectedIcon: true, + selected: true, + badgeValue: true, + }), + uiViewClassName: 'RCTTabBarItem', +}); + +module.exports = TabBarItemIOS; diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index 060125ea7..826c395bc 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -71,7 +71,7 @@ var View = React.createClass({ accessible: PropTypes.bool, /** - * This string can be used to identify the accessible element. + * Used to locate this view in end-to-end tests. */ testID: PropTypes.string, diff --git a/Libraries/Image/RCTGIFImage.h b/Libraries/Image/RCTGIFImage.h new file mode 100644 index 000000000..38f290462 --- /dev/null +++ b/Libraries/Image/RCTGIFImage.h @@ -0,0 +1,8 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import +#import +#import + +extern CAKeyframeAnimation *RCTGIFImageWithData(NSData *data); +extern CAKeyframeAnimation *RCTGIFImageWithFileURL(NSURL *URL); diff --git a/Libraries/Image/RCTGIFImage.m b/Libraries/Image/RCTGIFImage.m new file mode 100644 index 000000000..8fe49f6bc --- /dev/null +++ b/Libraries/Image/RCTGIFImage.m @@ -0,0 +1,91 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "RCTGIFImage.h" + +#import "RCTLog.h" + +static CAKeyframeAnimation *RCTGIFImageWithImageSource(CGImageSourceRef imageSource) +{ + if (!UTTypeConformsTo(CGImageSourceGetType(imageSource), kUTTypeGIF)) { + CFRelease(imageSource); + return nil; + } + + NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(imageSource, NULL); + NSUInteger loopCount = [properties[(id)kCGImagePropertyGIFDictionary][(id)kCGImagePropertyGIFLoopCount] unsignedIntegerValue]; + + size_t imageCount = CGImageSourceGetCount(imageSource); + NSTimeInterval duration = 0; + NSMutableArray *delays = [NSMutableArray arrayWithCapacity:imageCount]; + NSMutableArray *images = [NSMutableArray arrayWithCapacity:imageCount]; + for (size_t i = 0; i < imageCount; i++) { + CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, i, NULL); + NSDictionary *frameProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, i, NULL); + NSDictionary *frameGIFProperties = frameProperties[(id)kCGImagePropertyGIFDictionary]; + + const NSTimeInterval kDelayTimeIntervalDefault = 0.1; + NSNumber *delayTime = frameGIFProperties[(id)kCGImagePropertyGIFUnclampedDelayTime] ?: frameGIFProperties[(id)kCGImagePropertyGIFDelayTime]; + if (delayTime == nil) { + if (i == 0) { + delayTime = @(kDelayTimeIntervalDefault); + } else { + delayTime = delays[i - 1]; + } + } + + const NSTimeInterval kDelayTimeIntervalMinimum = 0.02; + if (delayTime.floatValue < (float)kDelayTimeIntervalMinimum - FLT_EPSILON) { + delayTime = @(kDelayTimeIntervalDefault); + } + + duration += delayTime.doubleValue; + delays[i] = delayTime; + images[i] = (__bridge_transfer id)image; + } + + NSMutableArray *keyTimes = [NSMutableArray arrayWithCapacity:delays.count]; + NSTimeInterval runningDuration = 0; + for (NSNumber *delayNumber in delays) { + [keyTimes addObject:@(runningDuration / duration)]; + runningDuration += delayNumber.doubleValue; + } + + [keyTimes addObject:@1.0]; + + CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"]; + animation.calculationMode = kCAAnimationDiscrete; + animation.repeatCount = loopCount == 0 ? HUGE_VALF : loopCount; + animation.keyTimes = keyTimes; + animation.values = images; + animation.duration = duration; + return animation; +} + +CAKeyframeAnimation *RCTGIFImageWithData(NSData *data) +{ + if (data.length == 0) { + return nil; + } + + CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)data, NULL); + CAKeyframeAnimation *animation = RCTGIFImageWithImageSource(imageSource); + CFRelease(imageSource); + return animation; +} + +CAKeyframeAnimation *RCTGIFImageWithFileURL(NSURL *URL) +{ + if (!URL) { + return nil; + } + + if (![URL isFileURL]) { + RCTLogError(@"Loading remote image URLs synchronously is a really bad idea."); + return nil; + } + + CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)URL, NULL); + CAKeyframeAnimation *animation = RCTGIFImageWithImageSource(imageSource); + CFRelease(imageSource); + return animation; +} diff --git a/Libraries/Image/RCTNetworkImage.xcodeproj/project.pbxproj b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj similarity index 74% rename from Libraries/Image/RCTNetworkImage.xcodeproj/project.pbxproj rename to Libraries/Image/RCTImage.xcodeproj/project.pbxproj index 57b27a3c1..409d61d32 100644 --- a/Libraries/Image/RCTNetworkImage.xcodeproj/project.pbxproj +++ b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */; }; + 1304D5AC1AA8C4A30002E2BE /* RCTStaticImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */; }; + 1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */; }; 58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */; }; 58B511901A9E6BD600147676 /* RCTNetworkImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118C1A9E6BD600147676 /* RCTNetworkImageView.m */; }; 58B511911A9E6BD600147676 /* RCTNetworkImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118E1A9E6BD600147676 /* RCTNetworkImageViewManager.m */; }; @@ -25,7 +28,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 58B5115D1A9E6B3D00147676 /* libRCTNetworkImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTNetworkImage.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 1304D5A71AA8C4A30002E2BE /* RCTStaticImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImage.h; sourceTree = ""; }; + 1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStaticImage.m; sourceTree = ""; }; + 1304D5A91AA8C4A30002E2BE /* RCTStaticImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImageManager.h; sourceTree = ""; }; + 1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStaticImageManager.m; sourceTree = ""; }; + 1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTGIFImage.h; sourceTree = ""; }; + 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGIFImage.m; sourceTree = ""; }; + 58B5115D1A9E6B3D00147676 /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; }; 58B511891A9E6BD600147676 /* RCTImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageDownloader.h; sourceTree = ""; }; 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageDownloader.m; sourceTree = ""; }; 58B5118B1A9E6BD600147676 /* RCTNetworkImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTNetworkImageView.h; sourceTree = ""; }; @@ -48,12 +57,18 @@ 58B511541A9E6B3D00147676 = { isa = PBXGroup; children = ( + 1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */, + 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */, 58B511891A9E6BD600147676 /* RCTImageDownloader.h */, 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */, 58B5118B1A9E6BD600147676 /* RCTNetworkImageView.h */, 58B5118C1A9E6BD600147676 /* RCTNetworkImageView.m */, 58B5118D1A9E6BD600147676 /* RCTNetworkImageViewManager.h */, 58B5118E1A9E6BD600147676 /* RCTNetworkImageViewManager.m */, + 1304D5A71AA8C4A30002E2BE /* RCTStaticImage.h */, + 1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */, + 1304D5A91AA8C4A30002E2BE /* RCTStaticImageManager.h */, + 1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */, 58B5115E1A9E6B3D00147676 /* Products */, ); sourceTree = ""; @@ -61,7 +76,7 @@ 58B5115E1A9E6B3D00147676 /* Products */ = { isa = PBXGroup; children = ( - 58B5115D1A9E6B3D00147676 /* libRCTNetworkImage.a */, + 58B5115D1A9E6B3D00147676 /* libRCTImage.a */, ); name = Products; sourceTree = ""; @@ -69,9 +84,9 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 58B5115C1A9E6B3D00147676 /* RCTNetworkImage */ = { + 58B5115C1A9E6B3D00147676 /* RCTImage */ = { isa = PBXNativeTarget; - buildConfigurationList = 58B511711A9E6B3D00147676 /* Build configuration list for PBXNativeTarget "RCTNetworkImage" */; + buildConfigurationList = 58B511711A9E6B3D00147676 /* Build configuration list for PBXNativeTarget "RCTImage" */; buildPhases = ( 58B511591A9E6B3D00147676 /* Sources */, 58B5115A1A9E6B3D00147676 /* Frameworks */, @@ -81,9 +96,9 @@ ); dependencies = ( ); - name = RCTNetworkImage; + name = RCTImage; productName = RCTNetworkImage; - productReference = 58B5115D1A9E6B3D00147676 /* libRCTNetworkImage.a */; + productReference = 58B5115D1A9E6B3D00147676 /* libRCTImage.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ @@ -100,7 +115,7 @@ }; }; }; - buildConfigurationList = 58B511581A9E6B3D00147676 /* Build configuration list for PBXProject "RCTNetworkImage" */; + buildConfigurationList = 58B511581A9E6B3D00147676 /* Build configuration list for PBXProject "RCTImage" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; @@ -112,7 +127,7 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 58B5115C1A9E6B3D00147676 /* RCTNetworkImage */, + 58B5115C1A9E6B3D00147676 /* RCTImage */, ); }; /* End PBXProject section */ @@ -124,7 +139,10 @@ files = ( 58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */, 58B511911A9E6BD600147676 /* RCTNetworkImageViewManager.m in Sources */, + 1304D5AC1AA8C4A30002E2BE /* RCTStaticImageManager.m in Sources */, 58B511901A9E6BD600147676 /* RCTNetworkImageView.m in Sources */, + 1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */, + 1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -213,8 +231,12 @@ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/../../ReactKit/**", ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/UIExplorer-gjaibsjtheitasdxdtcvxxqavkvy/Build/Products/Debug-iphoneos", + ); OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_NAME = RCTImage; SKIP_INSTALL = YES; }; name = Debug; @@ -227,8 +249,12 @@ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/../../ReactKit/**", ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/UIExplorer-gjaibsjtheitasdxdtcvxxqavkvy/Build/Products/Debug-iphoneos", + ); OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_NAME = RCTImage; SKIP_INSTALL = YES; }; name = Release; @@ -236,7 +262,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 58B511581A9E6B3D00147676 /* Build configuration list for PBXProject "RCTNetworkImage" */ = { + 58B511581A9E6B3D00147676 /* Build configuration list for PBXProject "RCTImage" */ = { isa = XCConfigurationList; buildConfigurations = ( 58B5116F1A9E6B3D00147676 /* Debug */, @@ -245,7 +271,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 58B511711A9E6B3D00147676 /* Build configuration list for PBXNativeTarget "RCTNetworkImage" */ = { + 58B511711A9E6B3D00147676 /* Build configuration list for PBXNativeTarget "RCTImage" */ = { isa = XCConfigurationList; buildConfigurations = ( 58B511721A9E6B3D00147676 /* Debug */, diff --git a/Libraries/Image/RCTImageDownloader.m b/Libraries/Image/RCTImageDownloader.m index 9f065aaa3..ea801dea7 100644 --- a/Libraries/Image/RCTImageDownloader.m +++ b/Libraries/Image/RCTImageDownloader.m @@ -109,39 +109,37 @@ typedef void (^RCTCachedDataDownloadBlock)(BOOL cached, NSData *data, NSError *e - (id)downloadImageForURL:(NSURL *)url size:(CGSize)size scale:(CGFloat)scale block:(RCTImageDownloadBlock)block { - NSString *cacheKey = [self cacheKeyForURL:url]; - __weak RCTImageDownloader *weakSelf = self; - return [self _downloadDataForURL:url block:^(BOOL cached, NSData *data, NSError *error) { - if (!data) { - return dispatch_async(dispatch_get_main_queue(), ^{ - block(nil, error); - }); - } + return [self downloadDataForURL:url block:^(NSData *data, NSError *error) { UIImage *image = [UIImage imageWithData:data scale:scale]; - if (image) { + + // Resize (TODO: should we take aspect ratio into account?) CGSize imageSize = size; if (CGSizeEqualToSize(imageSize, CGSizeZero)) { imageSize = image.size; + } else { + imageSize = (CGSize){ + MIN(size.width, image.size.width), + MIN(size.height, image.size.height) + }; } + // Rescale image if required size is smaller CGFloat imageScale = scale; - if (imageScale == 0 || imageScale > image.scale) { + if (imageScale == 0 || imageScale < image.scale) { imageScale = image.scale; } + // Decompress image at required size UIGraphicsBeginImageContextWithOptions(imageSize, NO, imageScale); [image drawInRect:(CGRect){{0, 0}, imageSize}]; image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); - - if (!cached) { - RCTImageDownloader *strongSelf = weakSelf; - [strongSelf->_cache setData:UIImagePNGRepresentation(image) forKey:cacheKey]; - } } + // TODO: should we cache the decompressed image? + dispatch_async(dispatch_get_main_queue(), ^{ block(image, nil); }); diff --git a/Libraries/Image/RCTNetworkImageView.m b/Libraries/Image/RCTNetworkImageView.m index 027fbf500..1b19dfe19 100644 --- a/Libraries/Image/RCTNetworkImageView.m +++ b/Libraries/Image/RCTNetworkImageView.m @@ -2,9 +2,10 @@ #import "RCTNetworkImageView.h" +#import "RCTConvert.h" +#import "RCTGIFImage.h" #import "RCTImageDownloader.h" #import "RCTUtils.h" -#import "RCTConvert.h" @implementation RCTNetworkImageView { @@ -53,7 +54,7 @@ if ([imageURL.pathExtension caseInsensitiveCompare:@"gif"] == NSOrderedSame) { _downloadToken = [_imageDownloader downloadDataForURL:imageURL block:^(NSData *data, NSError *error) { if (data) { - CAKeyframeAnimation *animation = [RCTConvert GIF:data]; + CAKeyframeAnimation *animation = RCTGIFImageWithData(data); CGImageRef firstFrame = (__bridge CGImageRef)animation.values.firstObject; self.layer.bounds = CGRectMake(0, 0, CGImageGetWidth(firstFrame), CGImageGetHeight(firstFrame)); self.layer.contentsScale = 1.0; diff --git a/Libraries/Image/RCTNetworkImageViewManager.m b/Libraries/Image/RCTNetworkImageViewManager.m index 5f8ad5d35..96d09c0cd 100644 --- a/Libraries/Image/RCTNetworkImageViewManager.m +++ b/Libraries/Image/RCTNetworkImageViewManager.m @@ -23,4 +23,3 @@ RCT_REMAP_VIEW_PROPERTY(src, imageURL) RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode) @end - diff --git a/ReactKit/Views/RCTStaticImage.h b/Libraries/Image/RCTStaticImage.h similarity index 100% rename from ReactKit/Views/RCTStaticImage.h rename to Libraries/Image/RCTStaticImage.h diff --git a/ReactKit/Views/RCTStaticImage.m b/Libraries/Image/RCTStaticImage.m similarity index 100% rename from ReactKit/Views/RCTStaticImage.m rename to Libraries/Image/RCTStaticImage.m diff --git a/ReactKit/Views/RCTStaticImageManager.h b/Libraries/Image/RCTStaticImageManager.h similarity index 100% rename from ReactKit/Views/RCTStaticImageManager.h rename to Libraries/Image/RCTStaticImageManager.h diff --git a/ReactKit/Views/RCTStaticImageManager.m b/Libraries/Image/RCTStaticImageManager.m similarity index 88% rename from ReactKit/Views/RCTStaticImageManager.m rename to Libraries/Image/RCTStaticImageManager.m index 84b782b1d..b83d8c42b 100644 --- a/ReactKit/Views/RCTStaticImageManager.m +++ b/Libraries/Image/RCTStaticImageManager.m @@ -4,8 +4,9 @@ #import -#import "RCTStaticImage.h" #import "RCTConvert.h" +#import "RCTGIFImage.h" +#import "RCTStaticImage.h" @implementation RCTStaticImageManager @@ -20,7 +21,7 @@ RCT_CUSTOM_VIEW_PROPERTY(src, RCTStaticImage *) { if (json) { if ([[[json description] pathExtension] caseInsensitiveCompare:@"gif"] == NSOrderedSame) { - [view.layer addAnimation:[RCTConvert GIF:json] forKey:@"contents"]; + [view.layer addAnimation:RCTGIFImageWithFileURL([RCTConvert NSURL:json]) forKey:@"contents"]; } else { view.image = [RCTConvert UIImage:json]; } @@ -40,4 +41,3 @@ RCT_CUSTOM_VIEW_PROPERTY(tintColor, RCTStaticImage *) } @end - diff --git a/Libraries/Network/RCTDataManager.xcodeproj/project.pbxproj b/Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj similarity index 91% rename from Libraries/Network/RCTDataManager.xcodeproj/project.pbxproj rename to Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj index 81b90312a..bfaa0413d 100644 --- a/Libraries/Network/RCTDataManager.xcodeproj/project.pbxproj +++ b/Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj @@ -23,7 +23,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 58B511DB1A9E6C8500147676 /* libRCTDataManager.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTDataManager.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 58B511DB1A9E6C8500147676 /* libRCTNetwork.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTNetwork.a; sourceTree = BUILT_PRODUCTS_DIR; }; 58B512061A9E6CE300147676 /* RCTDataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDataManager.h; sourceTree = ""; }; 58B512071A9E6CE300147676 /* RCTDataManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDataManager.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -51,7 +51,7 @@ 58B511DC1A9E6C8500147676 /* Products */ = { isa = PBXGroup; children = ( - 58B511DB1A9E6C8500147676 /* libRCTDataManager.a */, + 58B511DB1A9E6C8500147676 /* libRCTNetwork.a */, ); name = Products; sourceTree = ""; @@ -59,9 +59,9 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 58B511DA1A9E6C8500147676 /* RCTDataManager */ = { + 58B511DA1A9E6C8500147676 /* RCTNetwork */ = { isa = PBXNativeTarget; - buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTDataManager" */; + buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTNetwork" */; buildPhases = ( 58B511D71A9E6C8500147676 /* Sources */, 58B511D81A9E6C8500147676 /* Frameworks */, @@ -71,9 +71,9 @@ ); dependencies = ( ); - name = RCTDataManager; + name = RCTNetwork; productName = RCTDataManager; - productReference = 58B511DB1A9E6C8500147676 /* libRCTDataManager.a */; + productReference = 58B511DB1A9E6C8500147676 /* libRCTNetwork.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ @@ -90,7 +90,7 @@ }; }; }; - buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTDataManager" */; + buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTNetwork" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; @@ -102,7 +102,7 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 58B511DA1A9E6C8500147676 /* RCTDataManager */, + 58B511DA1A9E6C8500147676 /* RCTNetwork */, ); }; /* End PBXProject section */ @@ -206,7 +206,7 @@ "/Users/nicklockwood/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/ReactKit/build/Debug-iphoneos", ); OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_NAME = RCTNetwork; SKIP_INSTALL = YES; }; name = Debug; @@ -224,7 +224,7 @@ "/Users/nicklockwood/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/ReactKit/build/Debug-iphoneos", ); OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_NAME = RCTNetwork; SKIP_INSTALL = YES; }; name = Release; @@ -232,7 +232,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTDataManager" */ = { + 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTNetwork" */ = { isa = XCConfigurationList; buildConfigurations = ( 58B511ED1A9E6C8500147676 /* Debug */, @@ -241,7 +241,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTDataManager" */ = { + 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTNetwork" */ = { isa = XCConfigurationList; buildConfigurations = ( 58B511F01A9E6C8500147676 /* Debug */, diff --git a/Libraries/Text/ExpandingText.js b/Libraries/Text/ExpandingText.js index 80b06492a..181e0a2b8 100644 --- a/Libraries/Text/ExpandingText.js +++ b/Libraries/Text/ExpandingText.js @@ -20,39 +20,49 @@ var styles = StyleSheet.create({ }); /** - * - A react component for displaying text which supports truncating - * based on a set truncLength. In the following example, the text will truncate + * A react component for displaying text which supports truncating + * based on a set truncLength. + * + * In the following example, the text will truncate * to show only the first 17 characters plus '...' with a See More button to - * expand the text to its full length + * expand the text to its full length. * - * renderText: function() { - * return ; - * }, - * - * More example code in `ExpandingTextExample.js` + * ``` + * render: function() { + * return ; + * }, + * ``` */ var ExpandingText = React.createClass({ - PropTypes: { + propTypes: { /** - * Text to be displayed. Text will be truncated if the character length - * is greater than the truncLength property. + * Text to be displayed. It will be truncated if the character length + * is greater than the `truncLength` property. */ - text: React.PropTypes.string.isRequired, + text: React.PropTypes.string, /** - * The styles that will be applied to the text (both truncated and expanded). + * The styles that will be applied to the text (both truncated and + * expanded). */ - textStyle: Text.stylePropType, + textStyle: Text.propTypes.style, /** - * The styles that will be applied to the See More button + * The styles that will be applied to the See More button. Default + * is bold. */ - seeMoreStyle: Text.stylePropType, + seeMoreStyle: Text.propTypes.style, + /** + * The caption that will be appended at the end, by default it is + * `'See More'`. + */ + seeMoreText: React.PropTypes.string, /** * The maximum character length for the text that will * be displayed by default. Note that ... will be * appended to the truncated text which is counted towards - * the total truncLength of the default displayed string + * the total truncLength of the default displayed string. + * The default is 130. */ - truncLength: React.PropTypes.number + truncLength: React.PropTypes.number, }, getDefaultProps: function() { diff --git a/Libraries/Text/Text.js b/Libraries/Text/Text.js index 07160bc66..71e022d9b 100644 --- a/Libraries/Text/Text.js +++ b/Libraries/Text/Text.js @@ -87,6 +87,10 @@ var Text = React.createClass({ */ suppressHighlighting: React.PropTypes.bool, style: stylePropType, + /** + * Used to locate this view in end-to-end tests. + */ + testID: React.PropTypes.string, }, viewConfig: viewConfig, diff --git a/ReactKit/Base/RCTConvert.h b/ReactKit/Base/RCTConvert.h index 25b8cecc2..a5aa59fe6 100644 --- a/ReactKit/Base/RCTConvert.h +++ b/ReactKit/Base/RCTConvert.h @@ -3,9 +3,9 @@ #import #import -#import "Layout.h" -#import "RCTPointerEvents.h" -#import "RCTAnimationType.h" +#import "../Layout/Layout.h" +#import "../Views/RCTAnimationType.h" +#import "../Views/RCTPointerEvents.h" /** * This class provides a collection of conversion functions for mapping @@ -47,7 +47,6 @@ + (UIColor *)UIColor:(id)json; + (CGColorRef)CGColor:(id)json; -+ (CAKeyframeAnimation *)GIF:(id)json; + (UIImage *)UIImage:(id)json; + (CGImageRef)CGImage:(id)json; @@ -68,6 +67,10 @@ @end +#ifdef __cplusplus +extern "C" { +#endif + /** * This function will attempt to set a property using a json value by first * inferring the correct type from all available information, and then @@ -82,3 +85,7 @@ BOOL RCTSetProperty(id target, NSString *keypath, id json); * be set, it will do nothing and return NO. */ BOOL RCTCopyProperty(id target, id source, NSString *keypath); + +#ifdef __cplusplus +} +#endif diff --git a/ReactKit/Base/RCTConvert.m b/ReactKit/Base/RCTConvert.m index 39b0c6874..bec60ff6c 100644 --- a/ReactKit/Base/RCTConvert.m +++ b/ReactKit/Base/RCTConvert.m @@ -4,9 +4,6 @@ #import -#import -#import - #import "RCTLog.h" CGFloat const RCTDefaultFontSize = 14; @@ -431,88 +428,11 @@ RCT_STRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty"] return [self UIColor:json].CGColor; } -+ (CAKeyframeAnimation *)GIF:(id)json -{ - CGImageSourceRef imageSource = NULL; - if ([json isKindOfClass:[NSString class]]) { - NSString *path = json; - if (path.length == 0) { - return nil; - } - - NSURL *fileURL = [path isAbsolutePath] ? [NSURL fileURLWithPath:path] : [[NSBundle mainBundle] URLForResource:path withExtension:nil]; - imageSource = CGImageSourceCreateWithURL((CFURLRef)fileURL, NULL); - } else if ([json isKindOfClass:[NSData class]]) { - NSData *data = json; - if (data.length == 0) { - return nil; - } - - imageSource = CGImageSourceCreateWithData((CFDataRef)data, NULL); - } else { - RCTLogMustFix(@"Expected NSString or NSData for GIF, received %@: %@", [json class], json); - return nil; - } - - if (!UTTypeConformsTo(CGImageSourceGetType(imageSource), kUTTypeGIF)) { - CFRelease(imageSource); - return nil; - } - - NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(imageSource, NULL); - NSUInteger loopCount = [properties[(id)kCGImagePropertyGIFDictionary][(id)kCGImagePropertyGIFLoopCount] unsignedIntegerValue]; - - size_t imageCount = CGImageSourceGetCount(imageSource); - NSTimeInterval duration = 0; - NSMutableArray *delays = [NSMutableArray arrayWithCapacity:imageCount]; - NSMutableArray *images = [NSMutableArray arrayWithCapacity:imageCount]; - for (size_t i = 0; i < imageCount; i++) { - CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, i, NULL); - NSDictionary *frameProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, i, NULL); - NSDictionary *frameGIFProperties = frameProperties[(id)kCGImagePropertyGIFDictionary]; - - const NSTimeInterval kDelayTimeIntervalDefault = 0.1; - NSNumber *delayTime = frameGIFProperties[(id)kCGImagePropertyGIFUnclampedDelayTime] ?: frameGIFProperties[(id)kCGImagePropertyGIFDelayTime]; - if (delayTime == nil) { - if (i == 0) { - delayTime = @(kDelayTimeIntervalDefault); - } else { - delayTime = delays[i - 1]; - } - } - - const NSTimeInterval kDelayTimeIntervalMinimum = 0.02; - if (delayTime.floatValue < (float)kDelayTimeIntervalMinimum - FLT_EPSILON) { - delayTime = @(kDelayTimeIntervalDefault); - } - - duration += delayTime.doubleValue; - delays[i] = delayTime; - images[i] = (__bridge_transfer id)image; - } - - CFRelease(imageSource); - - NSMutableArray *keyTimes = [NSMutableArray arrayWithCapacity:delays.count]; - NSTimeInterval runningDuration = 0; - for (NSNumber *delayNumber in delays) { - [keyTimes addObject:@(runningDuration / duration)]; - runningDuration += delayNumber.doubleValue; - } - - [keyTimes addObject:@1.0]; - - CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"]; - animation.calculationMode = kCAAnimationDiscrete; - animation.repeatCount = loopCount == 0 ? HUGE_VALF : loopCount; - animation.keyTimes = keyTimes; - animation.values = images; - animation.duration = duration; - return animation; -} - + (UIImage *)UIImage:(id)json { + // TODO: we might as well cache the result of these checks (and possibly the + // image itself) so as to reduce overhead on subsequent checks of the same input + if (![json isKindOfClass:[NSString class]]) { RCTLogError(@"Expected NSString for UIImage, received %@: %@", [json class], json); return nil; @@ -532,9 +452,8 @@ RCT_STRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty"] image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:path ofType:nil]]; } } - if (!image) { - RCTLogWarn(@"No image was found at path %@", json); - } + // NOTE: we don't warn about nil images because there are legitimate + // case where we find out if a string is an image by using this method return image; } diff --git a/ReactKit/Modules/RCTUIManager.m b/ReactKit/Modules/RCTUIManager.m index 5a65669cd..47b2e6c84 100644 --- a/ReactKit/Modules/RCTUIManager.m +++ b/ReactKit/Modules/RCTUIManager.m @@ -1109,8 +1109,8 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView // Bubble dispatched events @"topTap": @{ @"phasedRegistrationNames": @{ - @"bubbled": @"notActuallyTapDontUseMe", - @"captured": @"notActuallyTapCaptureDontUseMe" + @"bubbled": @"onPress", + @"captured": @"onPressCapture" } }, @"topVisibleCellsChange": @{ diff --git a/ReactKit/ReactKit.xcodeproj/project.pbxproj b/ReactKit/ReactKit.xcodeproj/project.pbxproj index 721556306..899d865b4 100644 --- a/ReactKit/ReactKit.xcodeproj/project.pbxproj +++ b/ReactKit/ReactKit.xcodeproj/project.pbxproj @@ -7,12 +7,14 @@ objects = { /* Begin PBXBuildFile section */ - 1302F0FD1A78550100EBEF02 /* RCTStaticImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1302F0FA1A78550100EBEF02 /* RCTStaticImage.m */; }; - 1302F0FE1A78550100EBEF02 /* RCTStaticImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1302F0FC1A78550100EBEF02 /* RCTStaticImageManager.m */; }; 134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */; }; 134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */; }; 134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3C1A6E7F0800051CC8 /* RCTWebViewExecutor.m */; }; 13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */; }; + 137327E71AA5CF210034F82E /* RCTTabBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E01AA5CF210034F82E /* RCTTabBar.m */; }; + 137327E81AA5CF210034F82E /* RCTTabBarItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E21AA5CF210034F82E /* RCTTabBarItem.m */; }; + 137327E91AA5CF210034F82E /* RCTTabBarItemManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E41AA5CF210034F82E /* RCTTabBarItemManager.m */; }; + 137327EA1AA5CF210034F82E /* RCTTabBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E61AA5CF210034F82E /* RCTTabBarManager.m */; }; 13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */; }; 13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FE81A69327A00A75B9A /* RCTAlertManager.m */; }; 13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FEA1A69327A00A75B9A /* RCTExceptionsManager.m */; }; @@ -60,21 +62,27 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 1302F0F91A78550100EBEF02 /* RCTStaticImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImage.h; sourceTree = ""; }; - 1302F0FA1A78550100EBEF02 /* RCTStaticImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStaticImage.m; sourceTree = ""; }; - 1302F0FB1A78550100EBEF02 /* RCTStaticImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImageManager.h; sourceTree = ""; }; - 1302F0FC1A78550100EBEF02 /* RCTStaticImageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStaticImageManager.m; sourceTree = ""; }; + 13442BF21AA90E0B0037E5B0 /* RCTAnimationType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationType.h; sourceTree = ""; }; + 13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = ""; }; + 13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewControllerProtocol.h; sourceTree = ""; }; 134FCB391A6E7F0800051CC8 /* RCTContextExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTContextExecutor.h; sourceTree = ""; }; 134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTContextExecutor.m; sourceTree = ""; }; 134FCB3B1A6E7F0800051CC8 /* RCTWebViewExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebViewExecutor.h; sourceTree = ""; }; 134FCB3C1A6E7F0800051CC8 /* RCTWebViewExecutor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebViewExecutor.m; sourceTree = ""; }; 13723B4E1A82FD3C00F88898 /* RCTStatusBarManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStatusBarManager.h; sourceTree = ""; }; 13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStatusBarManager.m; sourceTree = ""; }; + 137327DF1AA5CF210034F82E /* RCTTabBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTabBar.h; sourceTree = ""; }; + 137327E01AA5CF210034F82E /* RCTTabBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTabBar.m; sourceTree = ""; }; + 137327E11AA5CF210034F82E /* RCTTabBarItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTabBarItem.h; sourceTree = ""; }; + 137327E21AA5CF210034F82E /* RCTTabBarItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTabBarItem.m; sourceTree = ""; }; + 137327E31AA5CF210034F82E /* RCTTabBarItemManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTabBarItemManager.h; sourceTree = ""; }; + 137327E41AA5CF210034F82E /* RCTTabBarItemManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTabBarItemManager.m; sourceTree = ""; }; + 137327E51AA5CF210034F82E /* RCTTabBarManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTabBarManager.h; sourceTree = ""; }; + 137327E61AA5CF210034F82E /* RCTTabBarManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTabBarManager.m; sourceTree = ""; }; 13A1F71C1A75392D00D3D453 /* RCTKeyCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTKeyCommands.h; sourceTree = ""; }; 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTKeyCommands.m; sourceTree = ""; }; 13B07FC71A68125100A75B9A /* Layout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Layout.c; sourceTree = ""; }; 13B07FC81A68125100A75B9A /* Layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Layout.h; sourceTree = ""; }; - 13B07FCD1A683B5F00A75B9A /* RCTScrollableProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTScrollableProtocol.h; sourceTree = ""; }; 13B07FE71A69327A00A75B9A /* RCTAlertManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAlertManager.h; sourceTree = ""; }; 13B07FE81A69327A00A75B9A /* RCTAlertManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAlertManager.m; sourceTree = ""; }; 13B07FE91A69327A00A75B9A /* RCTExceptionsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTExceptionsManager.h; sourceTree = ""; }; @@ -101,7 +109,9 @@ 13B080191A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIActivityIndicatorViewManager.m; sourceTree = ""; }; 13B080231A694A8400A75B9A /* RCTWrapperViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWrapperViewController.h; sourceTree = ""; }; 13B080241A694A8400A75B9A /* RCTWrapperViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWrapperViewController.m; sourceTree = ""; }; - 13DB9D681A8CC58200429C20 /* RCTAnimationType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationType.h; sourceTree = ""; }; + 13C325261AA63B6A0048765F /* RCTAutoInsetsProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAutoInsetsProtocol.h; sourceTree = ""; }; + 13C325271AA63B6A0048765F /* RCTScrollableProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTScrollableProtocol.h; sourceTree = ""; }; + 13C325281AA63B6A0048765F /* RCTViewNodeProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewNodeProtocol.h; sourceTree = ""; }; 13E067481A70F434002CDEE1 /* RCTUIManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUIManager.h; sourceTree = ""; }; 13E067491A70F434002CDEE1 /* RCTUIManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManager.m; sourceTree = ""; }; 13E0674B1A70F44B002CDEE1 /* RCTShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowView.h; sourceTree = ""; }; @@ -112,7 +122,6 @@ 13E067501A70F44B002CDEE1 /* RCTView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTView.m; sourceTree = ""; }; 13E067531A70F44B002CDEE1 /* UIView+ReactKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+ReactKit.h"; sourceTree = ""; }; 13E067541A70F44B002CDEE1 /* UIView+ReactKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+ReactKit.m"; sourceTree = ""; }; - 13ED13891A80C9D40050A8F9 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = ""; }; 13EFFCCF1A98E6FE002607DC /* RCTJSMethodRegistrar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSMethodRegistrar.h; sourceTree = ""; }; 5F5F0D971A9E456B001279FA /* RCTLocationObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTLocationObserver.h; sourceTree = ""; }; 5F5F0D981A9E456B001279FA /* RCTLocationObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTLocationObserver.m; sourceTree = ""; }; @@ -135,8 +144,6 @@ 83CBBA591A601E9000E9B192 /* RCTRedBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRedBox.m; sourceTree = ""; }; 83CBBA5E1A601EAA00E9B192 /* RCTBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBridge.h; sourceTree = ""; }; 83CBBA5F1A601EAA00E9B192 /* RCTBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBridge.m; sourceTree = ""; }; - 83CBBA611A601EB200E9B192 /* RCTAutoInsetsProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTAutoInsetsProtocol.h; sourceTree = ""; }; - 83CBBA621A601EB800E9B192 /* RCTViewNodeProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTViewNodeProtocol.h; sourceTree = ""; }; 83CBBA631A601ECA00E9B192 /* RCTJavaScriptExecutor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTJavaScriptExecutor.h; sourceTree = ""; }; 83CBBA651A601EF300E9B192 /* RCTEventDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTEventDispatcher.h; sourceTree = ""; }; 83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTEventDispatcher.m; sourceTree = ""; }; @@ -199,6 +206,12 @@ 13B07FF31A6947C200A75B9A /* Views */ = { isa = PBXGroup; children = ( + 13442BF21AA90E0B0037E5B0 /* RCTAnimationType.h */, + 13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */, + 13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */, + 13C325261AA63B6A0048765F /* RCTAutoInsetsProtocol.h */, + 13C325271AA63B6A0048765F /* RCTScrollableProtocol.h */, + 13C325281AA63B6A0048765F /* RCTViewNodeProtocol.h */, 13B0800C1A69489C00A75B9A /* RCTNavigator.h */, 13B0800D1A69489C00A75B9A /* RCTNavigator.m */, 13B0800E1A69489C00A75B9A /* RCTNavigatorManager.h */, @@ -213,10 +226,6 @@ 13B07FF91A6947C200A75B9A /* RCTScrollViewManager.m */, 13E0674B1A70F44B002CDEE1 /* RCTShadowView.h */, 13E0674C1A70F44B002CDEE1 /* RCTShadowView.m */, - 1302F0F91A78550100EBEF02 /* RCTStaticImage.h */, - 1302F0FA1A78550100EBEF02 /* RCTStaticImage.m */, - 1302F0FB1A78550100EBEF02 /* RCTStaticImageManager.h */, - 1302F0FC1A78550100EBEF02 /* RCTStaticImageManager.m */, 13B080141A69489C00A75B9A /* RCTTextField.h */, 13B080151A69489C00A75B9A /* RCTTextField.m */, 13B080161A69489C00A75B9A /* RCTTextFieldManager.h */, @@ -229,6 +238,14 @@ 13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */, 13B080231A694A8400A75B9A /* RCTWrapperViewController.h */, 13B080241A694A8400A75B9A /* RCTWrapperViewController.m */, + 137327DF1AA5CF210034F82E /* RCTTabBar.h */, + 137327E01AA5CF210034F82E /* RCTTabBar.m */, + 137327E11AA5CF210034F82E /* RCTTabBarItem.h */, + 137327E21AA5CF210034F82E /* RCTTabBarItem.m */, + 137327E31AA5CF210034F82E /* RCTTabBarItemManager.h */, + 137327E41AA5CF210034F82E /* RCTTabBarItemManager.m */, + 137327E51AA5CF210034F82E /* RCTTabBarManager.h */, + 137327E61AA5CF210034F82E /* RCTTabBarManager.m */, 13E067531A70F44B002CDEE1 /* UIView+ReactKit.h */, 13E067541A70F44B002CDEE1 /* UIView+ReactKit.m */, ); @@ -266,10 +283,8 @@ 83CBBA491A601E3B00E9B192 /* Base */ = { isa = PBXGroup; children = ( - 13DB9D681A8CC58200429C20 /* RCTAnimationType.h */, 83CBBA4A1A601E3B00E9B192 /* RCTAssert.h */, 83CBBA4B1A601E3B00E9B192 /* RCTAssert.m */, - 83CBBA611A601EB200E9B192 /* RCTAutoInsetsProtocol.h */, 83CBBA5E1A601EAA00E9B192 /* RCTBridge.h */, 83CBBA5F1A601EAA00E9B192 /* RCTBridge.m */, 830213F31A654E0800B993E6 /* RCTBridgeModule.h */, @@ -286,19 +301,16 @@ 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */, 83CBBA4D1A601E3B00E9B192 /* RCTLog.h */, 83CBBA4E1A601E3B00E9B192 /* RCTLog.m */, - 13ED13891A80C9D40050A8F9 /* RCTPointerEvents.h */, 83CBBA581A601E9000E9B192 /* RCTRedBox.h */, 83CBBA591A601E9000E9B192 /* RCTRedBox.m */, 830A229C1A66C68A008503DA /* RCTRootView.h */, 830A229D1A66C68A008503DA /* RCTRootView.m */, - 13B07FCD1A683B5F00A75B9A /* RCTScrollableProtocol.h */, 83BEE46C1A6D19BC00B5863B /* RCTSparseArray.h */, 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */, 83CBBA961A6020BB00E9B192 /* RCTTouchHandler.h */, 83CBBA971A6020BB00E9B192 /* RCTTouchHandler.m */, 83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */, 83CBBA501A601E3B00E9B192 /* RCTUtils.m */, - 83CBBA621A601EB800E9B192 /* RCTViewNodeProtocol.h */, ); path = Base; sourceTree = ""; @@ -383,7 +395,6 @@ 83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */, 5F5F0D991A9E456B001279FA /* RCTLocationObserver.m in Sources */, 830A229E1A66C68A008503DA /* RCTRootView.m in Sources */, - 1302F0FD1A78550100EBEF02 /* RCTStaticImage.m in Sources */, 13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */, 83CBBA5A1A601E9000E9B192 /* RCTRedBox.m in Sources */, 83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */, @@ -391,6 +402,7 @@ 13B080201A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m in Sources */, 13E067561A70F44B002CDEE1 /* RCTViewManager.m in Sources */, 13B080061A6947C200A75B9A /* RCTScrollViewManager.m in Sources */, + 137327EA1AA5CF210034F82E /* RCTTabBarManager.m in Sources */, 13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */, 13B080051A6947C200A75B9A /* RCTScrollView.m in Sources */, 13B07FF21A69327A00A75B9A /* RCTTiming.m in Sources */, @@ -401,18 +413,20 @@ 83CBBA521A601E3B00E9B192 /* RCTLog.m in Sources */, 13B0801D1A69489C00A75B9A /* RCTNavItemManager.m in Sources */, 13E067571A70F44B002CDEE1 /* RCTView.m in Sources */, + 137327E91AA5CF210034F82E /* RCTTabBarItemManager.m in Sources */, 134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */, 13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */, 83CBBA531A601E3B00E9B192 /* RCTUtils.m in Sources */, 83CBBA601A601EAA00E9B192 /* RCTBridge.m in Sources */, + 137327E81AA5CF210034F82E /* RCTTabBarItem.m in Sources */, 13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */, 13B0801A1A69489C00A75B9A /* RCTNavigator.m in Sources */, 830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */, + 137327E71AA5CF210034F82E /* RCTTabBar.m in Sources */, 134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */, 13B0801C1A69489C00A75B9A /* RCTNavItem.m in Sources */, 83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */, 13E0674A1A70F434002CDEE1 /* RCTUIManager.m in Sources */, - 1302F0FE1A78550100EBEF02 /* RCTStaticImageManager.m in Sources */, 13B0801B1A69489C00A75B9A /* RCTNavigatorManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ReactKit/Base/RCTAnimationType.h b/ReactKit/Views/RCTAnimationType.h similarity index 100% rename from ReactKit/Base/RCTAnimationType.h rename to ReactKit/Views/RCTAnimationType.h diff --git a/ReactKit/Base/RCTAutoInsetsProtocol.h b/ReactKit/Views/RCTAutoInsetsProtocol.h similarity index 100% rename from ReactKit/Base/RCTAutoInsetsProtocol.h rename to ReactKit/Views/RCTAutoInsetsProtocol.h diff --git a/ReactKit/Views/RCTNavigator.m b/ReactKit/Views/RCTNavigator.m index be78fccd6..05306e809 100644 --- a/ReactKit/Views/RCTNavigator.m +++ b/ReactKit/Views/RCTNavigator.m @@ -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() { 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; diff --git a/ReactKit/Views/RCTNavigatorManager.m b/ReactKit/Views/RCTNavigatorManager.m index 578b42bd0..85004471b 100644 --- a/ReactKit/Views/RCTNavigatorManager.m +++ b/ReactKit/Views/RCTNavigatorManager.m @@ -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); } }]; } diff --git a/ReactKit/Base/RCTPointerEvents.h b/ReactKit/Views/RCTPointerEvents.h similarity index 100% rename from ReactKit/Base/RCTPointerEvents.h rename to ReactKit/Views/RCTPointerEvents.h diff --git a/ReactKit/Views/RCTScrollViewManager.m b/ReactKit/Views/RCTScrollViewManager.m index afcf6436a..6247dadb6 100644 --- a/ReactKit/Views/RCTScrollViewManager.m +++ b/ReactKit/Views/RCTScrollViewManager.m @@ -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) diff --git a/ReactKit/Base/RCTScrollableProtocol.h b/ReactKit/Views/RCTScrollableProtocol.h similarity index 100% rename from ReactKit/Base/RCTScrollableProtocol.h rename to ReactKit/Views/RCTScrollableProtocol.h diff --git a/ReactKit/Views/RCTShadowView.m b/ReactKit/Views/RCTShadowView.m index f8c32763f..62d38249e 100644 --- a/ReactKit/Views/RCTShadowView.m +++ b/ReactKit/Views/RCTShadowView.m @@ -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) { diff --git a/ReactKit/Views/RCTTabBar.h b/ReactKit/Views/RCTTabBar.h new file mode 100644 index 000000000..58da056a3 --- /dev/null +++ b/ReactKit/Views/RCTTabBar.h @@ -0,0 +1,11 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +@class RCTEventDispatcher; + +@interface RCTTabBar : UIView + +- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; + +@end diff --git a/ReactKit/Views/RCTTabBar.m b/ReactKit/Views/RCTTabBar.m new file mode 100644 index 000000000..9f49fbcaf --- /dev/null +++ b/ReactKit/Views/RCTTabBar.m @@ -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 + +@end + +@implementation RKCustomTabBarController + +@synthesize currentTopLayoutGuide = _currentTopLayoutGuide; +@synthesize currentBottomLayoutGuide = _currentBottomLayoutGuide; + +- (void)viewWillLayoutSubviews +{ + [super viewWillLayoutSubviews]; + _currentTopLayoutGuide = self.topLayoutGuide; + _currentBottomLayoutGuide = self.bottomLayoutGuide; +} + +@end + +@interface RCTTabBar() + +@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 diff --git a/ReactKit/Views/RCTTabBarItem.h b/ReactKit/Views/RCTTabBarItem.h new file mode 100644 index 000000000..18b03f151 --- /dev/null +++ b/ReactKit/Views/RCTTabBarItem.h @@ -0,0 +1,11 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +@interface RCTTabBarItem : UIView + +@property (nonatomic, copy) NSString *icon; +@property (nonatomic, assign, getter=isSelected) BOOL selected; +@property (nonatomic, readonly) UITabBarItem *barItem; + +@end diff --git a/ReactKit/Views/RCTTabBarItem.m b/ReactKit/Views/RCTTabBarItem.m new file mode 100644 index 000000000..cca0e5180 --- /dev/null +++ b/ReactKit/Views/RCTTabBarItem.m @@ -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 diff --git a/ReactKit/Views/RCTTabBarItemManager.h b/ReactKit/Views/RCTTabBarItemManager.h new file mode 100644 index 000000000..623020769 --- /dev/null +++ b/ReactKit/Views/RCTTabBarItemManager.h @@ -0,0 +1,7 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "RCTViewManager.h" + +@interface RCTTabBarItemManager : RCTViewManager + +@end diff --git a/ReactKit/Views/RCTTabBarItemManager.m b/ReactKit/Views/RCTTabBarItemManager.m new file mode 100644 index 000000000..f74bc09ed --- /dev/null +++ b/ReactKit/Views/RCTTabBarItemManager.m @@ -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 diff --git a/ReactKit/Views/RCTTabBarManager.h b/ReactKit/Views/RCTTabBarManager.h new file mode 100644 index 000000000..34f745e1c --- /dev/null +++ b/ReactKit/Views/RCTTabBarManager.h @@ -0,0 +1,7 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "RCTViewManager.h" + +@interface RCTTabBarManager : RCTViewManager + +@end diff --git a/ReactKit/Views/RCTTabBarManager.m b/ReactKit/Views/RCTTabBarManager.m new file mode 100644 index 000000000..70882bc16 --- /dev/null +++ b/ReactKit/Views/RCTTabBarManager.m @@ -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 diff --git a/ReactKit/Views/RCTTextFieldManager.m b/ReactKit/Views/RCTTextFieldManager.m index 5cdfd43a5..4f46c0dff 100644 --- a/ReactKit/Views/RCTTextFieldManager.m +++ b/ReactKit/Views/RCTTextFieldManager.m @@ -11,7 +11,7 @@ - (UIView *)view { - return [[RCTTextField alloc] initWithEventDispatcher:self.eventDispatcher]; + return [[RCTTextField alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; } RCT_EXPORT_VIEW_PROPERTY(caretHidden) diff --git a/ReactKit/Views/RCTView.h b/ReactKit/Views/RCTView.h index 802336633..cf46eb9df 100644 --- a/ReactKit/Views/RCTView.h +++ b/ReactKit/Views/RCTView.h @@ -10,14 +10,18 @@ @interface RCTView : UIView +/** + * Used to control how touch events are processed. + */ @property (nonatomic, assign) RCTPointerEvents pointerEvents; + (void)autoAdjustInsetsForView:(UIView *)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 diff --git a/ReactKit/Views/RCTView.m b/ReactKit/Views/RCTView.m index dd0144009..9666e717b 100644 --- a/ReactKit/Views/RCTView.m +++ b/ReactKit/Views/RCTView.m @@ -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, diff --git a/ReactKit/Views/RCTViewControllerProtocol.h b/ReactKit/Views/RCTViewControllerProtocol.h new file mode 100644 index 000000000..2c82572c3 --- /dev/null +++ b/ReactKit/Views/RCTViewControllerProtocol.h @@ -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 + +@property (nonatomic, readonly, strong) id currentTopLayoutGuide; +@property (nonatomic, readonly, strong) id currentBottomLayoutGuide; + +@end diff --git a/ReactKit/Views/RCTViewManager.h b/ReactKit/Views/RCTViewManager.h index d3b7c8a01..b98fc34a3 100644 --- a/ReactKit/Views/RCTViewManager.h +++ b/ReactKit/Views/RCTViewManager.h @@ -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 diff --git a/ReactKit/Views/RCTViewManager.m b/ReactKit/Views/RCTViewManager.m index d534e0e8b..44bac5211 100644 --- a/ReactKit/Views/RCTViewManager.m +++ b/ReactKit/Views/RCTViewManager.m @@ -14,11 +14,6 @@ @synthesize bridge = _bridge; -- (RCTEventDispatcher *)eventDispatcher -{ - return _bridge.eventDispatcher; -} - + (NSString *)moduleName { // Default implementation, works in most cases diff --git a/ReactKit/Base/RCTViewNodeProtocol.h b/ReactKit/Views/RCTViewNodeProtocol.h similarity index 77% rename from ReactKit/Base/RCTViewNodeProtocol.h rename to ReactKit/Views/RCTViewNodeProtocol.h index b6f59ea10..0e3e08099 100644 --- a/ReactKit/Base/RCTViewNodeProtocol.h +++ b/ReactKit/Views/RCTViewNodeProtocol.h @@ -13,6 +13,7 @@ - (void)insertReactSubview:(id)subview atIndex:(NSInteger)atIndex; - (void)removeReactSubview:(id)subview; - (NSMutableArray *)reactSubviews; +- (id)reactSuperview; - (NSNumber *)reactTagAtPoint:(CGPoint)point; // View is an RCTRootView @@ -26,3 +27,9 @@ - (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; +} diff --git a/ReactKit/Views/RCTWrapperViewController.h b/ReactKit/Views/RCTWrapperViewController.h index 69075c6a4..afe736430 100644 --- a/ReactKit/Views/RCTWrapperViewController.h +++ b/ReactKit/Views/RCTWrapperViewController.h @@ -2,6 +2,8 @@ #import +#import "RCTViewControllerProtocol.h" + @class RCTEventDispatcher; @class RCTNavItem; @class RCTWrapperViewController; @@ -13,7 +15,7 @@ didMoveToNavigationController:(UINavigationController *)navigationController; @end -@interface RCTWrapperViewController : UIViewController +@interface RCTWrapperViewController : UIViewController - (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 navigationListener; -@property (nonatomic, strong, readwrite) RCTNavItem *navItem; +@property (nonatomic, weak) id navigationListener; +@property (nonatomic, strong) RCTNavItem *navItem; @end diff --git a/ReactKit/Views/RCTWrapperViewController.m b/ReactKit/Views/RCTWrapperViewController.m index 6b58d6631..aff0f2e4f 100644 --- a/ReactKit/Views/RCTWrapperViewController.m +++ b/ReactKit/Views/RCTWrapperViewController.m @@ -2,22 +2,30 @@ #import "RCTWrapperViewController.h" +#import + #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]; diff --git a/ReactKit/Views/UIView+ReactKit.h b/ReactKit/Views/UIView+ReactKit.h index 3e45da8e7..9f0213a63 100644 --- a/ReactKit/Views/UIView+ReactKit.h +++ b/ReactKit/Views/UIView+ReactKit.h @@ -8,4 +8,17 @@ @interface UIView (ReactKit) +/** + * 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 diff --git a/ReactKit/Views/UIView+ReactKit.m b/ReactKit/Views/UIView+ReactKit.m index 39bca8ec6..a9feb8eed 100644 --- a/ReactKit/Views/UIView+ReactKit.m +++ b/ReactKit/Views/UIView+ReactKit.m @@ -5,6 +5,7 @@ #import #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 diff --git a/packager/packager.js b/packager/packager.js index 08bab38b0..ca4e5c675 100644 --- a/packager/packager.js +++ b/packager/packager.js @@ -29,10 +29,6 @@ var options = parseCommandLine([{ }, { command: 'root', description: 'add another root(s) to be used by the packager in this project', -}, { - command: 'dev', - default: true, - description: 'produce development packages with extra warnings enabled', }]); if (!options.projectRoots) { @@ -97,7 +93,6 @@ function openStackFrameInEditor(req, res, next) { function getAppMiddleware(options) { return ReactPackager.middleware({ - dev: options.dev, projectRoots: options.projectRoots, blacklistRE: blacklist(false), cacheVersion: '2', @@ -106,7 +101,7 @@ function getAppMiddleware(options) { } function runServer( - options, /* {string projectRoot, bool web, bool dev} */ + options, /* {[]string projectRoot, bool web} */ readyCallback ) { var app = connect() diff --git a/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js b/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js index 9704c5b59..b25fd8211 100644 --- a/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js +++ b/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js @@ -24,7 +24,6 @@ describe('HasteDependencyResolver', function() { var depResolver = new HasteDependencyResolver({ projectRoot: '/root', - dev: false, }); // Is there a better way? How can I mock the prototype instead? @@ -36,7 +35,7 @@ describe('HasteDependencyResolver', function() { return q(); }); - return depResolver.getDependencies('/root/index.js') + return depResolver.getDependencies('/root/index.js', { dev: false }) .then(function(result) { expect(result.mainModuleId).toEqual('index'); expect(result.dependencies).toEqual([ @@ -85,7 +84,6 @@ describe('HasteDependencyResolver', function() { var depResolver = new HasteDependencyResolver({ projectRoot: '/root', - dev: true, }); // Is there a better way? How can I mock the prototype instead? @@ -97,7 +95,7 @@ describe('HasteDependencyResolver', function() { return q(); }); - return depResolver.getDependencies('/root/index.js') + return depResolver.getDependencies('/root/index.js', { dev: true }) .then(function(result) { expect(result.mainModuleId).toEqual('index'); expect(result.dependencies).toEqual([ @@ -147,7 +145,6 @@ describe('HasteDependencyResolver', function() { var depResolver = new HasteDependencyResolver({ projectRoot: '/root', polyfillModuleNames: ['some module'], - dev: false, }); // Is there a better way? How can I mock the prototype instead? @@ -159,7 +156,7 @@ describe('HasteDependencyResolver', function() { return q(); }); - return depResolver.getDependencies('/root/index.js') + return depResolver.getDependencies('/root/index.js', { dev: false }) .then(function(result) { expect(result.mainModuleId).toEqual('index'); expect(result.dependencies).toEqual([ @@ -218,7 +215,6 @@ describe('HasteDependencyResolver', function() { it('should ', function() { var depResolver = new HasteDependencyResolver({ projectRoot: '/root', - dev: false, }); var depGraph = depResolver._depGraph; diff --git a/packager/react-packager/src/DependencyResolver/haste/index.js b/packager/react-packager/src/DependencyResolver/haste/index.js index 9cb0661ac..6aada00b9 100644 --- a/packager/react-packager/src/DependencyResolver/haste/index.js +++ b/packager/react-packager/src/DependencyResolver/haste/index.js @@ -32,10 +32,6 @@ var validateOpts = declareOpts({ type: 'array', default: [], }, - dev: { - type: 'boolean', - default: true, - }, nonPersistent: { type: 'boolean', default: false, @@ -62,20 +58,20 @@ function HasteDependencyResolver(options) { fileWatcher: this._fileWatcher }); - this._polyfillModuleNames = [ - opts.dev - ? path.join(__dirname, 'polyfills/prelude_dev.js') - : path.join(__dirname, 'polyfills/prelude.js'), - path.join(__dirname, 'polyfills/require.js'), - path.join(__dirname, 'polyfills/polyfills.js'), - path.join(__dirname, 'polyfills/console.js'), - path.join(__dirname, 'polyfills/error-guard.js'), - ].concat( - opts.polyfillModuleNames || [] - ); + + this._polyfillModuleNames = opts.polyfillModuleNames || []; } -HasteDependencyResolver.prototype.getDependencies = function(main) { +var getDependenciesValidateOpts = declareOpts({ + dev: { + type: 'boolean', + default: true, + }, +}); + +HasteDependencyResolver.prototype.getDependencies = function(main, options) { + var opts = getDependenciesValidateOpts(options); + var depGraph = this._depGraph; var self = this; @@ -84,7 +80,7 @@ HasteDependencyResolver.prototype.getDependencies = function(main) { var dependencies = depGraph.getOrderedDependencies(main); var mainModuleId = dependencies[0].id; - self._prependPolyfillDependencies(dependencies); + self._prependPolyfillDependencies(dependencies, opts.dev); return { mainModuleId: mainModuleId, @@ -94,22 +90,30 @@ HasteDependencyResolver.prototype.getDependencies = function(main) { }; HasteDependencyResolver.prototype._prependPolyfillDependencies = function( - dependencies + dependencies, + isDev ) { - var polyfillModuleNames = this._polyfillModuleNames; - if (polyfillModuleNames.length > 0) { - var polyfillModules = polyfillModuleNames.map( - function(polyfillModuleName, idx) { - return new ModuleDescriptor({ - path: polyfillModuleName, - id: polyfillModuleName, - dependencies: polyfillModuleNames.slice(0, idx), - isPolyfill: true - }); - } - ); - dependencies.unshift.apply(dependencies, polyfillModules); - } + var polyfillModuleNames = [ + isDev + ? path.join(__dirname, 'polyfills/prelude_dev.js') + : path.join(__dirname, 'polyfills/prelude.js'), + path.join(__dirname, 'polyfills/require.js'), + path.join(__dirname, 'polyfills/polyfills.js'), + path.join(__dirname, 'polyfills/console.js'), + path.join(__dirname, 'polyfills/error-guard.js'), + ].concat(this._polyfillModuleNames); + + var polyfillModules = polyfillModuleNames.map( + function(polyfillModuleName, idx) { + return new ModuleDescriptor({ + path: polyfillModuleName, + id: polyfillModuleName, + dependencies: polyfillModuleNames.slice(0, idx), + isPolyfill: true + }); + } + ); + dependencies.unshift.apply(dependencies, polyfillModules); }; HasteDependencyResolver.prototype.wrapModule = function(module, code) { diff --git a/packager/react-packager/src/JSTransformer/index.js b/packager/react-packager/src/JSTransformer/index.js index 87cb6e1a3..35785e6ee 100644 --- a/packager/react-packager/src/JSTransformer/index.js +++ b/packager/react-packager/src/JSTransformer/index.js @@ -34,10 +34,6 @@ var validateOpts = declareOpts({ type: 'boolean', default: false, }, - dev: { - type: 'boolean', - default: true, - }, transformModulePath: { type:'string', required: false, diff --git a/packager/react-packager/src/Packager/Package.js b/packager/react-packager/src/Packager/Package.js index 3ef9c528f..5d9b201c7 100644 --- a/packager/react-packager/src/Packager/Package.js +++ b/packager/react-packager/src/Packager/Package.js @@ -7,6 +7,7 @@ var UglifyJS = require('uglify-js'); module.exports = Package; function Package(sourceMapUrl) { + this._finalized = false; this._modules = []; this._sourceMapUrl = sourceMapUrl; } @@ -40,23 +41,56 @@ Package.prototype.finalize = function(options) { Object.freeze(this._modules); Object.seal(this._modules); + this._finalized = true; }; -Package.prototype.getSource = function(options) { - if (!this._source) { - options = options || {}; +Package.prototype._assertFinalized = function() { + if (!this._finalized) { + throw new Error('Package need to be finalized before getting any source'); + } +}; + +Package.prototype._getSource = function() { + if (this._source == null) { this._source = _.pluck(this._modules, 'transformedCode').join('\n'); - if (options.inlineSourceMap) { - var sourceMap = this.getSourceMap({excludeSource: true}); - this._source += '\nRAW_SOURCE_MAP = ' + JSON.stringify(sourceMap) + ';'; - } - this._source += '\n\/\/@ sourceMappingURL=' + this._sourceMapUrl; } return this._source; }; +Package.prototype._getInlineSourceMap = function() { + if (this._inlineSourceMap == null) { + var sourceMap = this.getSourceMap({excludeSource: true}); + this._inlineSourceMap = '\nRAW_SOURCE_MAP = ' + + JSON.stringify(sourceMap) + ';'; + } + + return this._inlineSourceMap; +}; + +Package.prototype.getSource = function(options) { + this._assertFinalized(); + + options = options || {}; + + if (options.minify) { + return this.getMinifiedSourceAndMap().code; + } + + var source = this._getSource(); + + if (options.inlineSourceMap) { + source += this._getInlineSourceMap(); + } + + source += '\n\/\/@ sourceMappingURL=' + this._sourceMapUrl; + + return source; +}; + Package.prototype.getMinifiedSourceAndMap = function() { - var source = this.getSource({inlineSourceMap: false}); + this._assertFinalized(); + + var source = this._getSource(); try { return UglifyJS.minify(source, { fromString: true, @@ -88,6 +122,8 @@ Package.prototype.getMinifiedSourceAndMap = function() { }; Package.prototype.getSourceMap = function(options) { + this._assertFinalized(); + options = options || {}; var mappings = this._getMappings(); var map = { @@ -102,7 +138,6 @@ Package.prototype.getSourceMap = function(options) { return map; }; - Package.prototype._getMappings = function() { var modules = this._modules; diff --git a/packager/react-packager/src/Packager/index.js b/packager/react-packager/src/Packager/index.js index 123a39133..75cccdb26 100644 --- a/packager/react-packager/src/Packager/index.js +++ b/packager/react-packager/src/Packager/index.js @@ -36,10 +36,6 @@ var validateOpts = declareOpts({ type: 'boolean', default: false, }, - dev: { - type: 'boolean', - default: true, - }, transformModulePath: { type:'string', required: false, @@ -59,7 +55,6 @@ function Packager(options) { projectRoots: opts.projectRoots, blacklistRE: opts.blacklistRE, polyfillModuleNames: opts.polyfillModuleNames, - dev: opts.dev, nonPersistent: opts.nonPersistent, moduleFormat: opts.moduleFormat }); @@ -69,7 +64,6 @@ function Packager(options) { blacklistRE: opts.blacklistRE, cacheVersion: opts.cacheVersion, resetCache: opts.resetCache, - dev: opts.dev, transformModulePath: opts.transformModulePath, nonPersistent: opts.nonPersistent, }); @@ -82,14 +76,14 @@ Packager.prototype.kill = function() { ]); }; -Packager.prototype.package = function(main, runModule, sourceMapUrl) { +Packager.prototype.package = function(main, runModule, sourceMapUrl, isDev) { var transformModule = this._transformModule.bind(this); var ppackage = new Package(sourceMapUrl); var findEventId = Activity.startEvent('find dependencies'); var transformEventId; - return this.getDependencies(main) + return this.getDependencies(main, isDev) .then(function(result) { Activity.endEvent(findEventId); transformEventId = Activity.startEvent('transform'); @@ -119,8 +113,8 @@ Packager.prototype.invalidateFile = function(filePath) { this._transformer.invalidateFile(filePath); }; -Packager.prototype.getDependencies = function(main) { - return this._resolver.getDependencies(main); +Packager.prototype.getDependencies = function(main, isDev) { + return this._resolver.getDependencies(main, { dev: isDev }); }; Packager.prototype._transformModule = function(module) { diff --git a/packager/react-packager/src/Server/index.js b/packager/react-packager/src/Server/index.js index 406357217..053aa0333 100644 --- a/packager/react-packager/src/Server/index.js +++ b/packager/react-packager/src/Server/index.js @@ -35,10 +35,6 @@ var validateOpts = declareOpts({ type: 'boolean', default: false, }, - dev: { - type: 'boolean', - default: true, - }, transformModulePath: { type:'string', required: false, @@ -51,7 +47,6 @@ var validateOpts = declareOpts({ function Server(options) { var opts = validateOpts(options); - this._dev = opts.dev; this._projectRoots = opts.projectRoots; this._packages = Object.create(null); this._packager = new Packager(opts); @@ -73,14 +68,16 @@ Server.prototype._onFileChange = function(type, filepath, root) { }; Server.prototype._rebuildPackages = function() { - var dev = this._dev; var buildPackage = this._buildPackage.bind(this); var packages = this._packages; Object.keys(packages).forEach(function(key) { var options = getOptionsFromUrl(key); packages[key] = buildPackage(options).then(function(p) { // Make a throwaway call to getSource to cache the source string. - p.getSource({inlineSourceMap: dev}); + p.getSource({ + inlineSourceMap: options.dev, + minify: options.minify, + }); return p; }); }); @@ -97,7 +94,8 @@ Server.prototype._buildPackage = function(options) { return this._packager.package( options.main, options.runModule, - options.sourceMapUrl + options.sourceMapUrl, + options.dev ); }; @@ -166,11 +164,13 @@ Server.prototype.processRequest = function(req, res, next) { var building = this._packages[req.url] || this._buildPackage(options); this._packages[req.url] = building; - var dev = this._dev; - building.then( + building.then( function(p) { if (requestType === 'bundle') { - res.end(p.getSource({inlineSourceMap: dev})); + res.end(p.getSource({ + inlineSourceMap: options.dev, + minify: options.minify, + })); Activity.endEvent(startReqEventId); } else if (requestType === 'map') { res.end(JSON.stringify(p.getSourceMap())); @@ -196,8 +196,9 @@ function getOptionsFromUrl(reqUrl) { return { sourceMapUrl: urlObj.pathname.replace(/\.bundle$/, '.map'), main: main, - runModule: urlObj.query.runModule === 'true' || - urlObj.query.runModule === '1' || + dev: getBoolOptionFromQuery(urlObj.query, 'dev'), + minify: getBoolOptionFromQuery(urlObj.query, 'minify'), + runModule: getBoolOptionFromQuery(urlObj.query, 'runModule') || // Backwards compatibility. urlObj.pathname.split('.').some(function(part) { return part === 'runModule'; @@ -205,6 +206,10 @@ function getOptionsFromUrl(reqUrl) { }; } +function getBoolOptionFromQuery(query, opt) { + return query[opt] === 'true' || query[opt] === '1'; +} + function handleError(res, error) { res.writeHead(500, { 'Content-Type': 'application/json; charset=UTF-8', diff --git a/packager/react-packager/src/lib/declareOpts.js b/packager/react-packager/src/lib/declareOpts.js index ddd06061a..3b80da519 100644 --- a/packager/react-packager/src/lib/declareOpts.js +++ b/packager/react-packager/src/lib/declareOpts.js @@ -42,6 +42,8 @@ module.exports = function(descriptor) { var schema = Joi.object().keys(joiKeys); return function(opts) { + opts = opts || {}; + var res = Joi.validate(opts, schema, { abortEarly: true, allowUnknown: false,