How can I lock camera view orientation to landscape in iOS? - ios

I'd like to fix my Camera View in landscape Mode. And also show an alert when user rotate view to portrait. is there any solution available ?

Put this in your view controller:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.current.orientation.isLandscape {
print("will turn to landscape")
} else {
print("will turn to portrait")
//print an alert box here
UIDevice.current.setValue(UIInterfaceOrientation.landscapeLe‌​ft.rawValue, forKey: "orientation")
}
}
Source: How to detect orientation change?
Or put this to the view that needs to be in landscape:
override func shouldAutorotate() -> Bool {
return false
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.landscape
}
But this one cannot post an alert.

Related

How to maintain landscape or portrait mode when converting app to IPAD?

I'm currently adapting an iphone app to ipad and have implemented the following code in the viewController to maintain one of the views in landscape (the other views are in portrait):
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
//TODO: - Keep presentationView in landscape
let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
}
override func viewDidDisappear(_ animated: Bool) {
let value = UIInterfaceOrientation.portrait.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .landscapeLeft
}
override var shouldAutorotate: Bool {
return true
}
This works fine for the iphone but does not work when displaying the app on iPad i.e. the view rotates and can be seen in both portrait and landscape. Be grateful for any suggestions on how I can adapt the code to make it work on iPad.
Add method viewWillTransition(to:with:).
Notifies the container that the size of its view is about to change.UIKit calls this method before changing the size of a presented view controller’s view. You can override this method in your own objects and use it to perform additional tasks related to the size change.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isLandscape {
print("Landscape")
// Update frame
} else {
print("Portrait")
// Update frame
}
}

iOS Allow different screen orientation withouth autorotation

I want to keep a portrait orientation even if user rotate the phone. At the same time, I want to change the orientation with a button.
If I put only portrait as supported orientation in plist and then rotate, application gives me error. If I put all the supported orientation but shouldAutorotate method to NO, Application crashes.
So, basically as I can see I can only support multiple orientation if I let the application to autorotate.
Can I achieve what I need?
shouldAutorotate
this is a get-only property.
what you can do is:
override var shouldAutorotate: Bool {
return false
}
Maybe this is also interesting for you: how to dissable and enable auto rotate on swift?
May be the following line of code will work for you. set orientation programmatically.
In your Appdelegate class write this code:
var orientationLock = UIInterfaceOrientationMask.all
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return self.orientationLock
}
2.Make custom helper class as follows:
struct AppUtility {
static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {
if let delegate = UIApplication.shared.delegate as? AppDelegate {
delegate.orientationLock = orientation
}
}
/// OPTIONAL Added method to adjust lock and rotate to the desired orientation
static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) {
self.lockOrientation(orientation)
UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
}
}
then use it in your desire viewcontroller like:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
AppUtility.lockOrientation(.portrait)
// Or to rotate and lock
// AppUtility.lockOrientation(.portrait, andRotateTo: .portrait)
}
override func viewWillDisappear(_ animated: Bool){
super.viewWillDisappear(animated)
// Don't forget to reset when view is being removed
AppUtility.lockOrientation(.all)
}
note: keep remember Require Full Screen option is checked from target settings.

Swift - How to detect orientation changes

