Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix iOS device orientation setup, honor the project settings #1375

Merged
merged 3 commits into from
Nov 4, 2020

Conversation

ichan-mb
Copy link
Member

This is a regression fix for a feature introduced in #1354. The bugs reported here: #1374

This PR contains:

  • Changelog
  • Documentation
  • Tests

@AndrewEQ
Copy link
Contributor

Cool, will do more testing and let ya know...

@AndrewEQ
Copy link
Contributor

AndrewEQ commented Sep 29, 2020

Ok here are my findings:

Uno 2.0.0-beta.5 + latest fuselibs (as of 28 Sep 2020)

Mobile.Orientations

iOS 12 - Xcode 10.1 - iPhone 6

  • working
  • upsidedown - throws a crashing uncaught error for not being supported I think:
    *** Terminating app due to uncaught exception 'UIApplicationInvalidInterfaceOrientation', reason: 'Supported orientations has no common orientation with the application, and [uAppDelegate shouldAutorotate] is returning YES'

iOS 13/14 - Xcode 12.0.1 - iPhone SE

  • Portrait - Works
  • UpsideDown - Works initially for first launch, first screen but subsequent launches or change in page navigation doesn't work
  • LandscapeLeft - Works initially for first launch, first screen but subsequent launches or change in page navigation doesn't work
  • LandscapeRight - Doesn't work

Trigger Orientations

iOS 12 - Xcode 10.1 - iPhone 6

  • working
  • upsidedown - throws a crashing uncaught error for not being supported (see above)

iOS 12 - Xcode 12.0.1 - iPhone 6

  • throws a crashing error when switching to a page with the trigger defined:
    -[uWindow windowScene]: unrecognized selector sent to instance 0x157d01720
    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[uWindow windowScene]: unrecognized selector sent to instance 0x157d01720'

iOS 13/14 - Xcode 12.0.1 - iPhone SE

Test App:

<App Background="#222">

	<JavaScript File="MainView.js" />

	<Router ux:Name="router" />

	<Navigator DefaultPath="home" Active="{page}">
		<Page ux:Template="home">
			<StackPanel Alignment="Center" ItemSpacing="10">
				<Button Text="Portrait" Padding="12" Clicked="{gotoPortrait}">
					<Rectangle Layer="Background" Color="#fff" CornerRadius="50" />
				</Button>
				<Button Text="PortraitUpsideDown" Padding="12" Clicked="{gotoPortraitUpsideDown}">
					<Rectangle Layer="Background" Color="#fff" CornerRadius="50" />
				</Button>
				<Button Text="LandscapeLeft" Padding="12" Clicked="{gotoLandscapeLeft}">
					<Rectangle Layer="Background" Color="#fff" CornerRadius="50" />
				</Button>
				<Button Text="LandscapeRight" Padding="12" Clicked="{gotoLandscapeRight}">
					<Rectangle Layer="Background" Color="#fff" CornerRadius="50" />
				</Button>
			</StackPanel>
		</Page>
		<Page ux:Template="portrait">
			<Activated>
				<SetWindowOrientation To="Portrait" />
			</Activated>
			<StackPanel ItemSpacing="10" Alignment="Center">
				<StatusBarBackground />
				<Text TextColor="#fff" Alignment="Center">PORTRAIT</Text>

				<Button Text="Go Back" Padding="12" Clicked="{goBack}">
					<Rectangle Layer="Background" Color="#fff" CornerRadius="50" />
				</Button>
			</StackPanel>
			<Rectangle Layer="Background" Color="#18f" />
		</Page>
		<Page ux:Template="portraitupsidedown">
			<Activated>
				<SetWindowOrientation To="PortraitUpsideDown" />
			</Activated>
			<StackPanel ItemSpacing="10" Alignment="Center">
				<StatusBarBackground />
				<Text TextColor="#fff" Alignment="Center">PORTRAIT UPSIDE DOWN</Text>

				<Button Text="Go Back" Padding="12" Clicked="{goBack}">
					<Rectangle Layer="Background" Color="#fff" CornerRadius="50" />
				</Button>
			</StackPanel>
			<Rectangle Layer="Background" Color="#1f8" />
		</Page>
		<Page ux:Template="landscapeleftpage">
			<Activated>
				<SetWindowOrientation To="LandscapeLeft" />
			</Activated>
			<StackPanel ItemSpacing="10" Alignment="Center">
				<StatusBarBackground />
				<Text TextColor="#fff" Alignment="Center">LANDSCAPE LEFT</Text>

				<Button Text="Go Back" Padding="12" Clicked="{goBack}">
					<Rectangle Layer="Background" Color="#fff" CornerRadius="50" />
				</Button>
			</StackPanel>
			<Rectangle Layer="Background" Color="#f18" />
		</Page>
		<Page ux:Template="landscaperight">
			<Activated>
				<SetWindowOrientation To="LandscapeRight" />
			</Activated>
			<StackPanel ItemSpacing="10" Alignment="Center">
				<StatusBarBackground />
				<Text TextColor="#fff" Alignment="Center">LANDSCAPE RIGHT</Text>

				<Button Text="Go Back" Padding="12" Clicked="{goBack}">
					<Rectangle Layer="Background" Color="#fff" CornerRadius="50" />
				</Button>
			</StackPanel>
			<Rectangle Layer="Background" Color="#81f" />
		</Page>
	</Navigator>

	<Rectangle Layer="Background" Color="#18f" />
