Handling Screen Orientation Changes in Capacitor Apps

Hinddeep Purohit
6 min readAug 12, 2020

Developers often feel the need to control the orientation of the user’s screen. Certain user interfaces are best displayed in portrait orientation whereas the others in landscape. For instance, it is a common practice to switch to landscape mode when watching videos. Similarly, people often prefer to use apps in one-handed mode. Portrait mode is the de-facto standard for implementing one-handed mode. However, the auto-rotate feature lends users the flexibility to change the orientation of their screen as desired. In circumstances where this behaviour is detrimental to the UX, the developer needs to override this setting. This tutorial discusses how to handle screen orientation changes on capacitor based Android and iOS apps.

Fundamentals of an Ionic App

Hybrid apps built on the ionic framework are merely web apps that run in a WebView and are served from a local web server both wrapped up inside a native application. The web and the native layers can interact with each other by using a bridge that connects the two. This bridge could be the brand-new Capacitor or the age-old Cordova/PhoneGap. Capacitor supersedes Cordova and has many advantages over its predecessor. Perhaps, the most striking and handy reason to prefer capacitor over Cordova is that it gives full access to the native project. With Cordova a developer has a hard time trying to bend the native code. Whereas, with Capacitor a user has fine grained access to the native portion of that project and can open it in native IDE. This flexibility is a boon. For other differences between the two, please visit the following link:

The Problem

Ionic strongly favours write-once-run-everywhere paradigm. Therefore one should prefer Web APIs over native code. Only in situations where JS falls flat on the ground should one resort to native code. Screen Orientation web API would have been the best cross-platform solution for a hybrid app built with Ionic. But upon a deeper inspection it isn’t difficult to find out that the Web API is not supported by Android’s WebView and Safari on iOS. This nasty piece of information leaves no alternative but to write custom native code.

Screen Orientation Web API: https://developer.mozilla.org/en-US/docs/Web/API/Screen/orientation

Those looking to handle screen orientation changes or to restrict the orientation to any particular mode must have come across Ionic’s native plugin called “Screen Orientation”. As the name suggests it isn’t hard to determine that it is the older Cordova’s plugin. Capacitor is backward compatible with Cordova plugins. However, Capacitor is the future of hybrid mobile apps. Thus, older projects should migrate all Cordova plugins to Capacitor in a phased manner and newer ones should only include Capacitor plugins as far as possible.

Native Plugin: https://ionicframework.com/docs/native/screen-orientation

Before whipping up a plugin myself, I scoured the internet for a capacitor plugin that has all the feature of its native counterpart. My endeavours smelt the sea. Seeing no out-of-the-box solution I wrote one myself.

The next reason for writing the plugin may not matter much to consumers of the plugin. However, those want granular control over their native projects and want to minimize the dependence on community-plugins may find it really intriguing. The iOS portion of Cordova’s Screen Orientation Plugin was written in Objective-C. By contemporary standards, Objective-C is a super-arcane language. The source code literally made me cringe! Swift supersedes the esoteric Objective-C and can be learnt “Swift-ly”! As an aside, all the Capacitor plugins made by the creators of Ionic are written in Swift. This implies that the modern-day plugins must use Swift. It’s no surprise that I wrote the iOS portion of the plugin in Swift.

The Solution

The Capacitor plugin I’ve developed can be used to detect the current of orientation of the screen, lock the screen in a particular orientation (disable auto-rotate) or unlock screen rotation (enable auto-rotate) and to listen for orientation changes.

Platforms Supported: Android and iOS

Link to the plugin: https://github.com/hinddeep/capacitor-screen-orientation

Demo Application: https://github.com/hinddeep/Demo-Ionic-Screen_Orientation

npm Link: https://www.npmjs.com/package/capacitor-screen-orientation

Implementation

NOTE: The plugin that I’ve written is for the Capacitor. Capacitor plugins cannot be consumed on Cordova Projects. This sample assumes you’re using Angular as your frontend framework.

1. Open cmd and start an ionic app: ionic start myApp blank — capacitor — type angular

2. Add the required platforms: npx cap add android/ ios

3. Install the Plugin: npm install capacitor-screen-orientation

4. Build the app: npm run build

5. Copy resources from www dir to the native platform: npx cap copy

6. Update the native projects: npx cap update

Android Notes:

1. Open android’s project in Android Studio: npx cap open android

2. Open MainActivity.java and add the following code inside this.init() add(ScreenOrientation.class);Adding the above mentioned line will add the following import statement: import com.bkon.capacitor.screenorientation.ScreenOrientation;If you encounter errors, please add this import manually to MainActivity.javaIf you want to listen for the orientation change event on Android:1.  Open “AndroidManifest.xml” for your app2.  Find the Activity tag3.  Go to android:configChanges=”...”4.     Remove ‘orientation |’ from configChangesFrom the context of Capacitor plugin it is not possible to override onConfigurationChanged(). Orientation can be handled inside onConfigurationChanged() only. To circumvent this quirk we need to restart the activity when the screen orientation changes as it is possible to override onStart() method. So, when the  activity restarts and onStart() method is invoked we can handle the orientation change. Parameters mentioned inside android:configChanges=”...” prevent the activity from restarting. In order to restart the activity we need to remove “orientation” from android:confiChanges=”...”.SPECIAL NOTE: Ionic has implicitly disabled PORTRAIT_SECONDARY.Supported Orientations:1.     LANDSCAPE: left of right is decided by the device’s sensor2.     LANDSCAPE_PRIMARY: explicitly specified by developer3.     LANDSCAPE_SECONDARY: explicitly specified by developer4.     PORTRAIT: up or upside down is decided by the device’s sensor5.     PORTRAIT_PRIMARY: explicitly specified by the user6.     PORTRAIT_SECONDARY: explicitly specified by the user7.     CURRENT: current orientation of the device

iOS Notes:

If you want to lock the screen to the specified orientation on iOS:1.     Open AppDelegate.swift for your app2.     Add the following code:var orientationLock = UIInterfaceOrientationMask.allfunc application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {return self.orientationLock}@objc func setOrientationLock(_ notification: Notification){     if let data = notification.userInfo as? [String: Int]    {       for (_, mask) in data        {           switch mask           {              case 1: self.orientationLock = UIInterfaceOrientationMask.portrait              break;              case 2: self.orientationLock = UIInterfaceOrientationMask.portraitUpsideDown              break;              case 3: self.orientationLock = UIInterfaceOrientationMask.landscapeRight              break;             case 4: self.orientationLock = UIInterfaceOrientationMask.landscapeLeft              break;             case 5: self.orientationLock = UIInterfaceOrientationMask.landscape             break;             default: self.orientationLock = UIInterfaceOrientationMask.all           }        }     }}3. Locate: "func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {"4. Add the following code inside the function: 
NotificationCenter.default.addObserver(self, selector: #selector(self.setOrientationLock), name: NSNotification.Name(rawValue: "CAPOrientationLocked"), object: nil)
SPECIAL NOTE: Ionic has implicitly disabled portraitUpsideDown.

Supported Orientations:

1. all: all orientations

2. allButUpsideDown: all orientations other than upside down

3. landscape: left of right is decided by the device’s sensor

4. landscapeLeft: explicitly specified by the user

5. landscapeRight: explicitly specified by the user

6. portrait: up or upside down is decided by the device’s sensor

7. portraitUpsideDown: explicitly specified by the user

The screen can be locked to any of the above orientations. From the context of Capacitor plugin it is not possible to modify the value of the orientationLock variable defined in the AppDelegate.swift file. To do that we need to manually add code to AppDelegate.

To unlock screen rotation simply alter the orientationLock variable to the following:

var orientationLock = UIInterfaceOrientationMask.all

Web Notes:

1. Open the web portion of your project in your favourite code editor and import the plugin

import { Plugins } from “@capacitor/core”;const { ScreenOrientation } = Plugins;

2. Declare and initialize 3 variables:

screen_orientation: string;screen_orientation_event: string;screen_orientation_lock: string;

3. From the constructor call the function that handles the subscription to the orientation change event fired by the plugin.

constructor() {this.screen_orientation = “”;this.screen_orientation_event = “”;this.screen_orientation_lock = “”;this.subscribeToOrientationChanges();}

4. Create a function to get the current screen orientation:

async getOrientation() {let obj = await ScreenOrientation.getScreenOrientation();this.screen_orientation = obj.orientation;}

5. Create a function to lock the screen in a particular orientation:

async lockOrientation() {await ScreenOrientation.lockScreenOrientation({orientation: this.screen_orientation_lock,});}

Supported values for screen_orientation_lock variable:

1. LANDSCAPE_PRIMARY (Android and iOS)

2. PORTRAIT_PRIMARY (Android and iOS)

3. LANDSCAPE_SECONDARY (Android and iOS)

4. PORTRAIT_SECONDARY (Android and iOS)

5. LANDSCAPE (Android only)

6. PORTRAIT (Android only)

7. CURRENT (Android only)

6. Create a function to unlock screen rotation:

async UnlockOrientation() {await ScreenOrientation.unlockScreenOrientation({});}

7. Create a function that handles subscription to the orientation change event:

subscribeToOrientationChanges() {ScreenOrientation.addListener(“orientation_changed”, (data) => {this.screen_orientation_event = data.orientation;});}

8. Create a function that rotates the device to a specific orientation (iOS only):

async rotate() { 
await ScreenOrientation.rotateTo({
orientation: this.rotateTo,
});
}

I hope you enjoyed reading this article and were able to overcome a frustrating shortcoming. With this plugin you can selectively lock/unlock the orientation of a specific page for a stipulated time interval on Android and iOS. Handling orientation changes becomes a breeze for pure web devs who do not wish to meddle with the native code. Please feel free to report any bugs or issue feature requests/comment on my github repository.

--

--