I want to add two images to single image view (i.e for landscape one image and for portrait another image)but i don't know how to detect orientation changes using swift languages.
I tried this answer but it takes only one image
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.currentDevice().orientation.isLandscape.boolValue {
print("Landscape")
} else {
print("Portrait")
}
}
I am new to iOS development,any advice would be greatly appreciated!
let const = "Background" //image name
let const2 = "GreyBackground" // image name
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = UIImage(named: const)
// Do any additional setup after loading the view.
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isLandscape {
print("Landscape")
imageView.image = UIImage(named: const2)
} else {
print("Portrait")
imageView.image = UIImage(named: const)
}
}
Using NotificationCenter and UIDevice's beginGeneratingDeviceOrientationNotifications
Swift 4.2+
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)
}
func rotated() {
if UIDevice.current.orientation.isLandscape {
print("Landscape")
} else {
print("Portrait")
}
}
Swift 3
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
func rotated() {
if UIDevice.current.orientation.isLandscape {
print("Landscape")
} else {
print("Portrait")
}
}
⚠️Device Orientation != Interface Orientation⚠️
Swift 5.* iOS16 and below
You should really make a difference between:
Device Orientation => Indicates the orientation of the physical device
Interface Orientation => Indicates the orientation of the interface displayed on the screen
There are many scenarios where those 2 values are mismatching such as:
When you lock your screen orientation
When you have your device flat
In most cases you would want to use the interface orientation and you can get it via the window:
private var windowInterfaceOrientation: UIInterfaceOrientation? {
return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
}
In case you also want to support < iOS 13 (such as iOS 12) you would do the following:
private var windowInterfaceOrientation: UIInterfaceOrientation? {
if #available(iOS 13.0, *) {
return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
} else {
return UIApplication.shared.statusBarOrientation
}
}
Now you need to define where to react to the window interface orientation change. There are multiple ways to do that but the optimal solution is to do it within
willTransition(to newCollection: UITraitCollection.
This inherited UIViewController method which can be overridden will be trigger every time the interface orientation will be change. Consequently you can do all your modifications in the latter.
Here is a solution example:
class ViewController: UIViewController {
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
super.willTransition(to: newCollection, with: coordinator)
coordinator.animate(alongsideTransition: { (context) in
guard let windowInterfaceOrientation = self.windowInterfaceOrientation else { return }
if windowInterfaceOrientation.isLandscape {
// activate landscape changes
} else {
// activate portrait changes
}
})
}
private var windowInterfaceOrientation: UIInterfaceOrientation? {
return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
}
}
By implementing this method you'll then be able to react to any change of orientation to your interface. But keep in mind that it won't be triggered at the opening of the app so you will also have to manually update your interface in viewWillAppear().
I've created a sample project which underlines the difference between device orientation and interface orientation. Additionally it will help you to understand the different behavior depending on which lifecycle step you decide to update your UI.
Feel free to clone and run the following repository:
https://github.com/wjosset/ReactToOrientation
Swift 3
Above code updated:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isLandscape {
print("Landscape")
} else {
print("Portrait")
}
}
Swift 4+:
I was using this for soft keyboard design, and for some reason the UIDevice.current.orientation.isLandscape method kept telling me it was Portrait, so here's what I used instead:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if(size.width > self.view.frame.size.width){
//Landscape
}
else{
//Portrait
}
}
If your are using Swift version >= 3.0 there are some code updates you have to apply as others have already said. Just don't forget to call super:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
// YOUR CODE OR FUNCTIONS CALL HERE
}
If you are thinking to use a StackView for your images be aware you can do something like the following:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isLandscape {
stackView.axis = .horizontal
} else {
stackView.axis = .vertical
} // else
}
If your are using Interface Builder don't forget to select the custom class for this UIStackView object, in the Identity Inspector section at the right panel. Then just create (also through Interface Builder) the IBOutlet reference to the custom UIStackView instance:
#IBOutlet weak var stackView: MyStackView!
Take the idea and adapt it to your needs. Hope this can help you!
Swift 4.2, RxSwift
If we need to reload collectionView.
NotificationCenter.default.rx.notification(UIDevice.orientationDidChangeNotification)
.observeOn(MainScheduler.instance)
.map { _ in }
.bind(to: collectionView.rx.reloadData)
.disposed(by: bag)
Swift 4, RxSwift
If we need to reload collectionView.
NotificationCenter.default.rx.notification(NSNotification.Name.UIDeviceOrientationDidChange)
.observeOn(MainScheduler.instance)
.map { _ in }
.bind(to: collectionView.rx.reloadData)
.disposed(by: bag)
Here is a modern Combine solution:
import UIKit
import Combine
class MyClass: UIViewController {
private var subscriptions = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter
.default
.publisher(for: UIDevice.orientationDidChangeNotification)
.sink { [weak self] _ in
let orientation = UIDevice.current.orientation
print("Landscape: \(orientation.isLandscape)")
}
.store(in: &subscriptions)
}
}
I believe the correct answer is actually a combination of both approaches: viewWIllTransition(toSize:) and NotificationCenter's UIDeviceOrientationDidChange.
viewWillTransition(toSize:) notifies you before the transition.
NotificationCenter UIDeviceOrientationDidChange notifies you after.
You have to be very careful. For example, in UISplitViewController when the device rotates into certain orientations, the DetailViewController gets popped off the UISplitViewController's viewcontrollers array, and pushed onto the master's UINavigationController. If you go searching for the detail view controller before the rotation has finished, it may not exist and crash.
Swift 4
I've had some minor issues when updating the ViewControllers view using UIDevice.current.orientation, such as updating constraints of tableview cells during rotation or animation of subviews.
Instead of the above methods I am currently comparing the transition size to the view controllers view size. This seems like the proper way to go since one has access to both at this point in code:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
print("Will Transition to size \(size) from super view size \(self.view.frame.size)")
if (size.width > self.view.frame.size.width) {
print("Landscape")
} else {
print("Portrait")
}
if (size.width != self.view.frame.size.width) {
// Reload TableView to update cell's constraints.
// Ensuring no dequeued cells have old constraints.
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
Output on a iPhone 6:
Will Transition to size (667.0, 375.0) from super view size (375.0, 667.0)
Will Transition to size (375.0, 667.0) from super view size (667.0, 375.0)
You can use viewWillTransition(to:with:) and tap into animate(alongsideTransition:completion:) to get the interface orientation AFTER the transition is complete. You just have to define and implement a protocol similar to this in order to tap into the event. Note that this code was used for a SpriteKit game and your specific implementation may differ.
protocol CanReceiveTransitionEvents {
func viewWillTransition(to size: CGSize)
func interfaceOrientationChanged(to orientation: UIInterfaceOrientation)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
guard
let skView = self.view as? SKView,
let canReceiveRotationEvents = skView.scene as? CanReceiveTransitionEvents else { return }
coordinator.animate(alongsideTransition: nil) { _ in
if let interfaceOrientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation {
canReceiveRotationEvents.interfaceOrientationChanged(to: interfaceOrientation)
}
}
canReceiveRotationEvents.viewWillTransition(to: size)
}
You can set breakpoints in these functions and observe that interfaceOrientationChanged(to orientation: UIInterfaceOrientation) is always called after viewWillTransition(to size: CGSize) with the updated orientation.
All previous contributes are fine, but a little note:
a) if orientation is set in plist, only portrait or example, You will be not notified via viewWillTransition
b) if we anyway need to know if user has rotated device, (for example a game or similar..) we can only use:
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
tested on Xcode8, iOS11
To get the correct orientation on app start you have to check it in viewDidLayoutSubviews(). Other methods described here won't work.
Here's an example how to do it:
var mFirstStart = true
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if (mFirstStart) {
mFirstStart = false
detectOrientation()
}
}
func detectOrientation() {
if UIDevice.current.orientation.isLandscape {
print("Landscape")
// do your stuff here for landscape
} else {
print("Portrait")
// do your stuff here for portrait
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
detectOrientation()
}
This will work always, on app first start, and if rotating while the app is running.
Another way to detect device orientations is with the function traitCollectionDidChange(_:). The system calls this method when the iOS interface environment changes.
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
{
super.traitCollectionDidChange(previousTraitCollection)
//...
}
Furthermore, you can use function willTransition(to:with:) ( which is called before traitCollectionDidChange(_:) ), to get information just before the orientation is applied.
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator)
{
super.willTransition(to: newCollection, with: coordinator)
//...
}
My app is running on iOS 15 and I have checked only on iPhone/iPad so I can't say about all use cases however I am using the following environment variable:
#Environment(\.verticalSizeClass) private var verticalSizeClass
Then checking its value using the following:
verticalSizeClass == .compact is horizontal
verticalSizeClass == .regular is vertical
https://developer.apple.com/documentation/swiftui/environmentvalues/verticalsizeclass

