-
Notifications
You must be signed in to change notification settings - Fork 24.5k
Commit
…l views Summary:This adds a `takeSnapshot` method to UIManager that can be used to capture screenshots as an image. The takeSnapshot method accepts either 'screen', 'window' or a view ref as an argument. You can also specify the size, format and quality of the captured image. I've added an example of capturing a screenshot at UIExplorer > Snapshot / Screenshot. I've also added an example of sharing a screenshot to the UIExplorer > ActionSheetIOS demo. Reviewed By: javache Differential Revision: D2958351 fb-gh-sync-id: d2eb93fea3297ec5aaa312854dd6add724a7f4f8 shipit-source-id: d2eb93fea3297ec5aaa312854dd6add724a7f4f8
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/** | ||
* The examples provided by Facebook are for non-commercial testing and | ||
* evaluation purposes only. | ||
* | ||
* Facebook reserves all rights not expressly granted. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | ||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | ||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
* | ||
* @flow | ||
*/ | ||
'use strict'; | ||
|
||
var React = require('react-native'); | ||
var { | ||
Image, | ||
StyleSheet, | ||
Text, | ||
UIManager, | ||
View, | ||
} = React; | ||
|
||
var ScreenshotExample = React.createClass({ | ||
getInitialState() { | ||
return { | ||
uri: undefined, | ||
}; | ||
}, | ||
|
||
render() { | ||
return ( | ||
<View> | ||
<Text onPress={this.takeScreenshot} style={style.button}> | ||
Click to take a screenshot | ||
</Text> | ||
<Image style={style.image} source={{uri: this.state.uri}}/> | ||
</View> | ||
); | ||
}, | ||
|
||
takeScreenshot() { | ||
UIManager | ||
.takeSnapshot('screen', {format: 'jpeg', quality: 0.8}) // See UIManager.js for options | ||
.then((uri) => this.setState({uri})) | ||
.catch((error) => alert(error)); | ||
} | ||
}); | ||
|
||
var style = StyleSheet.create({ | ||
button: { | ||
marginBottom: 10, | ||
fontWeight: '500', | ||
}, | ||
image: { | ||
flex: 1, | ||
height: 300, | ||
resizeMode: 'contain', | ||
backgroundColor: 'black', | ||
}, | ||
}); | ||
|
||
exports.title = 'Snapshot / Screenshot'; | ||
exports.description = 'API to capture images from the screen.'; | ||
exports.examples = [ | ||
{ | ||
title: 'Take screenshot', | ||
render(): ReactElement { return <ScreenshotExample />; } | ||
}, | ||
]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1182,6 +1182,75 @@ static void RCTMeasureLayout(RCTShadowView *view, | |
callback(@[results]); | ||
} | ||
|
||
RCT_EXPORT_METHOD(takeSnapshot:(id /* NSString or NSNumber */)target | ||
withOptions:(NSDictionary *)options | ||
resolve:(RCTPromiseResolveBlock)resolve | ||
reject:(RCTPromiseRejectBlock)reject) | ||
{ | ||
[self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) { | ||
|
||
// Get view | ||
UIView *view; | ||
if (target == nil || [target isEqual:@"screen"]) { | ||
view = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:YES]; | ||
} else if ([target isEqual:@"window"]) { | ||
view = RCTKeyWindow(); | ||
} else if ([target isKindOfClass:[NSNumber class]]) { | ||
view = viewRegistry[target]; | ||
if (!view) { | ||
RCTLogError(@"No view found with reactTag: %@", target); | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
nicklockwood
Author
Contributor
|
||
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) { | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
nicklockwood
Author
Contributor
|
||
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); | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
nicklockwood
Author
Contributor
|
||
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. | ||
|
4 comments
on commit ac12f98
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nicklockwood you broke website generation script.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is great !
This should be