Some quick issues, i have 5 VC on stack. when i am in my second VC, then if i rotate to landscape and when i move to 1st VC my layout is totally collapsed.
in my second VC i added viewWillTransition and i am invalidateLayoutof my table view. And if i add tableview.reloaddata() in my first screen viewWillAppear .Then it's fine.
But consider if i have 5 VC on stack i can not write code in all VC. Is there any better way to handle it.
In your app delegate add this....
var orientationLock = UIInterfaceOrientationMask.all
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return self.orientationLock
}
struct AppUtility {
static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {
if let delegate = UIApplication.shared.delegate as? AppDelegate {
delegate.orientationLock = orientation
}
}
static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) {
self.lockOrientation(orientation)
UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
}
}
Used this from any view controller like this...
AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait)
Related
I want to be able to rotate a modally presented view controller, but not rotate the view controller that presented it. I am controlling orientation changes in my app like this:
In my AppDelegate:
var orientationLock: UIInterfaceOrientationMask = .portrait
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return orientationLock
}
In my OrientationManager:
struct OrientationManager {
static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {
if let delegate = UIApplication.shared.delegate as? AppDelegate {
delegate.orientationLock = orientation
}
}
static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation) {
self.lockOrientation(orientation)
UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
}
}
In my ModallyPresentedViewController:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
OrientationManager.lockOrientation(.all)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
OrientationManager.lockOrientation(.portrait, andRotateTo: .portrait)
}
Currently this works, but it does rotate the modal view below it, which is why I have to call OrientationManager.lockOrientation(.portrait, andRotateTo: .portrait) when the view disappears. When the view disappears the presenting view controller is still rotated for a few seconds before it moves back to being in portrait mode. How can I prevent the presenting view controller from rotating? Thanks!
(Side note, does anyone know if UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation") is a private API?)
In my project changing the orientation for single view controller alone to both
landscape and portrait.
Here is the code i tried for changing orientation for view controller
#IBOutlet weak var imagePhoto: UIImageView!
Added in viewDidLoad:
NotificationCenter.default.addObserver(self, selector:
#selector(viewWillTransition(to:with:)), name:
NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
override func viewWillTransition(to size: CGSize, with coordinator:
UIViewControllerTransitionCoordinator){
if UIDevice.current.orientation.isLandscape {
print("Landscape")
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.orientationLock = .landscape
} else {
print("Portrait")
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.orientationLock = .portrait
}
}
Code added in Appdelegatedata
func application(_ application: UIApplication,
supportedInterfaceOrientationsFor window: UIWindow?) ->
UIInterfaceOrientationMask {
if let rootViewController = UIApplication.topViewController() {
if rootViewController is PostImageZoomViewController {
let controller = rootViewController as!
PostImageZoomViewController
if controller.isPresented {
return .all
}
}
}
return .portrait
}
I am designing an universal app and I like to support portrait only for both iPhone and iPad. But I need iPad to support both regular and upside down portrait orientation.
So I set Supported interface orientations for both iPhone and iPad in info.plist as Portrait (top home button) and Portrait (bottom home button).
Following in AppDeleagte.swift
var shouldSupportAllOrientation = false
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if (shouldSupportAllOrientation == true){
return UIInterfaceOrientationMask.all
}
return UIInterfaceOrientationMask.portrait
}
Following in ViewController.swift
override func viewWillAppear(_ animated: Bool) {
let appdelegate = UIApplication.shared.delegate as! AppDelegate
appdelegate.shouldSupportAllOrientation = false
let value = UIInterfaceOrientation.portraitUpsideDown.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
}
When I rotate my iPad upside down in simulator, it still remains upside down and does not rotate 180 degrees. Where am I going wrong?
You didn't mention how you had configured the Deployment Info for the project. By default, the 'Upside Down' option is unchecked for Universal projects, and you will need to select this to get the orientation you need
I am able to achieve this by doing
Following in AppDeleagte.swift
var shouldSupportAllOrientation = false
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if (shouldSupportAllOrientation == true){
return UIInterfaceOrientationMask.all
}
return [UIInterfaceOrientationMask.portrait, UIInterfaceOrientationMask.portraitUpsideDown]
}
Following in ViewController.swift
override func viewWillAppear(_ animated: Bool) {
let appdelegate = UIApplication.shared.delegate as! AppDelegate
appdelegate.shouldSupportAllOrientation = false
}
My whole application is in portrait mode. I just want to use one view controller in landscape mode (left).
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask
{
if let _navigationController = window?.rootViewController as? UINavigationController {
if _navigationController.topViewController is FullScreenPlayerVC {
return UIInterfaceOrientationMask.LandscapeLeft
}
}
return UIInterfaceOrientationMask.Portrait
}
This is my controller A
override func shouldAutorotate() -> Bool
{
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask
{
return UIInterfaceOrientationMask.Portrait
}
Now i push Controller B. This is my controller B
override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)
let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
}
override func shouldAutorotate() -> Bool
{
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask
{
return UIInterfaceOrientationMask.Landscape
}
It works as per my requirement when i push controller B while holding my device in portrait mode but if i am holding my phone in landscape left already.
It does not perform desired action. Searched a lot about it but not able to find the solution yet.
I have tried many solutions but nothings working fine.
This is a generic solution for your problem and others related.
1. Create auxiliar class UIHelper and put on the following methods:
/**This method returns top view controller in application */
class func topViewController() -> UIViewController?
{
let helper = UIHelper()
return helper.topViewControllerWithRootViewController(rootViewController: UIApplication.shared.keyWindow?.rootViewController)
}
/**This is a recursive method to select the top View Controller in a app, either with TabBarController or not */
private func topViewControllerWithRootViewController(rootViewController:UIViewController?) -> UIViewController?
{
if(rootViewController != nil)
{
// UITabBarController
if let tabBarController = rootViewController as? UITabBarController,
let selectedViewController = tabBarController.selectedViewController {
return self.topViewControllerWithRootViewController(rootViewController: selectedViewController)
}
// UINavigationController
if let navigationController = rootViewController as? UINavigationController ,let visibleViewController = navigationController.visibleViewController {
return self.topViewControllerWithRootViewController(rootViewController: visibleViewController)
}
if ((rootViewController!.presentedViewController) != nil) {
let presentedViewController = rootViewController!.presentedViewController;
return self.topViewControllerWithRootViewController(rootViewController: presentedViewController!);
}else
{
return rootViewController
}
}
return nil
}
2. Create a Protocol with your desire behavior, for your specific case will be landscape.
protocol orientationIsOnlyLandscape {}
Nota: If you want, add it in the top of UIHelper Class.
3. Extend your View Controller
In your case:
class B_ViewController: UIViewController,orientationIsOnlyLandscape {
....
}
4. In app delegate class add this method:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
let presentedViewController = UIHelper.topViewController()
if presentedViewController is orientationIsOnlyLandscape {
return .landscape
}
return .portrait
}
Final Notes:
If you that more class are in landscape mode, just extend that
protocol.
If you want others behaviors from view controllers, create other protocols and follow the same structure.
This example solves the problem with orientations changes after push
view controllers
I have a tabBarController which has 4 items. One of them is a camera (a barcode scanner) which I implemented with AVCaptureSession. So, if you tab the tab "scanner" will automatically show you a camera screen.
The problem is that I can't disable autorotate of individual items of the tabBarController. So, the screen of the camera rotates when you rotate the device and is very weird.
I tried:
override func shouldAutorotate() -> Bool {
return false
}
and
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return .Portrait
}
but nothing works.
In your AppDelegate add the following
var shouldSupportAllOrientation = false
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
if (shouldSupportAllOrientation == true){
return UIInterfaceOrientationMask.All
}
return UIInterfaceOrientationMask.Portrait
}
Then go to each view and add the following in viewWillAppear
let appdelegate = UIApplication.sharedApplication().delegate as! AppDelegate
// false = only portrait
// true = all orientations
appdelegate.shouldSupportAllOrientation = false
Update
To lock the screen when you go from landscape to portrait, just add this code in viewWillAppear.
let value = UIInterfaceOrientation.Portrait.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")