How I lock rotation on a UIViewController presented as modal form sheet?

I'm presenting a UIViewController modally with presentation style as Form Sheet, and it's not locking orientation when it appears as a form sheet. The form sheet modal presentation style looks different when shown on the iPhone 6+ in landscape, or on the iPad in any rotation.
I'm presenting the VC via a storyboard segue
This same code works correctly in locking orientation on iPhone (except 6+), but does not work when style is form sheet. How can I lock a modal form sheet in portrait on iPad or iPhone 6+?
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return .Portrait
}
override func shouldAutorotate() -> Bool {
return false
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return .Portrait
}
this code works for me, hope it works for u too
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let val = UIInterfaceOrientation.Portrait;
UIDevice .currentDevice() .setValue(val, forKey: "orientation");
}
override func shouldAutorotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation) -> Bool {
return (toInterfaceOrientation == UIInterfaceOrientation.Portrait)
}
override func shouldAutorotate() -> Bool {
return false
}

Force landscape orientation for UIWebView?

I have an app where all the views are portraits but only one view which is a UIWebView which loads a PDF needs to be in Landscape.
My project settings are below:
I have tried the following:
override func shouldAutorotate() -> Bool {
return false
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
let orientation: UIInterfaceOrientationMask = [UIInterfaceOrientationMask.Landscape, UIInterfaceOrientationMask.LandscapeLeft]
return orientation
}
The above code does not seem to be working. I got the code by searching online. I am not sure what I am doing wrong. Any suggestions would be help and if you can provide some sample code would be great.
I just want to force landscape orientation for one UIWebView.
EDIT:
Error:
If its hard to read here is the error:
Supported orientations has no common orientation with the application, and [KM_T_World.MainViewController shouldAutorotate] is returning YES'
Implement this in your View Controller:
override func viewDidLoad() {
super.viewDidLoad()
UIViewController.attemptRotationToDeviceOrientation()
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return [UIInterfaceOrientationMask.LandscapeLeft, UIInterfaceOrientationMask.LandscapeRight]
}
Use this code:
override func viewDidAppear(animated: Bool) {
let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
}
And your Device orientation should be:
Result:
Sample for more Info.
UPDATE:
It is simple to lock orientation for particular view by adding this code:
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask{
return UIInterfaceOrientationMask.Portrait
}

Resources