I have an UIViewController that supports all UIInterfaceOrientationMasks
But, in one certain case I need to prevent it from rotation
Before iOS 16 i was just handling this case like this
override var shouldAutorotate: Bool {
return !screenRecorderIsActive
}
And everything was working fine
After update to iOS 16 my controller keeps rotating and I can't find a way to fix it
In my case, I need to prevent auto rotation not based upon a particular orientation, but rather based upon what you're doing in the app and what kind of hardware your device has. And, I need to continue to support iOS versions all the way back to 9.3. These updates forced me to to do the following:
The new functions aren't available back in 9.3, so I have to do some checking if the version of iOS is 16.x. If so, then I had to implement supportedInterfaceOrientations so that the current user interface orientation is queried and only returns TRUE if the orientation requested is the same as the current user interface orientation. In other words, don't allow a change.
Also, since both returning from the background or rotations or events from other view controllers can change my logic choice for which orientations are allowed, I had to be sure to call the setNeedsUpdateOfSupportedInterfaceOrientations method when returning from the background or any other time I needed to prevent auto rotation.
Again, I had to wrap this code in iOS version checks. Finally I had to implement things the old way for older versions of iOS.
I was very disappointed that such a breaking change was introduced. Yes, the methods were deprecated for a long time, but the new methods weren't introduce until 16.0 - and even more critically, my code does different things when using the front landscape camera now available on the iPad 10, so I had to wait until I had a unit in my hands to uncover these issues.
Quoted from the iOS 16 release notes:
[UIViewController shouldAutorotate] has been deprecated is no longer
supported. [UIViewController attemptRotationToDeviceOrientation] has
been deprecated and replaced with [UIViewController
setNeedsUpdateOfSupportedInterfaceOrientations].
Workaround: Apps relying on shouldAutorotate should reflect their
preferences using the view controllers supportedInterfaceOrientations.
If the supported orientations change, use `-[UIViewController
setNeedsUpdateOfSupportedInterface
To implement dynamic supported interface orientations, you can use some code like this:
var screenRecorderIsActive: Bool {
didSet {
setNeedsUpdateOfSupportedInterfaceOrientations()
}
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if screenRecorderIsActive {
return [.landscape] // For example, or a variable representing the orientation when the condition was set
}
return [.all]
}
Related
My app is crashing when you return to it after switching away from it using the 4-finger task-switch gesture on an iPad.
When the user does the 4-finger gesture to switch away from my app, I see applicationWillResignActive: then applicationDidEnterBackground: in my app delegate, then (assuming we're currently in portrait orientation) my top-level view controller gets viewWillTransitionToSize:withTransitionCoordinator: with a size that indicates landscape layout (even though the device has not rotated). This of course results in a lot of resizing and rearranging of views for the new orientation. Immediately after that I get viewWillTransitionToSize:withTransitionCoordinator: with a size that indicates portrait (the original orientation of the device). And again, I go through all my layout again for the new (actually, original) orientation.
What I'm finding is that if I wait for all of this to complete, I can switch in and out of my app all day. If, on the other hand, I switch back to my app while this needless work is going on, I get a crash deep in some iOS transition code.
At this point I'm trying to understand why I get these stray rotation events. If I can avoid getting those, I can avoid getting into whatever situation is causing the crash. They don't seem necessary.
I can't think of a reason why it would be useful or correct to get viewWillTransitionToSize in the background, so just bounce out if you are in the background:
if UIApplication.shared.applicationState == .background {
return
}
Still, this feels like a bug, and in my opinion you should report it to Apple.
It turns out there isn't a way to prevent the rotation events from being reported to the app. I suspect that's an iOS bug. However, ignoring viewWillTransitionToSize:withTransitionCoordinator: when the application state is UIApplicationStateBackground and doing the same in the view's layoutSubviews (if present) allowed me to work around the problem.
The multitasking features got updates in iOS 11, one of those was slide over which is demonstrated in the gif below.
With these changes it's no longer possible to use the techniques that check frame size from iOS 9 to detect if another app is a "slide over" over my app.
Is there any new method to detect if another app is running as slide over?
I was able to get this working fairly easily on an iPad Pro (which supports side-by-side apps, not just slide-overs). Here's the code:
class ViewController: UIViewController {
override func viewWillLayoutSubviews() {
isThisAppFullScreen()
}
#discardableResult func isThisAppFullScreen() -> Bool {
let isFullScreen = UIApplication.shared.keyWindow?.frame == UIScreen.main.bounds
print("\(#function) - \(isFullScreen)")
return isFullScreen
}
}
The end result is that it will print "true" if the view is full screen and "false" if it's sharing the screen with another app, and this is run every time anything is shown, hidden, or resized.
The problem then is older devices that only support slide-over. With these, your app is not being resized anymore. Instead, it's just resigning active use and the other app is becoming active.
In this case, all you can do is put logic in the AppDelegate to look for applicationWillResignActive and applicationDidBecomeActive. When you slide-over, you get applicationWillResignActive but not applicationDidEnterBackground.
You could look for this as a possibility, but you cannot distinguish between a slide-over and a look at the Notifications from sliding down from the top of the screen. It's not ideal for that reason, but monitoring application lifecycle is probably the best you can do.
I am detecting the device orientation with these lines of code:
UIDevice.currentDevice().orientation.isPortrait.boolValue
UIDevice.currentDevice().orientation.isLandscape.boolValue
And I detect check if it's an iPad with this code:
userInterfaceIdiom == .Pad
I used this code in commonInit() function which is called in the Init() function. When I execute my code on iPad the two first lines return both false which is not correct, one of them should be true. The third line is working fine.
If I use the code in other functions, such as supportedInterfaceOrientations(), it works fine. Do you know what could be the problem?
You are probably neglecting this documentation:
You also use the UIDevice instance to detect changes in the device’s
characteristics, such as physical orientation. You get the current
orientation using the orientation property or receive change
notifications by registering for the
UIDeviceOrientationDidChangeNotification notification. Before using
either of these techniques to get orientation data, you must enable
data delivery using the beginGeneratingDeviceOrientationNotifications
method. When you no longer need to track the device orientation, call
the endGeneratingDeviceOrientationNotifications method to disable the
delivery of notifications.
Also, this is a 3-D orientation. You probably don't want that. What is the actual value of simply the orientation property? See:
enum UIDeviceOrientation : Int {
case Unknown
case Portrait
case PortraitUpsideDown
case LandscapeLeft
case LandscapeRight
case FaceUp
case FaceDown }
Probably what you really want is here:
"interfaceOrientation" is deprecated in iOS 8, How to change this method Objective C
Sometimes TraitCollections doesn't fill all your design needs. For
those cases, Apple recommends to compare view's bounds :
if view.bounds.size.width > view.bounds.size.height {
// ... }
I am trying to create an action when I turn the screen of my device.
In the Apple documentation I have read that you have to use this method:
override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
println("I rotate.")
self.tabBar.frame = CGRectMake(0, 80, self.view.bounds.size.width, self.view.bounds.size.height);
}
But when I turn the screen in the log does not print "I rotate."
EDIT:
These are the settings of the project:
The willRotateToInterfaceOrientation function is deprecated in iOS 8 as you can see in the Apple documentation here. See the discussion here on StackOverflow.
From the documentation, you also are supposed to call super:
Your implementation of this method must call super at some point during its execution.
That shouldn't cause the issue you are seeing though. Double check that your target allows rotation. Go to your project, select the target, and under general make sure you have other orientations checked. If you have only one orientation checked then you can rotate the device, but it won't change orientation.
Where is this method? It will only be called if in a subclass of UIViewController.
EDIT: It seems that willRotateToInterfaceOrientation is not called in a UITabBarController subclass (which seems to be what you're using) in iOS 8, but it is called in iOS 7. Even though the method is deprecated, it is still called in a direct subclass of UIViewController in iOS 8. But, for iOS 8 and above, it is preferable to use the new viewWillTransitionToSize:withTransitionCoordinator:.
I am working in flash CS5.5 on an app for iOS. I want to get the ipad/iphone to stop animating the orientationChange and just change it directly, is this possible?
I thought this was a solution but it didnt help AS3 - iOS force landscape mode only?.
If you try setting Stage.autoOrients = false;, the flash.events.StageOrientationEvent.ORIENTATION_CHANGE will never fire. That's helpful for disabling orientation changes altogether, but not for your issue. While I haven't tried it myself, you may be able to listen to the event:
flash.events.StageOrientationEvent.ORIENTATION_CHANGING
You may be able to call event.preventDefault() in that listener to stop the actual rotation from occuring. Then you can manually set it yourself:
Stage.setOrientation(StageOrientation.ROTATED_RIGHT);
have you tried the SO answer: Disable orienation change rotation animation ?
the code from that answer that goes in the view-controller that is the home for your flash CS5.5 or air 3.5 is:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[UIView setAnimationsEnabled:YES];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
[UIView setAnimationsEnabled:NO];
return TRUE; /* Your original orientation booleans, in case you prevent one of the orientations */
}
that code makes use of native iOS UIViewController functions that can be overridden. you would have to have a native iOS objective C class that overrides UIViewController, and then you could insert the code above. calls are made when the device is rotated to these as part of view controller life cycle.