diff --git a/Libraries/ReactNative/UIManager.js b/Libraries/ReactNative/UIManager.js index c1c16888964..833005dbe92 100644 --- a/Libraries/ReactNative/UIManager.js +++ b/Libraries/ReactNative/UIManager.js @@ -24,19 +24,6 @@ invariant( 'UIManager is undefined. The native module config is probably incorrect.', ); -// In past versions of ReactNative users called UIManager.takeSnapshot() -// However takeSnapshot was moved to ReactNative in order to support flat -// bundles and to avoid a cyclic dependency between UIManager and ReactNative. -// UIManager.takeSnapshot still exists though. In order to avoid confusion or -// accidental usage, mask the method with a deprecation warning. -UIManager.__takeSnapshot = UIManager.takeSnapshot; -UIManager.takeSnapshot = function() { - invariant( - false, - 'UIManager.takeSnapshot should not be called directly. ' + - 'Use ReactNative.takeSnapshot instead.', - ); -}; const triedLoadingConfig = new Set(); UIManager.getViewManagerConfig = function(viewManagerName: string) { if ( diff --git a/Libraries/ReactNative/UIManagerProperties.js b/Libraries/ReactNative/UIManagerProperties.js index ca8e59de686..548a6ecadb6 100644 --- a/Libraries/ReactNative/UIManagerProperties.js +++ b/Libraries/ReactNative/UIManagerProperties.js @@ -56,8 +56,6 @@ module.exports = [ 'StyleConstants', 'AccessibilityEventTypes', 'UIView', - '__takeSnapshot', - 'takeSnapshot', 'getViewManagerConfig', 'blur', 'focus', diff --git a/Libraries/ReactNative/takeSnapshot.js b/Libraries/ReactNative/takeSnapshot.js deleted file mode 100644 index 6227a18e79a..00000000000 --- a/Libraries/ReactNative/takeSnapshot.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @flow - */ - -const ReactNative = require('ReactNative'); -const UIManager = require('UIManager'); - -/** - * Capture an image of the screen, window or an individual view. The image - * will be stored in a temporary file that will only exist for as long as the - * app is running. - * - * The `view` argument can be the literal string `window` if you want to - * capture the entire window, or it can be a reference to a specific - * React Native component. - * - * The `options` argument may include: - * - width/height (number) - the width and height of the image to capture. - * - format (string) - either 'png' or 'jpeg'. Defaults to 'png'. - * - quality (number) - the quality when using jpeg. 0.0 - 1.0 (default). - * - * Returns a Promise. - * @platform ios - */ -module.exports = function takeSnapshot( - view?: 'window' | React$Element | number, - options?: { - width?: number, - height?: number, - format?: 'png' | 'jpeg', - quality?: number, - }, -): Promise { - if (typeof view !== 'number' && view !== 'window') { - view = ReactNative.findNodeHandle(view) || 'window'; - } - - // Call the hidden '__takeSnapshot' method; the main one throws an error to - // prevent accidental backwards-incompatible usage. - return UIManager.__takeSnapshot(view, options); -}; diff --git a/Libraries/react-native/react-native-implementation.js b/Libraries/react-native/react-native-implementation.js index adf7b4d488a..9c25b1e0a2d 100644 --- a/Libraries/react-native/react-native-implementation.js +++ b/Libraries/react-native/react-native-implementation.js @@ -346,9 +346,6 @@ module.exports = { get requireNativeComponent() { return require('requireNativeComponent'); }, - get takeSnapshot() { - return require('takeSnapshot'); - }, // Prop Types get ColorPropType() { diff --git a/RNTester/NativeModuleExample/NativeScreenshotManager.js b/RNTester/NativeModuleExample/NativeScreenshotManager.js new file mode 100644 index 00000000000..dfff40070fb --- /dev/null +++ b/RNTester/NativeModuleExample/NativeScreenshotManager.js @@ -0,0 +1,28 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import type {TurboModule} from 'RCTExport'; +import * as TurboModuleRegistry from 'TurboModuleRegistry'; + +export interface Spec extends TurboModule { + +getConstants: () => {||}; + takeSnapshot(id: string): Promise; +} + +const NativeModule = TurboModuleRegistry.get('ScreenshotManager'); + +export function takeSnapshot(id: string): Promise { + if (NativeModule != null) { + return NativeModule.takeSnapshot(id); + } + return Promise.reject(); +} diff --git a/RNTester/NativeModuleExample/Screenshot.h b/RNTester/NativeModuleExample/Screenshot.h new file mode 100644 index 00000000000..313d07d53c7 --- /dev/null +++ b/RNTester/NativeModuleExample/Screenshot.h @@ -0,0 +1,12 @@ +// +// RNTTakeScreenshot.h +// RNTester +// +// Created by Eric Lewis on 3/1/19. +// Copyright © 2019 Facebook. All rights reserved. +// + +#import + +@interface ScreenshotManager : RCTViewManager +@end diff --git a/RNTester/NativeModuleExample/Screenshot.m b/RNTester/NativeModuleExample/Screenshot.m new file mode 100644 index 00000000000..a51e2830e32 --- /dev/null +++ b/RNTester/NativeModuleExample/Screenshot.m @@ -0,0 +1,84 @@ +// +// RNTTakeScreenshot.m +// RNTester +// +// Created by Eric Lewis on 3/1/19. +// Copyright © 2019 Facebook. All rights reserved. +// + +#import "Screenshot.h" + +#import + +@implementation ScreenshotManager + +RCT_EXPORT_MODULE(); + +RCT_EXPORT_METHOD(takeScreenshot:(id /* NSString or NSNumber */)target + withOptions:(NSDictionary *)options + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) +{ + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { + + // Get view + UIView *view; + if (target == nil || [target isEqual:@"window"]) { + view = RCTKeyWindow(); + } else if ([target isKindOfClass:[NSNumber class]]) { + view = viewRegistry[target]; + if (!view) { + RCTLogError(@"No view found with reactTag: %@", target); + return; + } + } + + // Get options + CGSize size = [RCTConvert CGSize:options]; + NSString *format = [RCTConvert NSString:options[@"format"] ?: @"png"]; + + // Capture image + if (size.width < 0.1 || size.height < 0.1) { + size = view.bounds.size; + } + UIGraphicsBeginImageContextWithOptions(size, NO, 0); + BOOL success = [view drawViewHierarchyInRect:(CGRect){CGPointZero, size} afterScreenUpdates:YES]; + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + if (!success || !image) { + reject(RCTErrorUnspecified, @"Failed to capture view snapshot.", nil); + return; + } + + // Convert image to data (on a background thread) + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + + NSData *data; + if ([format isEqualToString:@"png"]) { + data = UIImagePNGRepresentation(image); + } else if ([format isEqualToString:@"jpeg"]) { + CGFloat quality = [RCTConvert CGFloat:options[@"quality"] ?: @1]; + data = UIImageJPEGRepresentation(image, quality); + } else { + RCTLogError(@"Unsupported image format: %@", format); + return; + } + + // Save to a temp file + NSError *error = nil; + NSString *tempFilePath = RCTTempFilePath(format, &error); + if (tempFilePath) { + if ([data writeToFile:tempFilePath options:(NSDataWritingOptions)0 error:&error]) { + resolve(tempFilePath); + return; + } + } + + // If we reached here, something went wrong + reject(RCTErrorUnspecified, error.localizedDescription, error); + }); + }]; +} + +@end diff --git a/Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestCase.h b/RNTester/RCTTest/FBSnapshotTestCase/FBSnapshotTestCase.h similarity index 100% rename from Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestCase.h rename to RNTester/RCTTest/FBSnapshotTestCase/FBSnapshotTestCase.h diff --git a/Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestCase.m b/RNTester/RCTTest/FBSnapshotTestCase/FBSnapshotTestCase.m similarity index 100% rename from Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestCase.m rename to RNTester/RCTTest/FBSnapshotTestCase/FBSnapshotTestCase.m diff --git a/Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.h b/RNTester/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.h similarity index 100% rename from Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.h rename to RNTester/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.h diff --git a/Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.m b/RNTester/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.m similarity index 100% rename from Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.m rename to RNTester/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.m diff --git a/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Compare.h b/RNTester/RCTTest/FBSnapshotTestCase/UIImage+Compare.h similarity index 100% rename from Libraries/RCTTest/FBSnapshotTestCase/UIImage+Compare.h rename to RNTester/RCTTest/FBSnapshotTestCase/UIImage+Compare.h diff --git a/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Compare.m b/RNTester/RCTTest/FBSnapshotTestCase/UIImage+Compare.m similarity index 100% rename from Libraries/RCTTest/FBSnapshotTestCase/UIImage+Compare.m rename to RNTester/RCTTest/FBSnapshotTestCase/UIImage+Compare.m diff --git a/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Diff.h b/RNTester/RCTTest/FBSnapshotTestCase/UIImage+Diff.h similarity index 100% rename from Libraries/RCTTest/FBSnapshotTestCase/UIImage+Diff.h rename to RNTester/RCTTest/FBSnapshotTestCase/UIImage+Diff.h diff --git a/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Diff.m b/RNTester/RCTTest/FBSnapshotTestCase/UIImage+Diff.m similarity index 100% rename from Libraries/RCTTest/FBSnapshotTestCase/UIImage+Diff.m rename to RNTester/RCTTest/FBSnapshotTestCase/UIImage+Diff.m diff --git a/Libraries/RCTTest/RCTSnapshotManager.h b/RNTester/RCTTest/RCTSnapshotManager.h similarity index 100% rename from Libraries/RCTTest/RCTSnapshotManager.h rename to RNTester/RCTTest/RCTSnapshotManager.h diff --git a/Libraries/RCTTest/RCTSnapshotManager.m b/RNTester/RCTTest/RCTSnapshotManager.m similarity index 100% rename from Libraries/RCTTest/RCTSnapshotManager.m rename to RNTester/RCTTest/RCTSnapshotManager.m diff --git a/Libraries/RCTTest/RCTSnapshotNativeComponent.js b/RNTester/RCTTest/RCTSnapshotNativeComponent.js similarity index 63% rename from Libraries/RCTTest/RCTSnapshotNativeComponent.js rename to RNTester/RCTTest/RCTSnapshotNativeComponent.js index b7dcfa3085b..041c9580b8c 100644 --- a/Libraries/RCTTest/RCTSnapshotNativeComponent.js +++ b/RNTester/RCTTest/RCTSnapshotNativeComponent.js @@ -1,12 +1,12 @@ /** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @flow - */ + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ 'use strict'; @@ -30,4 +30,6 @@ type SnapshotViewNativeType = Class>; const requireNativeComponent = require('requireNativeComponent'); -module.exports = ((requireNativeComponent('RCTSnapshot'):any): SnapshotViewNativeType); +module.exports = ((requireNativeComponent( + 'RCTSnapshot', +): any): SnapshotViewNativeType); diff --git a/Libraries/RCTTest/RCTTest.xcodeproj/project.pbxproj b/RNTester/RCTTest/RCTTest.xcodeproj/project.pbxproj similarity index 100% rename from Libraries/RCTTest/RCTTest.xcodeproj/project.pbxproj rename to RNTester/RCTTest/RCTTest.xcodeproj/project.pbxproj diff --git a/Libraries/RCTTest/RCTTestModule.h b/RNTester/RCTTest/RCTTestModule.h similarity index 100% rename from Libraries/RCTTest/RCTTestModule.h rename to RNTester/RCTTest/RCTTestModule.h diff --git a/Libraries/RCTTest/RCTTestModule.m b/RNTester/RCTTest/RCTTestModule.m similarity index 100% rename from Libraries/RCTTest/RCTTestModule.m rename to RNTester/RCTTest/RCTTestModule.m diff --git a/Libraries/RCTTest/RCTTestRunner.h b/RNTester/RCTTest/RCTTestRunner.h similarity index 100% rename from Libraries/RCTTest/RCTTestRunner.h rename to RNTester/RCTTest/RCTTestRunner.h diff --git a/Libraries/RCTTest/RCTTestRunner.m b/RNTester/RCTTest/RCTTestRunner.m similarity index 100% rename from Libraries/RCTTest/RCTTestRunner.m rename to RNTester/RCTTest/RCTTestRunner.m diff --git a/Libraries/RCTTest/React-RCTTest.podspec b/RNTester/RCTTest/React-RCTTest.podspec similarity index 100% rename from Libraries/RCTTest/React-RCTTest.podspec rename to RNTester/RCTTest/React-RCTTest.podspec diff --git a/RNTester/RNTester.xcodeproj/project.pbxproj b/RNTester/RNTester.xcodeproj/project.pbxproj index 1f7caf99668..29a782fa3ad 100644 --- a/RNTester/RNTester.xcodeproj/project.pbxproj +++ b/RNTester/RNTester.xcodeproj/project.pbxproj @@ -35,7 +35,6 @@ 1497CFB01B21F5E400C1F8F2 /* RCTFontTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA81B21F5E400C1F8F2 /* RCTFontTests.m */; }; 1497CFB11B21F5E400C1F8F2 /* RCTEventDispatcherTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA91B21F5E400C1F8F2 /* RCTEventDispatcherTests.m */; }; 1497CFB31B21F5E400C1F8F2 /* RCTUIManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */; }; - 14B6DA821B276C5900BF4DD1 /* libRCTTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58005BEE1ABA80530062E044 /* libRCTTest.a */; }; 14D6D7111B220EB3001FB087 /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14D6D7101B220EB3001FB087 /* libOCMock.a */; }; 14D6D71E1B2222EF001FB087 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 147CED4B1AB34F8C00DA3E4C /* libRCTActionSheet.a */; }; 14D6D7201B2222EF001FB087 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134A8A251AACED6A00945AAE /* libRCTGeolocation.a */; }; @@ -43,7 +42,6 @@ 14D6D7221B2222EF001FB087 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341802B1AA91779003F314A /* libRCTNetwork.a */; }; 14D6D7231B2222EF001FB087 /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DC67F11AB71876001358AB /* libRCTPushNotification.a */; }; 14D6D7241B2222EF001FB087 /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; }; - 14D6D7251B2222EF001FB087 /* libRCTTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58005BEE1ABA80530062E044 /* libRCTTest.a */; }; 14D6D7261B2222EF001FB087 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FEF1AA914B8003F314A /* libRCTText.a */; }; 14D6D7271B2222EF001FB087 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; }; 14D6D7281B2222EF001FB087 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */; }; @@ -62,7 +60,6 @@ 2D4624FB1DA2EAC300C74D09 /* RCTRootViewIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B885551BED29AF00008352 /* RCTRootViewIntegrationTests.m */; }; 2D4624FD1DA2EAC300C74D09 /* RNTesterSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 143BC5A01B21E45C00462512 /* RNTesterSnapshotTests.m */; }; 2D4624FE1DA2EAC300C74D09 /* RCTUIManagerScenarioTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */; }; - 2D4625351DA2EBBE00C74D09 /* libRCTTest-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DD323CC1DA2DD8B000FE1B8 /* libRCTTest-tvOS.a */; }; 2D4BD8D21DA2E20D005AC8A8 /* RCTURLUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B6C1A21C34225900D3FAF5 /* RCTURLUtilsTests.m */; }; 2D4BD8D31DA2E20D005AC8A8 /* RCTBundleURLProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FF44371CF6111500720EFD /* RCTBundleURLProviderTests.m */; }; 2D4BD8D41DA2E20D005AC8A8 /* RCTAllocationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */; }; @@ -103,7 +100,6 @@ 2DE7E8011FB2A4F3009E225D /* libRCTNetwork-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DD323C31DA2DD8B000FE1B8 /* libRCTNetwork-tvOS.a */; }; 2DE7E8021FB2A4F3009E225D /* libRCTPushNotification-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D05746C1DE6008900184BB4 /* libRCTPushNotification-tvOS.a */; }; 2DE7E8031FB2A4F3009E225D /* libRCTSettings-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DD323C81DA2DD8B000FE1B8 /* libRCTSettings-tvOS.a */; }; - 2DE7E8041FB2A4F3009E225D /* libRCTTest-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DD323CC1DA2DD8B000FE1B8 /* libRCTTest-tvOS.a */; }; 2DE7E8051FB2A4F3009E225D /* libRCTText-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DD323D01DA2DD8B000FE1B8 /* libRCTText-tvOS.a */; }; 2DE7E8061FB2A4F3009E225D /* libRCTWebSocket-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DD323D51DA2DD8B000FE1B8 /* libRCTWebSocket-tvOS.a */; }; 2DE7E8071FB2A4F3009E225D /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DD323D91DA2DD8B000FE1B8 /* libReact.a */; }; @@ -121,6 +117,11 @@ 3DD981D61D33C6FB007DC7BE /* RNTesterUnitTestsBundle.js in Resources */ = {isa = PBXBuildFile; fileRef = 3DD981D51D33C6FB007DC7BE /* RNTesterUnitTestsBundle.js */; }; 52C11BBB1EEACA7100C1A058 /* libRCTBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5281CA511EEAC9A700AC40CD /* libRCTBlob.a */; }; 52C11BE11EEACA7800C1A058 /* libRCTBlob-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5281CA531EEAC9A700AC40CD /* libRCTBlob-tvOS.a */; }; + 6862DFC62229DCE600684E03 /* libRCTTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6862DFAC2229DCC400684E03 /* libRCTTest.a */; }; + 6862DFC72229DD1000684E03 /* libRCTTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6862DFAC2229DCC400684E03 /* libRCTTest.a */; }; + 6862DFC82229DD1900684E03 /* libRCTTest-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6862DFAE2229DCC400684E03 /* libRCTTest-tvOS.a */; }; + 6862DFC92229DD2100684E03 /* libRCTTest-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6862DFAE2229DCC400684E03 /* libRCTTest-tvOS.a */; }; + 6862DFCF2229DFCC00684E03 /* Screenshot.m in Sources */ = {isa = PBXBuildFile; fileRef = 6862DFCE2229DFCC00684E03 /* Screenshot.m */; }; 68FF44381CF6111500720EFD /* RCTBundleURLProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FF44371CF6111500720EFD /* RCTBundleURLProviderTests.m */; }; 834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; }; 83636F8F1B53F22C009F943E /* RCTUIManagerScenarioTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */; }; @@ -286,13 +287,6 @@ remoteGlobalIDString = 2D2A28611D9B046600D4039D; remoteInfo = "RCTSettings-tvOS"; }; - 2DD323CB1DA2DD8B000FE1B8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2D2A286E1D9B047700D4039D; - remoteInfo = "RCTTest-tvOS"; - }; 2DD323CF1DA2DD8B000FE1B8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 13417FEA1AA914B8003F314A /* RCTText.xcodeproj */; @@ -419,13 +413,20 @@ remoteGlobalIDString = ADD01A681E09402E00F6D226; remoteInfo = "RCTBlob-tvOS"; }; - 58005BED1ABA80530062E044 /* PBXContainerItemProxy */ = { + 6862DFAB2229DCC400684E03 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */; + containerPortal = 6862DF932229DCC400684E03 /* RCTTest.xcodeproj */; proxyType = 2; remoteGlobalIDString = 580C376F1AB104AF0015E709; remoteInfo = RCTTest; }; + 6862DFAD2229DCC400684E03 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6862DF932229DCC400684E03 /* RCTTest.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A286E1D9B047700D4039D; + remoteInfo = "RCTTest-tvOS"; + }; 834C36D11AF8DA610019C93C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */; @@ -558,7 +559,9 @@ 3DB99D0B1BA0340600302749 /* RNTesterIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNTesterIntegrationTests.m; sourceTree = ""; }; 3DD981D51D33C6FB007DC7BE /* RNTesterUnitTestsBundle.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = RNTesterUnitTestsBundle.js; sourceTree = ""; }; 5281CA4B1EEAC9A700AC40CD /* RCTBlob.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTBlob.xcodeproj; path = ../Libraries/Blob/RCTBlob.xcodeproj; sourceTree = ""; }; - 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = ../Libraries/RCTTest/RCTTest.xcodeproj; sourceTree = ""; }; + 6862DF932229DCC400684E03 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = RCTTest/RCTTest.xcodeproj; sourceTree = ""; }; + 6862DFCD2229DFCB00684E03 /* Screenshot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Screenshot.h; sourceTree = ""; }; + 6862DFCE2229DFCC00684E03 /* Screenshot.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Screenshot.m; sourceTree = ""; }; 68FF44371CF6111500720EFD /* RCTBundleURLProviderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBundleURLProviderTests.m; sourceTree = ""; }; 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManagerScenarioTests.m; sourceTree = ""; }; 8385CEF41B873B5C00C6273E /* RCTImageLoaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoaderTests.m; sourceTree = ""; }; @@ -588,7 +591,7 @@ 14D6D7221B2222EF001FB087 /* libRCTNetwork.a in Frameworks */, 14D6D7231B2222EF001FB087 /* libRCTPushNotification.a in Frameworks */, 14D6D7241B2222EF001FB087 /* libRCTSettings.a in Frameworks */, - 14D6D7251B2222EF001FB087 /* libRCTTest.a in Frameworks */, + 6862DFC62229DCE600684E03 /* libRCTTest.a in Frameworks */, 14D6D7261B2222EF001FB087 /* libRCTText.a in Frameworks */, 14D6D7271B2222EF001FB087 /* libRCTVibration.a in Frameworks */, 14D6D7281B2222EF001FB087 /* libRCTWebSocket.a in Frameworks */, @@ -624,7 +627,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 14B6DA821B276C5900BF4DD1 /* libRCTTest.a in Frameworks */, + 6862DFC72229DD1000684E03 /* libRCTTest.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -632,7 +635,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2D4625351DA2EBBE00C74D09 /* libRCTTest-tvOS.a in Frameworks */, + 6862DFC92229DD2100684E03 /* libRCTTest-tvOS.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -659,6 +662,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 6862DFC82229DD1900684E03 /* libRCTTest-tvOS.a in Frameworks */, 2DE7E7FD1FB2A4F3009E225D /* libRCTAnimation.a in Frameworks */, 2DE7E7FE1FB2A4F3009E225D /* libRCTBlob-tvOS.a in Frameworks */, 2DE7E7FF1FB2A4F3009E225D /* libRCTImage-tvOS.a in Frameworks */, @@ -666,7 +670,6 @@ 2DE7E8011FB2A4F3009E225D /* libRCTNetwork-tvOS.a in Frameworks */, 2DE7E8021FB2A4F3009E225D /* libRCTPushNotification-tvOS.a in Frameworks */, 2DE7E8031FB2A4F3009E225D /* libRCTSettings-tvOS.a in Frameworks */, - 2DE7E8041FB2A4F3009E225D /* libRCTTest-tvOS.a in Frameworks */, 2DE7E8051FB2A4F3009E225D /* libRCTText-tvOS.a in Frameworks */, 2DE7E8061FB2A4F3009E225D /* libRCTWebSocket-tvOS.a in Frameworks */, 2DE7E8071FB2A4F3009E225D /* libReact.a in Frameworks */, @@ -699,7 +702,7 @@ 134180261AA91779003F314A /* RCTNetwork.xcodeproj */, 14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */, 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */, - 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */, + 6862DF932229DCC400684E03 /* RCTTest.xcodeproj */, 13417FEA1AA914B8003F314A /* RCTText.xcodeproj */, D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */, 139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */, @@ -778,6 +781,7 @@ 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 13B07FB01A68108700A75B9A /* AppDelegate.m */, AFEACAB12223EB2C004E5198 /* NativeExampleModules */, + 6862DFD02229E1DE00684E03 /* NativeModuleExample */, 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, 13B07FB71A68108700A75B9A /* main.m */, 1323F18D1C04ABAC0091BED0 /* Supporting Files */, @@ -976,15 +980,24 @@ name = Products; sourceTree = ""; }; - 58005BE51ABA80530062E044 /* Products */ = { + 6862DF942229DCC400684E03 /* Products */ = { isa = PBXGroup; children = ( - 58005BEE1ABA80530062E044 /* libRCTTest.a */, - 2DD323CC1DA2DD8B000FE1B8 /* libRCTTest-tvOS.a */, + 6862DFAC2229DCC400684E03 /* libRCTTest.a */, + 6862DFAE2229DCC400684E03 /* libRCTTest-tvOS.a */, ); name = Products; sourceTree = ""; }; + 6862DFD02229E1DE00684E03 /* NativeModuleExample */ = { + isa = PBXGroup; + children = ( + 6862DFCD2229DFCB00684E03 /* Screenshot.h */, + 6862DFCE2229DFCC00684E03 /* Screenshot.m */, + ); + path = NativeModuleExample; + sourceTree = ""; + }; 834C36CE1AF8DA610019C93C /* Products */ = { isa = PBXGroup; children = ( @@ -1261,8 +1274,8 @@ ProjectRef = 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */; }, { - ProductGroup = 58005BE51ABA80530062E044 /* Products */; - ProjectRef = 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */; + ProductGroup = 6862DF942229DCC400684E03 /* Products */; + ProjectRef = 6862DF932229DCC400684E03 /* RCTTest.xcodeproj */; }, { ProductGroup = 13417FEB1AA914B8003F314A /* Products */; @@ -1428,13 +1441,6 @@ remoteRef = 2DD323C71DA2DD8B000FE1B8 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 2DD323CC1DA2DD8B000FE1B8 /* libRCTTest-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libRCTTest-tvOS.a"; - remoteRef = 2DD323CB1DA2DD8B000FE1B8 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 2DD323D01DA2DD8B000FE1B8 /* libRCTText-tvOS.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -1554,11 +1560,18 @@ remoteRef = 5281CA521EEAC9A700AC40CD /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 58005BEE1ABA80530062E044 /* libRCTTest.a */ = { + 6862DFAC2229DCC400684E03 /* libRCTTest.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTTest.a; - remoteRef = 58005BED1ABA80530062E044 /* PBXContainerItemProxy */; + remoteRef = 6862DFAB2229DCC400684E03 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 6862DFAE2229DCC400684E03 /* libRCTTest-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTTest-tvOS.a"; + remoteRef = 6862DFAD2229DCC400684E03 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 834C36D21AF8DA610019C93C /* libRCTSettings.a */ = { @@ -1739,6 +1752,7 @@ 27F441EC1BEBE5030039B79C /* FlexibleSizeExampleView.m in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, AFEACA842223EB05004E5198 /* CrashyCrash.m in Sources */, + 6862DFCF2229DFCC00684E03 /* Screenshot.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/RNTester/js/ActionSheetIOSExample.js b/RNTester/js/ActionSheetIOSExample.js index 59f4d1b8b67..1f7abc2e797 100644 --- a/RNTester/js/ActionSheetIOSExample.js +++ b/RNTester/js/ActionSheetIOSExample.js @@ -15,11 +15,12 @@ const ReactNative = require('react-native'); const { ActionSheetIOS, StyleSheet, - takeSnapshot, Text, View, Alert, + NativeModules, } = ReactNative; +const ScreenshotManager = NativeModules.ScreenshotManager; const BUTTONS = ['Option 0', 'Option 1', 'Option 2', 'Delete', 'Cancel']; const DESTRUCTIVE_INDEX = 3; @@ -199,7 +200,7 @@ class ShareScreenshotExample extends React.Component< showShareActionSheet = () => { // Take the snapshot (returns a temp file uri) - takeSnapshot('window') + ScreenshotManager.takeScreenshot('window') .then(uri => { // Share image data ActionSheetIOS.showShareActionSheetWithOptions( @@ -254,7 +255,7 @@ class ShareScreenshotAnchorExample extends React.Component< showShareActionSheet = () => { // Take the snapshot (returns a temp file uri) - takeSnapshot('window') + ScreenshotManager.takeScreenshot('window') .then(uri => { // Share image data ActionSheetIOS.showShareActionSheetWithOptions( diff --git a/RNTester/js/SnapshotExample.js b/RNTester/js/SnapshotExample.js index 6a6a90451ff..a9a01963fca 100644 --- a/RNTester/js/SnapshotExample.js +++ b/RNTester/js/SnapshotExample.js @@ -11,7 +11,8 @@ const React = require('react'); const ReactNative = require('react-native'); -const {Alert, Image, StyleSheet, Text, View} = ReactNative; +const {Alert, Image, NativeModules, StyleSheet, Text, View} = ReactNative; +const ScreenshotManager = NativeModules.ScreenshotManager; class ScreenshotExample extends React.Component<{}, $FlowFixMeState> { state = { @@ -30,7 +31,7 @@ class ScreenshotExample extends React.Component<{}, $FlowFixMeState> { } takeScreenshot = () => { - ReactNative.takeSnapshot('window', {format: 'jpeg', quality: 0.8}) // See UIManager.js for options + ScreenshotManager.takeScreenshot('window', {format: 'jpeg', quality: 0.8}) // See UIManager.js for options .then(uri => this.setState({uri})) .catch(error => Alert.alert(error)); }; diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 83c04e1091e..5cd2b8ba998 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -1364,73 +1364,6 @@ RCT_EXPORT_METHOD(measureLayoutRelativeToParent:(nonnull NSNumber *)reactTag RCTMeasureLayout(shadowView, shadowView.reactSuperview, callback); } -RCT_EXPORT_METHOD(takeSnapshot:(id /* NSString or NSNumber */)target - withOptions:(NSDictionary *)options - resolve:(RCTPromiseResolveBlock)resolve - reject:(RCTPromiseRejectBlock)reject) -{ - [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - - // Get view - UIView *view; - if (target == nil || [target isEqual:@"window"]) { - view = RCTKeyWindow(); - } else if ([target isKindOfClass:[NSNumber class]]) { - view = viewRegistry[target]; - if (!view) { - RCTLogError(@"No view found with reactTag: %@", target); - return; - } - } - - // Get options - CGSize size = [RCTConvert CGSize:options]; - NSString *format = [RCTConvert NSString:options[@"format"] ?: @"png"]; - - // Capture image - if (size.width < 0.1 || size.height < 0.1) { - size = view.bounds.size; - } - UIGraphicsBeginImageContextWithOptions(size, NO, 0); - BOOL success = [view drawViewHierarchyInRect:(CGRect){CGPointZero, size} afterScreenUpdates:YES]; - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - if (!success || !image) { - reject(RCTErrorUnspecified, @"Failed to capture view snapshot.", nil); - return; - } - - // Convert image to data (on a background thread) - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - - NSData *data; - if ([format isEqualToString:@"png"]) { - data = UIImagePNGRepresentation(image); - } else if ([format isEqualToString:@"jpeg"]) { - CGFloat quality = [RCTConvert CGFloat:options[@"quality"] ?: @1]; - data = UIImageJPEGRepresentation(image, quality); - } else { - RCTLogError(@"Unsupported image format: %@", format); - return; - } - - // Save to a temp file - NSError *error = nil; - NSString *tempFilePath = RCTTempFilePath(format, &error); - if (tempFilePath) { - if ([data writeToFile:tempFilePath options:(NSDataWritingOptions)0 error:&error]) { - resolve(tempFilePath); - return; - } - } - - // If we reached here, something went wrong - reject(RCTErrorUnspecified, error.localizedDescription, error); - }); - }]; -} - /** * JS sets what *it* considers to be the responder. Later, scroll views can use * this in order to determine if scrolling is appropriate.