</App>

MainView.js:

var Observable = require('FuseJS/Observable');

var page = Observable('home');

function gotoPortrait() {
  router.push('portrait');
  console.log('gotoPortrait')
}

function gotoPortraitUpsideDown() {
  router.push('portraitupsidedown');
  console.log('gotoPortraitUpsideDown')
}

function gotoLandscape() {
  router.push('landscape');
  console.log('gotoLandscape')
}

function gotoLandscapeLeft() {
  router.push('landscapeleftpage');
  console.log('gotoLandscapeLeft')
}

function gotoLandscapeRight() {
  router.push('landscaperight');
  console.log('gotoLandscapeRight')
}

function goBack() {
  router.goBack()
  console.log('goBack')
}

module.exports = {
  page,
  gotoPortrait,
  gotoPortraitUpsideDown,
  gotoLandscape,
  gotoLandscapeLeft,
  gotoLandscapeRight,
  goBack
};

@AndrewEQ
Copy link
Contributor

Here's a good article on iOS orientation: https://useyourloaf.com/blog/upside-down-and-rotating-iphones

@ichan-mb
Copy link
Member Author

Thank you for the detailed testing. I don't have iOS devices with iOS 12 so I can't take the test to see it by myself 🙏.

Trigger Orientations

I'm curious about trigger orientation is not working on the ios 14 using Xcode 12. Have you tried opening / launching the testing app from the home screen, not from the XCode? because in my testing using iPhone XR iOS 14, launching from XCode indeed the trigger is not working, but when it launched directly from the home screen, the trigger works as intended. Do you have the same experience?

Here is the screen recording, launching the app from the home screen, and everything works perfectly: https://drive.google.com/file/d/1xuwGK1OvTquWVQ6AENAWnmTHP-edBSAF/view?usp=sharing

I don't know if is because I'm using the iPhone X series. I'll try to take a test again tomorrow using iPhone 7 in my office.

And the tests that you have been done on iOS 12 - Xcode 12.0.1 - iPhone 6 that crashed miserably because it doesn't know about the windowScene property and that because windowScene is only available on the iOS 13 up. What is strange is that on the source code, I did the conditional compiling and only call windowScene if it's on the iOS 13 up as you can see it here: https://github.com/fuse-open/fuselibs/pull/1375/files#diff-f0519ddc88708b91b1a8a05a96f94bcbR418. Am I do it wrong? I need suggestion from you and @mortend about this

Mobile.Orientations

In my test when Mobile.Orientations project settings are set, on iOS 14 using iPhone XR with Xcode 12, it is working as intended, launching the app from the Xcode or from the home screen it will set the orientation according to the Mobile.Orientations setting. I've to try again with a different device model tomorrow to make sure.

I also notice that when you set Mobile.Orientations to PortraitUpsideDown or LandscapeLeft or LandscapeRight. The generated of the Xcode project doesn't set to the correct setting of the Device Orientation according to the Mobile.Orientations but leave all the device orientation unchecked all as seen in the picture below:

image

The issue I think is in the template of info.plist: https://github.com/fuse-open/uno/blob/c3a98bb5138419348cc9170f7c5352e2b9accdad/lib/UnoCore/Targets/iOS/%40(Project.Name)/%40(Project.Name)-Info.plist#L54 We don't take care of all the possibilities of the Mobile.Orientations

My guess that because of this, the test that have you been done on the iPhone 6 ios 12 using Xcode 10 got crashed when setting up to PortraitUpsideDown

@mortend
Copy link
Member

mortend commented Sep 30, 2020

