Disable SplitViewController only on iPhone - Swift - swift

I'm working on an universal iOS app and I'm using SplitViewController. I have been trying to disable the splitView on iPhone 6+ and 6s+ in landscape mode but nothing seems to work. I attempted to override the UITraitCollection by setting its horizontalSizeClass to Compact, it doesn't seem to work either. Has anyone attempted this? Below is my code to override UITraitCollection
override func overrideTraitCollectionForChildViewController(childViewController: UIViewController) -> UITraitCollection? {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
let collections = [UITraitCollection(horizontalSizeClass: .Compact), UITraitCollection(verticalSizeClass: .Compact) ]
return UITraitCollection(traitsFromCollections: collections)
}else {
return super.traitCollection
}
}
I have this in my viewDidLoad of the splitViewController.

Related

iPad orientation programmatically not working

I have an issue about the orientation on iPad.
I disabled the orientation on iPhone but i can change it programmatically on some specific controller.
But on iPad i can’t. i don’t know why although i searched in this website and some developers write answers to check UIRequiresFullScreene and i do it.
But it disables the orientation on all screens.
Is there any way to give ability to change orientation on specific controller on iPad ?
The following code i used to change iPad Orientation but it is not working.
UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")
Thanks advance
When you set programmatically the orientation of the device. To perform the orientation animation the function
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask
from AppDelegate is being called.
If you have set it there to return only UIInterfaceOrientationMask.portrait you will never see the transition.
Also I personally have these kind of problems and to solve this I had to programmaticaly force the phone to rotate to the current Orientation and then to rotate to the wanted orientation.
Example for phone to rotate landscapeLeft from portrait
func setOrientationToLandScapeOnly() {
appDelegate.shouldForceLandscapeOnly = true
appDelegate.shouldForcePortraitOnly = false
if UIDevice.current.userInterfaceIdiom == .phone {
switch UIApplication.shared.statusBarOrientation {
case .landscapeLeft, .landscapeRight:
return
default:
APIOrientationHandler.shared.setDeviceValueForOrientation(UIInterfaceOrientation.portrait.rawValue)
APIOrientationHandler.shared.setDeviceValueForOrientation(UIInterfaceOrientation.landscapeLeft.rawValue)
return
}
}
}
And inside AppDelegate.swift
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if shouldForcePortraitOnly {
return UIInterfaceOrientationMask.portrait
}
if shouldForceLandscapeOnly {
return UIInterfaceOrientationMask.landscape
}
if (UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad) {
return UIInterfaceOrientationMask.landscape
}
return UIInterfaceOrientationMask.portrait
}

Unable to lock rotation for one view controller in IOS10

Background
I have an app that uses AVFoundation in order to have a custom camera. This happens in the OCRViewController. When I take a picture I send the captured picture to a different view ImagePreviewViewController.
I am using Xcode 10.2.1 (10E1001) with Swift 5
The Goal
What I would like to achieve is to lock the orientation of the ImagePreviewViewController to the original orientation of the image. I already know how to get the orientation of the image but I am not able to lock the orientation of the view.
I get the image rotation as such: let imageOri = capturedImage?.imageOrientation
What did I try?
I tried the accepted answers at and several other sources:
How to lock orientation just for one view controller?
How to lock orientation of one view controller to portrait mode only in Swift
https://www.hackingwithswift.com/example-code/uikit/how-to-lock-a-view-controllers-orientation-using-supportedinterfaceorientations
Reading the documentation at https://developer.apple.com/documentation/uikit/uiviewcontroller#//apple_ref/occ/instm/UIViewController/supportedInterfaceOrientations under Handling View Rotation the following is stated:
I also tried the many suggested solutions while writing this query, however, the majority appears to use the following approach (or a variation of it), and it does not work for me.
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}
override func shouldAutorotate() -> Bool{
return false
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return UIInterfaceOrientation.Portrait
}
As of iOS 8, all rotation-related methods are deprecated. Instead, rotations are treated as a change in the size of the view controller’s view and are therefore reported using the viewWillTransition(to:with:) method.
However, I am not sure how to progress from here.
Interesting code snippets
The following method is in my OCRViewController, here I instantiate the ImagePreviewViewController and attach the captured image.
func displayCapturedPhoto(capturedPhoto : UIImage) {
let imagePreviewViewController = storyboard?.instantiateViewController(withIdentifier: "ImagePreviewViewController") as! ImagePreviewViewController
imagePreviewViewController.capturedImage = capturedPhoto
navigationController?.pushViewController(imagePreviewViewController, animated: true)
}
Using the below override function inside my ImagePreviewViewController I am able to detect the orientation of the view controller.
override func viewDidAppear(_ animated: Bool) {
if UIDevice.current.orientation.isLandscape {
print("Landscape")
} else {
print("Portrait")
}
}
To restrict the rotation of one screen, use this.
In AppDelegate
var restrictRotation = Bool()
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if !restrictRotation {
return .portrait
} else {
return .all
}
}
In your viewcontroller add the function,
func restrictRotation(restrict : Bool) -> Void {
let appDelegate = UIApplication.shared.delegate as? AppDelegate
appDelegate?.restrictRotation = restrict
}
In the ViewDidload() method, call the function to disable rotation.
self.restrictRotation(restrict: false)
in viewWillDisappear() method, call the function to enable rotation.
self.restrictRotation(restrict: true)

