Permalink
Browse files

Update Dimensions when device orientation changes

Reviewed By: nicklockwood

Differential Revision: D2939877

fb-gh-sync-id: ec6161448bff34c07b93f19e1ee953657675bad5
shipit-source-id: ec6161448bff34c07b93f19e1ee953657675bad5
  • Loading branch information...
javache authored and Facebook Github Bot 7 committed Mar 15, 2016
1 parent f6853b8 commit b653d43e2e9d8a5506818b47d69993328132bbe4
Showing with 85 additions and 51 deletions.
  1. +43 −38 Libraries/Utilities/Dimensions.js
  2. +42 −13 React/Modules/RCTUIManager.m
@@ -13,53 +13,53 @@
var Platform = require('Platform');
var UIManager = require('UIManager');
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
var invariant = require('fbjs/lib/invariant');
var dimensions = UIManager.Dimensions;
// We calculate the window dimensions in JS so that we don't encounter loss of
// precision in transferring the dimensions (which could be non-integers) over
// the bridge.
if (dimensions && dimensions.windowPhysicalPixels) {
// parse/stringify => Clone hack
dimensions = JSON.parse(JSON.stringify(dimensions));
var windowPhysicalPixels = dimensions.windowPhysicalPixels;
dimensions.window = {
width: windowPhysicalPixels.width / windowPhysicalPixels.scale,
height: windowPhysicalPixels.height / windowPhysicalPixels.scale,
scale: windowPhysicalPixels.scale,
fontScale: windowPhysicalPixels.fontScale,
};
if (Platform.OS === 'android') {
// Screen and window dimensions are different on android
var screenPhysicalPixels = dimensions.screenPhysicalPixels;
dimensions.screen = {
width: screenPhysicalPixels.width / screenPhysicalPixels.scale,
height: screenPhysicalPixels.height / screenPhysicalPixels.scale,
scale: screenPhysicalPixels.scale,
fontScale: screenPhysicalPixels.fontScale,
};
// delete so no callers rely on this existing
delete dimensions.screenPhysicalPixels;
} else {
dimensions.screen = dimensions.window;
}
// delete so no callers rely on this existing
delete dimensions.windowPhysicalPixels;
}
var dimensions = {};
class Dimensions {
/**
* This should only be called from native code.
* This should only be called from native code by sending the
* didUpdateDimensions event.
*
* @param {object} dims Simple string-keyed object of dimensions to set
*/
static set(dims: {[key:string]: any}): bool {
static set(dims: {[key:string]: any}): void {
// We calculate the window dimensions in JS so that we don't encounter loss of
// precision in transferring the dimensions (which could be non-integers) over
// the bridge.
if (dims && dims.windowPhysicalPixels) {
// parse/stringify => Clone hack
dims = JSON.parse(JSON.stringify(dims));
var windowPhysicalPixels = dims.windowPhysicalPixels;
dims.window = {
width: windowPhysicalPixels.width / windowPhysicalPixels.scale,
height: windowPhysicalPixels.height / windowPhysicalPixels.scale,
scale: windowPhysicalPixels.scale,
fontScale: windowPhysicalPixels.fontScale,
};
if (Platform.OS === 'android') {
// Screen and window dimensions are different on android
var screenPhysicalPixels = dims.screenPhysicalPixels;
dims.screen = {
width: screenPhysicalPixels.width / screenPhysicalPixels.scale,
height: screenPhysicalPixels.height / screenPhysicalPixels.scale,
scale: screenPhysicalPixels.scale,
fontScale: screenPhysicalPixels.fontScale,
};
// delete so no callers rely on this existing
delete dims.screenPhysicalPixels;
} else {
dims.screen = dims.window;
}
// delete so no callers rely on this existing
delete dims.windowPhysicalPixels;
}
Object.assign(dimensions, dims);
return true;
}
/**
@@ -83,4 +83,9 @@ class Dimensions {
}
}
Dimensions.set(UIManager.Dimensions);
RCTDeviceEventEmitter.addListener('didUpdateDimensions', function(update) {
Dimensions.set(update);
});
module.exports = Dimensions;
@@ -201,17 +201,14 @@ @implementation RCTUIManager
NSDictionary *_componentDataByName;
NSMutableSet<id<RCTComponent>> *_bridgeTransactionListeners;
UIInterfaceOrientation _currentInterfaceOrientation;
}
@synthesize bridge = _bridge;
RCT_EXPORT_MODULE()
/**
* Declared in RCTBridge.
*/
extern NSString *RCTBridgeModuleNameForClass(Class cls);
- (void)didReceiveNewContentSizeMultiplier
{
__weak RCTUIManager *weakSelf = self;
@@ -225,6 +222,23 @@ - (void)didReceiveNewContentSizeMultiplier
});
}
- (void)interfaceOrientationWillChange:(NSNotification *)notification
{
UIInterfaceOrientation nextOrientation =
[notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] integerValue];
// Update when we go from portrait to landscape, or landscape to portrait
if ((UIInterfaceOrientationIsPortrait(_currentInterfaceOrientation) &&
!UIInterfaceOrientationIsPortrait(nextOrientation)) ||
(UIInterfaceOrientationIsLandscape(_currentInterfaceOrientation) &&
!UIInterfaceOrientationIsLandscape(nextOrientation))) {
[_bridge.eventDispatcher sendDeviceEventWithName:@"didUpdateDimensions"
body:RCTExportedDimensions(YES)];
}
_currentInterfaceOrientation = nextOrientation;
}
- (void)invalidate
{
/**
@@ -298,6 +312,11 @@ - (void)setBridge:(RCTBridge *)bridge
selector:@selector(didReceiveNewContentSizeMultiplier)
name:RCTAccessibilityManagerDidUpdateMultiplierNotification
object:_bridge.accessibilityManager];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(interfaceOrientationWillChange:)
name:UIApplicationWillChangeStatusBarOrientationNotification
object:nil];
}
- (dispatch_queue_t)methodQueue
@@ -1231,7 +1250,7 @@ static void RCTMeasureLayout(RCTShadowView *view,
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
{
[self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
[self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
// Get view
UIView *view;
@@ -1367,21 +1386,31 @@ static void RCTMeasureLayout(RCTShadowView *view,
allJSConstants[name] = constantsNamespace;
}];
_currentInterfaceOrientation = [RCTSharedApplication() statusBarOrientation];
[allJSConstants addEntriesFromDictionary:@{
@"customBubblingEventTypes": bubblingEvents,
@"customDirectEventTypes": directEvents,
@"Dimensions": @{
@"window": @{
@"width": @(RCTScreenSize().width),
@"height": @(RCTScreenSize().height),
@"scale": @(RCTScreenScale()),
},
},
@"Dimensions": RCTExportedDimensions(NO)
}];
return allJSConstants;
}
static NSDictionary *RCTExportedDimensions(BOOL rotateBounds)
{
RCTAssertMainThread();
// Don't use RCTScreenSize since it the interface orientation doesn't apply to it
CGRect screenSize = [[UIScreen mainScreen] bounds];
return @{
@"window": @{
@"width": @(rotateBounds ? screenSize.size.height : screenSize.size.width),
@"height": @(rotateBounds ? screenSize.size.width : screenSize.size.height),
@"scale": @(RCTScreenScale()),
},
};
}
RCT_EXPORT_METHOD(configureNextLayoutAnimation:(NSDictionary *)config
withCallback:(RCTResponseSenderBlock)callback
errorCallback:(__unused RCTResponseSenderBlock)errorCallback)

1 comment on commit b653d43

@ghuh

This comment has been minimized.

Show comment
Hide comment
@ghuh

ghuh May 2, 2016

Is it possible that this is not working correctly on an iPad? When I call Dimensions.get( 'window' ) on iPhone it always seems to be correct. When called on iPad, it seems to be one screen rotation behind. So it is updating, just not when expected.

ghuh commented on b653d43 May 2, 2016

Is it possible that this is not working correctly on an iPad? When I call Dimensions.get( 'window' ) on iPhone it always seems to be correct. When called on iPad, it seems to be one screen rotation behind. So it is updating, just not when expected.

Please sign in to comment.