And the tests that you have been done on iOS 12 - Xcode 12.0.1 - iPhone 6 that crashed miserably because it doesn't know about the windowScene property and that because windowScene is only available on the iOS 13 up. What is strange is that on the source code, I did the conditional compiling and only call windowScene if it's on the iOS 13 up as you can see it here: https://github.com/fuse-open/fuselibs/pull/1375/files#diff-f0519ddc88708b91b1a8a05a96f94bcbR418. Am I do it wrong? I need suggestion from you and @mortend about this

@ichan-mb Regarding conditional compiling, I think we'll also need a run-time test to see if we're running on iOS 13 (apps compiled using iOS 13 SDK can still be run on older iOS versions). Something like this might work.

UIInterfaceOrientation mask;

#if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
if (@available(iOS 13.0, *)) {
    mask = [[UIApplication sharedApplication].windows firstObject].windowScene.interfaceOrientation;
    // ...
}
#endif

mask = [[UIApplication sharedApplication] statusBarOrientation];
// ...

Note that the #else was removed because we need to fall-through in case the run-time test fail when run on iOS 12 or older.

@AndrewEQ
Copy link
Contributor

AndrewEQ commented Oct 1, 2020

Round 2

Trigger Orientations

  • iOS 14 - You're correct, I have the same experience; the triggers work outside(homescreen launching) of testing with Xcode debugging (so strange).

  • iOS 12 - probably what you've described, I can test it again on your next PR update.

Mobile.Orientations

  • iOS 14
    • Xcode testing:
      • Xcode "Device Orientation" setting:
        • Auto - sets Portrait, Upside Down, Landscape Left, Landscape Right
        • Portrait - sets Portrait & Upside Down (should only be Portrait?)
        • LandscapeLeft - not set
        • LandscapeRight - not set
        • PortraitUpsideDown - not set
      • Startup orientation change works
      • Orientation locked for all
    • Homescreen launch testing:
      • Xcode Device Orientation setting (same as Xcode testing)
      • Startup orientation change works
      • Orientation lock
        • I realised that I was testing with the wrong Uno at the time(FuseX), and can confirm that your PR change worked as expected. (I do think we need to change the "Portrait values" though, see below)

Portrait value discussion

So there's a "Landscape" value thats supposed to represent both LandscapeLeft and LandscapeRight. In the same vein, "Portrait" is trying to represent both "Portrait" and "Upside Down". I think this is done in error as when you just want to restrict to only Portrait, you won't be able to.

What do you guys think of the value "PortraitUpsideDown" for the combo of "Portrait" and "Upside Down", then Portrait represents just Portrait and UpsideDown represents Upside Down?

@AndrewEQ
Copy link
Contributor

AndrewEQ commented Oct 1, 2020

So to confirm, the difference between "Xcode testing" and "home screen testing" feels like compile-time vs run-time checking?