How to transfer a boolean from app delegate to a viewcontroller?

I'm currently trying to transfer a variable from app delegate to a ViewController. Most of the guides I find are in objective-c and I don't have the skill to convert that code into swift. Could someone help me find a way to do this.
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
var isIpad: Bool!
if UIDevice.current.userInterfaceIdiom == .pad {
isIpad = true
print("iPad")
}else{
print("not iPad")
isIpad = false
}
}
Although I would suggest to take a look to #rmaddy's first comment, you could declare a stored property anywhere in the module (it doesn't has to be declared in the app delegate):
let isIpad = UIDevice.current.userInterfaceIdiom == .pad
It means that there is no need to pass a value from the app delegate to the view controller. Thus in the view controller -for example-:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if isIpad {
// ...
}
}
}

cannot get shouldautorotate to fire when changing orientation

I'm developing an app with Xcode9 using Swift4 and it was originally intended for just iPads but now needs to run on phones too. I need to lock the phones to Portrait and iPads remain Portrait/Landscape.
I've done this before using the shouldautorotate function and returning true or false depending on the device as below.
override func shouldAutorotate() -> Bool {
return false
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.all
}
However, when I change the orientation it does not fire. Is this another depreciated method and if so how do I restrict the orientation now?
If your view controller is embedded into UINavigationController or UITabBarController, then this shouldAutorotate() is queried from them as this container is the topmost view controller. And after iOS updates it does not ask for contaning view controllers if they need to be rotated or not. Thus you may use your custom subclass and creat it in runtime or provide as custom calss in your storyboard:
import UIKit
class CustomNavigationController: UINavigationController {
override var shouldAutorotate: Bool {
return self.viewControllers.last?.shouldAutorotate ?? true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return self.viewControllers.last?.supportedInterfaceOrientations ?? .all
}
}
After much googling, this worked for me
https://stackoverflow.com/questions/38969419/ios-how-can-enable-or-disable-rotate-on-each-uiviewcontroller

How can I get the calling ViewController from the supportedInterfaceOrientationsFor method in AppDelegate?

I've added a feature in my app which is in Swift 4, which allows a view to orientate to landscape or portrait as it displays a chart. I've created two separate views for each orientation and I've created the logic to handle the process. It works okay except for one minor niggle which I can solve if I can determine the calling ViewController. I've tried using
self.window?.rootViewController?.presentedViewController
Which has not proved to be accurate. I've checked the parameters of both application and window without any success. Is this possible to do or do I need to rely on a global variable instead?
The app delegate returns a bitmask that indicates the "maximum set" of orientations supported by the complete app, regardless of which view controller is currently presented.
The presented, concrete view controller is also asked in addition (supportedInterfaceOrientations) and returns its own supported orientations.
In the end, both return values will be intersected to get the current supported orientations.
If I remember correctly, both values have to agree in at least one orientation. If e.g. the app delegate says it only supports "portrait (up)", but the view controller only supports "landscape left", your app will crash (or do something even worse)
You can retrieve the topmost viewController of your app like this:
func topViewController() -> UIViewController {
let controller = UIApplication.shared.keyWindow?.rootViewController
if let presentedVC = controller?.presentedViewController {
return presentedVC
}
return controller!
}
Getting to the topViewController is dependent on the configuration of the views. Using .isKind(of: VC) helps you return the appropriate ViewController based on whatever view hierarchy you may be using:
private func topViewControllerWithRootViewController(rootViewController: UIViewController!) -> UIViewController? {
if (rootViewController == nil) { return nil }
if (rootViewController.isKind(of: UITabBarController.self)) {
return topViewControllerWithRootViewController(rootViewController: (rootViewController as! UITabBarController).selectedViewController)
} else if (rootViewController.isKind(of: UINavigationController.self)) {
return topViewControllerWithRootViewController(rootViewController: (rootViewController as! UINavigationController).visibleViewController)
} else if (rootViewController.presentedViewController != nil) {
return topViewControllerWithRootViewController(rootViewController: rootViewController.presentedViewController)
}
return rootViewController
}