I am fairly new to iOS and am a bit stuck on this issue. The problem is that when I rotate the device from landscape to portrait in SFSafariViewcontroller the DONE button gets pushed up. See image here:
I thought about maybe recalculating the height and width in viewWillTransistion but I am honestly not sure what to do. The App is portrait only and only this ViewController should be rotated.
My Safari View Controller:
extension SFSafariViewController {
convenience init(url URL: URL, entersReaderIfAvailable: Bool) {
let configuration = SFSafariViewController.Configuration()
configuration.entersReaderIfAvailable = entersReaderIfAvailable
self.init(url: URL, configuration: configuration)
}
}
public class MySafariViewController: SFSafariViewController {
public var isIdleTimerDisabled: Bool = false
public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if size.height > size.width {
Logging.debug("We are in portrait mode currently. Transitioning to landscape")
} else {
Logging.debug("We are in landscape mode currently. Transitioning to portrait")
}
}
public override func viewDidLoad() {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
appDelegate.allowRotateAlways = true
self.view.insetsLayoutMarginsFromSafeArea = true
}
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if isIdleTimerDisabled {
UIApplication.shared.isIdleTimerDisabled = true
}
}
public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
public override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if isIdleTimerDisabled { // for the case if it's been true at the startup
UIApplication.shared.isIdleTimerDisabled = false
}
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
appDelegate.allowRotateAlways = false
}
public override var shouldAutorotate: Bool {
return true
}
}
I am allowing rotation by setting the allowRotateAlways variabel to true. The variabel is used in AppDelegate in the func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {} function to change the orientation. So orientation change works i.e going from portrait to landscape and vice versa, but I am unsure why I get these side effects.
Related
I am trying to make just one view of the landscape and other all views are on the portrait. It works but there is an issue on every second-time screen doesn't rotate at all.
Here is the scenario that I am talking about:
On every second time, device rotation doesn't work.
Here is my code
AppDelegate.swift
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return .portrait
}
Controller1.swift
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
override var shouldAutorotate: Bool {
return true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
AppUtility.lockOrientation(.portrait)
}
DetailController.swift
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
AppUtility.lockOrientation(.landscape, andRotateTo: .landscapeLeft)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if isMovingFromParentViewController {
AppUtility.lockOrientation(.portrait, andRotateTo: .portrait)
}
}
Code that effects the rotation
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")
}
}
So, How to force rotate detail view every time it loads?
Step 1:- Please define a below-mentioned variable in app delegate.
var shouldRotate : Bool = false
Step 2:- Implement the following delegate method as below.
//MARK:- Set Device Orientation Delegate Method
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return shouldRotate ? .landscapeRight : .portrait
}
Step 3 :- Define constant class and add "Set Portrait Orientation" and "Set Landscape Right Orientation" function in the class.
//MARK:- Set Portrait Orientation
func setPortraitOrientation() {
appDelegateInstance.shouldRotate = false
UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
}
//MARK:- Set Landscape Right Orientation
func setLandscapeRightOrientation() {
appDelegateInstance.shouldRotate = true
UIDevice.current.setValue(UIInterfaceOrientation.landscapeRight.rawValue, forKey: "orientation")
}
Step 4:- Use the following code in your class (Target landscape class)
Step 4.1:- In view will appear call set Landscape Right Orientation function shown below.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
setLandscapeRightOrientation()
}
Step 4.2 :- When you leave the screen implement the following code in the action.
//MARK:- Left Bar Button Tapped Delegate Method
func leftBarButtonTapped() {
_ = self.navigationController?.popViewController(animated: false)
setPortraitOrientation()
}
Happy Coding! Cheers!!
I've got an Objetive-C app delegate that is part of a project I'm interfacing with my own. I'm attempting to lock orientation, but it looks like the old methods (below) no longer work. Setting the orientation in viewDidLoad rotates the screen but allows the user to rotate back to portrait. I've attempted converting to "override var" but no luck. The current solutions (link below) look to all involve an app delegate call, but I cannot locate the solution for a Swift call to an Objective C app delegate.
How to lock orientation of one view controller to portrait mode only in Swift
override func shouldAutorotate() -> Bool {
return false
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.landscapeRight
}
To force only one controller to be at landscape orientation.
Manager:
class OrientationManager {
static let shared = OrientationManager()
/// you can set any orientation to lock
var orientationLock = UIInterfaceOrientationMask.portrait
/// lock orientation and force to rotate device
func lock(for orientation: UIInterfaceOrientationMask, rotateTo rotateOrientation: UIInterfaceOrientation) {
orientationLock = orientation
UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
}
}
Usage:
1) Add code to AppDelegate
func application(_ application: UIApplication,
supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return OrientationManager.shared.orientationLock
}
2) Use in controller
open class LandscapeController: UIViewController {
/// orientation for one controller
override open func viewDidLoad() {
super.viewDidLoad()
OrientationManager.shared.lock(for: .landscape, rotateTo: .landscapeLeft)
}
/// set previous state of orientation or any new one
override open func viewWillDisappear(_ animated : Bool) {
super.viewWillDisappear(animated)
OrientationManager.shared.lock(for: .portrait, rotateTo: .portrait)
}
}
Solved my problem using this link, after trying just about everything.
how to lock portrait orientation for only main view using swift
I utilized the answer from JasonJasonJason and updated the code to get rid of the ->.
The below code went in my first VC
extension UINavigationController {
override open var shouldAutorotate: Bool {
return true
}
override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return (visibleViewController?.supportedInterfaceOrientations)!
}
}
Then subsequent VCs
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.landscape
}
Then since I had a TabBarController, I added this to the controller that pushes to the tab view. If you try to re-use the initial UINavigationController extension it won't let you, but the TabBarController ignores it. So the below extension solves it for any VC's with the TabBar
extension UITabBarController {
override open var shouldAutorotate: Bool {
return true
}
override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.landscape
}
}
For xcode 8.x & swift 3.x
We can control device orientation for particular screen.
On AppDelegate.swift make a variable enableAllOrientation:
var enableAllOrientation = false
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if (enableAllOrientation == true){
return UIInterfaceOrientationMask.all
}
return UIInterfaceOrientationMask.portrait
}
& mask for particular view controller as -
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.enableAllOrientation = false
}
I have a portrait application with one landscape ViewController.
I've been digging a lot on how to force the orientation to landscape when the app is locked to portrait and I tried a lot of solutions presented here and not only but without any luck so far.
So far I managed to autorotate the VC I need in landscape but since the orientation is not locked, all other VCs will also rotate.
override func viewDidAppear(animated: Bool)
{
let value = UIInterfaceOrientation.LandscapeRight.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
}
override func shouldAutorotate() -> Bool
{
return true
}
After some more digging I ended up with this after finding a sample project but while it works in that project, it doesn't seem to work for me.
This is in AppDelegate
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
if self.window?.rootViewController?.presentedViewController is ConclusionReferencesVC {
let conclusionReferencesVC = self.window!.rootViewController!.presentedViewController as! ConclusionReferencesVC
if conclusionReferencesVC.isPresented
{
return UIInterfaceOrientationMask.LandscapeRight;
}
else
{
return UIInterfaceOrientationMask.Portrait;
}
}
else
{
return UIInterfaceOrientationMask.Portrait;
}
}
This is in the VC I want to have in landscape:
var isPresented = true
#IBAction
func dismiss()
{
isPresented = false
self.presentingViewController!.dismissViewControllerAnimated(true, completion: nil);
}
For some reason, the supportedInterfaceOrientationsForWindow method does not validate the initial condition.
I also tried these among others but no luck:
How to lock orientation of one view controller to portrait mode only in Swift
supportedInterfaceOrientationsForWindow in Swift 2.0
Any ideas? It seems I'm missing something but I can't figure it out what.
try this for force to LandscapeRight mode only
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if(self.supportedInterfaceOrientations() == UIInterfaceOrientationMask.LandscapeRight && UIDevice.currentDevice().orientation != UIDeviceOrientation.LandscapeRight)
{
let value = UIInterfaceOrientation.LandscapeRight.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
}
}
and then use a category like this
import UIKit
extension UINavigationController{
override public func shouldAutorotate() -> Bool
{
return (self.viewControllers.last?.shouldAutorotate())!
}
override public func supportedInterfaceOrientations() ->UIInterfaceOrientationMask
{
return (self.viewControllers.last?.supportedInterfaceOrientations())!;
}
override public func preferredInterfaceOrientationForPresentation()-> UIInterfaceOrientation
{
return (self.viewControllers.last?.preferredInterfaceOrientationForPresentation())!;
}
}
EDITED
If you are not using navigation controller use this
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if(self.supportedInterfaceOrientations() == UIInterfaceOrientationMask.LandscapeRight && UIDevice.currentDevice().orientation != UIDeviceOrientation.LandscapeRight)
{
let value = UIInterfaceOrientation.LandscapeRight.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
}
}
override func supportedInterfaceOrientations() ->UIInterfaceOrientationMask
{
return .LandscapeRight;
}
I hope this helps you
As an update to Reinier Melian's post, here is the UINavigationController extension in Swift 3:
import UIKit
extension UINavigationController {
override open var shouldAutorotate: Bool {
return (self.viewControllers.last?.shouldAutorotate)!
}
override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return (self.viewControllers.last?.supportedInterfaceOrientations)!
}
override open var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
return (self.viewControllers.last?.preferredInterfaceOrientationForPresentation)!
}
}
Unfortunately, this code crashes if you call a UIImagePickerController, as that is contained within a UINavigationController whose last view controller is nil.
probably I´m crazy ;-), but why don´t u use this?
How can I unlock orientation just for one controller? I allowed just one - portrait - mode for whole my application, but just in one I need a landscape mode. For this I tried the next snippet:
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}
override func shouldAutorotate() -> Bool{
return false
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return UIInterfaceOrientation.Portrait
}
but it doesn't help me. Are there any other solutions?
In Appdelegate define a var :
import UIKit
var isViewAppeared = false
now in Appdelegate:
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {
if isViewAppeared{
return Int(UIInterfaceOrientationMask.AllButUpsideDown.rawValue)
}
return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}
Now in ViewController where you want landscape:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
isViewAppeared = true
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
isViewAppeared = false
UIView.animateWithDuration(0.4, animations: { () -> Void in
let value = UIInterfaceOrientation.Portrait.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
})
}
Do the following:
enable the desired orientations in the General Info Screen
in each ViewController.swift
override the autorotation function with either true or false (true for the one that should rotate, false for the others)
Hope that helps :)
No matter what I do, status bar keeps coming when i open image picker and won't go away after it is dismissed. I tried various swift solutions I was able to read on this site that are supposed to be fix the problem, but it won't help at all.
Here is what I do. I subclass the picker controller:
class MyImagePickerController: UIImagePickerController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.setNeedsStatusBarAppearanceUpdate()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.setNavBar()
}
override func prefersStatusBarHidden() -> Bool {
self.setNavBar()
return true
}
override func childViewControllerForStatusBarHidden() -> UIViewController? {
return nil;
}
func setNavBar() -> Void {
self.setNavBar(65)
}
func setNavBar(height: CGFloat) -> Void {
var frame = self.navigationBar.frame;
frame.size.height = height;
self.navigationBar.frame = frame;
}
}
then I to call it from an IBoutled action:
func chooseImageFromGallery() {
var image = MyImagePickerController()
image.delegate = self
image.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
image.allowsEditing = false
self.presentViewController(image, animated: true, completion: nil)
}
Also, of course, I set the bar hidden in the Plist file. Problem is specifically when calling the picker controller.
Please answer in Swift.
Use below code to do this
import Foundation
extension UIImagePickerController {
override public func prefersStatusBarHidden() -> Bool {
return true
}
}
this is an extention(category) of UIImagePickerController and works for me.