Compile time checking: #if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
Runtime checking: if (@available(iOS 13.0, *)) {

@AndrewEQ
Copy link
Contributor

AndrewEQ commented Oct 1, 2020

Android case-insensitive version for: https://github.com/fuse-open/uno/blob/6aa9bf8c65c7bce7a23bdebe5ed1d7b216929d7d/lib/UnoCore/Targets/Android/app/src/main/AndroidManifest.xml#L103

#if @(Project.Mobile.Orientations:ToLower:Equals('portrait'))
                  android:screenOrientation="portrait"
#elif @(Project.Mobile.Orientations:ToLower:Equals('upsidedown'))
                  android:screenOrientation="reversePortrait"
#elif @(Project.Mobile.Orientations:ToLower:Equals('landscapeleft'))
                  android:screenOrientation="landscape"
#elif @(Project.Mobile.Orientations:ToLower:Equals('landscaperight'))
                  android:screenOrientation="reverseLandscape"
#elif @(Project.Mobile.Orientations:ToLower:Equals('portraitupsidedown'))
                  android:screenOrientation="sensorPortrait"
#elif @(Project.Mobile.Orientations:ToLower:Equals('landscape'))
                  android:screenOrientation="sensorLandscape"
#else
                  android:screenOrientation="user"
#endif

@AndrewEQ
Copy link
Contributor

AndrewEQ commented Oct 1, 2020

iOS case-insensitive version for: https://github.com/fuse-open/uno/blob/c3a98bb5138419348cc9170f7c5352e2b9accdad/lib/UnoCore/Targets/iOS/%40(Project.Name)/%40(Project.Name)-Info.plist#L54


	<key>UISupportedInterfaceOrientations</key>
	<array>
#if @(Project.Mobile.Orientations:ToLower:Equals('auto')) || @(Project.Mobile.Orientations:ToLower:Equals('portraitupsidedown'))
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
#endif
#if @(Project.Mobile.Orientations:ToLower:Equals('auto')) || @(Project.Mobile.Orientations:ToLower:Equals('landscape'))
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
#endif
#if @(Project.Mobile.Orientations:ToLower:Equals('portrait'))
		<string>UIInterfaceOrientationPortrait</string>
#endif
#if @(Project.Mobile.Orientations:ToLower:Equals('upsidedown'))
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
#endif
#if @(Project.Mobile.Orientations:ToLower:Equals('landscapeleft'))
		<string>UIInterfaceOrientationLandscapeLeft</string>
#endif
#if @(Project.Mobile.Orientations:ToLower:Equals('landscaperight'))
		<string>UIInterfaceOrientationLandscapeRight</string>
#endif
	</array>
	<key>UISupportedInterfaceOrientations~ipad</key>
	<array>
#if @(Project.Mobile.Orientations:ToLower:Equals('auto')) || @(Project.Mobile.Orientations:ToLower:Equals('portraitupsidedown'))
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
#endif
#if @(Project.Mobile.Orientations:ToLower:Equals('auto')) || @(Project.Mobile.Orientations:ToLower:Equals('landscape'))
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
#endif
#if @(Project.Mobile.Orientations:ToLower:Equals('portrait'))
		<string>UIInterfaceOrientationPortrait</string>
#endif
#if @(Project.Mobile.Orientations:ToLower:Equals('upsidedown'))
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
#endif
#if @(Project.Mobile.Orientations:ToLower:Equals('landscapeleft'))
		<string>UIInterfaceOrientationLandscapeLeft</string>
#endif
#if @(Project.Mobile.Orientations:ToLower:Equals('landscaperight'))
		<string>UIInterfaceOrientationLandscapeRight</string>
#endif
	</array>

@AndrewEQ
Copy link
Contributor

AndrewEQ commented Oct 1, 2020

Updated my round 2 findings as I realised I was testing with the wrong Uno.

@AndrewEQ
Copy link
Contributor

AndrewEQ commented Oct 1, 2020

Code for SystemUI.uno:

		private static int GetProjectSettingsOrientation()
 		{
 			if (@(Project.Mobile.Orientations:ToLower) == "portraitupsidedown")
 				return  extern<int>"UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown";
 			if (@(Project.Mobile.Orientations:ToLower) == "portrait")
 				return  extern<int>"UIInterfaceOrientationMaskPortrait";
 			if (@(Project.Mobile.Orientations:ToLower) == "upsidedown")
 				return  extern<int>"UIInterfaceOrientationMaskPortraitUpsideDown";
 			if (@(Project.Mobile.Orientations:ToLower) == "landscape")
 				return  extern<int>"UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight";
 			if (@(Project.Mobile.Orientations:ToLower) == "landscapeleft")
 				return  extern<int>"UIInterfaceOrientationMaskLandscapeLeft";
 			if (@(Project.Mobile.Orientations:ToLower) == "landscaperight")
 				return  extern<int>"UIInterfaceOrientationMaskLandscapeRight";
 			return  extern<int>"UIInterfaceOrientationMaskAll";
 		}

@AndrewEQ
Copy link
Contributor

AndrewEQ commented Oct 4, 2020

Trigger Orientations

Ok tested Xcode 12

  • iOS 13/14 - iPhone SE - works fine on all orientations (home screen test)
  • iOS 12 - iPhone 6 - works fine on all orientations except for "PortraitUpsideDown", it crashes with:
    *** Terminating app due to uncaught exception 'UIApplicationInvalidInterfaceOrientation', reason: 'Supported orientations has no common orientation with the application, and [uAppDelegate shouldAutorotate] is returning YES'

Mobile.Orientations

I guess we can fix them in another PR, seeing how the naming logic is broken for when we just want Portrait, we could add another value called "PortraitOnly".

@mortend
Copy link
Member

mortend commented Nov 4, 2020

Is this PR ready to be merged now?

@AndrewEQ
Copy link
Contributor

AndrewEQ commented Nov 4, 2020

Its just upside down orientation not working on iOS12 but if y'all ok with that for now, then we're good to go.

@mortend mortend merged commit 8054f03 into fuse-open:master Nov 4, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants