Unity iOs - screen gets black when changing rotation while Unity is paused - ios

I have a unity app, that uses Vuforia AR library. I extended this app with some iOs code. I wanted to be able to display my custom UIViewControllers sometimes instead of the main view controller that Unity uses. But I want to be able to switch back. I.e: I have a button that opens UIViewController with "About app" information and this controller then has "Exit" button to return to Unity controller.
This is my code for adding the new controller instead of the Unity View Controller:
self.unityRootController = self.keyWindow.rootViewController; // keep reference to the Unity Controller
self.keyWindow.rootViewController = controller; // display my own controller
// this pauses the unity and qcar -> so that QCAR doesn't try to recognize objects, when Unity Controller is not up
QCARUnityPlayer::getInstance().QCARPause(true);
UnityPause(true);
Then when returning from my own controller to Unity controller:
self.keyWindow.rootViewController = self.unityRootController; // return to Unity Controller
// start tracking with QCAR and unpause Unity View
QCARUnityPlayer::getInstance().QCARPause(false);
UnityPause(NO);
This works fine except one case, that's caused me to pull my hair ever since encountering it. My app supports changing orientation of screen in both Unity and ObjectiveC code. And this is the trouble: If I open my View Controller while the device is for example in Portrait Orientation, then turn the device to Landscape orientation, my controller gets correctly rotated. But if I at this moment want to close my custom view controller and return to unity controller, the screen gets black suddenly. After rotating the device to portrait orientation, the unity controller suddenly starts working as expected.
I'd like the unity controller to start directly after closing my custom view controller even if the screen orientation is changed. I suppose I have to add some code after I start again the Unity Player here:
QCARUnityPlayer::getInstance().QCARPause(false);
UnityPause(NO);
// TODO: supposedly some code should go here, that fixes black screen
But I tried to find something in the AppController.mm, that is builded by Unity and I could not find anything, that would help me. I found few lines of unity code, that had the word "orientation" in it, but I tried to use that code and didn't help. Does anybody know, what should I do, please? Thanks a bunch :)

Not sure if you have fixed this, Its been a while... But the reason that your UnityViewController is not functioning properly is because a view controller that is not visible will not receive rotation events. Because you are setting your view controller as the root view controller, the UnityDefaultViewController defined in "iPhone_View.mm" is not receiving rotation events.
I have been studying Unity's rotation for a while now as I have my own issue with a similar setup.
There are a few things that you might be able to try:
In iPhone_View.mm there is a function that is defined called: UpdateOrientationFromController(UIViewController* controller) that might be able to solve your problem. You would want to call this function on the view controller that you designed yourself where you placed your //TODO comment. That function calls a few key functions and methods that handle Unity's interface orientations:
ConvertToUnityScreenOrientation: for some reason Unity Uses their own defined screen orientations, this internally converts the orientation to what Unity uses.
QCARUnityPlayer::getInstance().QCARSetOrientation(): QCAR Also needs its orientation to be set
UnitySetScreenOrientation(_curOrientation): The unity library has an internal way of checking the screen orientation at every rendered frame. I think this function sets an internal flag for the Unity View to check in CheckOrientationRequest() defined in iPhone_View.h as well.
AppController_RenderPluginMethodWithArg is called, I am not sure what this does, but I think it does something important
OrientTo(_curOrientation): this kicks off the actual orientation change and handles the right calls. This includes the call to AppController's onForcedOrientation: method that does the final orientation.
You may be able to call AppController's onForcedOrientation: method directly with a UnityScreenOrientation after you convert a UIDeviceOrientation to a UnityScreen Orientation with ConvertToUnityScreenOrientation() defined in iPhone_OrientationSupport.h. You may not want to call this method directly though, because there are quite a few things that are involved with Unity's orientation methods. You would do this when your app returns from presenting your view controller.
The last thing, and the "hackiest thing" that you can do, that will get the job done, but not in the most efficient way, is to add UnityDefaultViewController to your ViewController as a child ViewController with the addChildViewController: UIViewController Docs, or maybe as just a property, and then override the willRotateToInterfaceOrientation:duration: and didRotateFromInterfaceOrientation:in your ViewController and after calling super, pass the message to the UnityDefaultViewController. This will cause the UnityDefaultViewController to update the UnityView every time the orientation changes, even though the view isn't actually visible. You may or may not need to unpause Unity in order for this to function.
Of the three methods, the last one should definitely work, because I have implemented it myself. However, the first one should work as well, because Unity uses it several times to check the devices current orientation from the splash screen.
Hope this helps!

Related

How to apply an user interface orientation to an iOS storyboard scene

I am developing an iOS application that allows someone to play one of two different games.
Both games are played in a set of rounds. When a player finishes a round the application transitions to a new scene that displays results and then either transitions to another round scene or an end game scene.
Because of the transient nature of the scenes in the application, it makes little sense to persist scenes as is done in more traditional applications, where someone can return to a previous scene.
To that end, most scene navigation is accomplished by using a UINavigationViewController and simply calling setViewControllers(animated:) supplying an array that contains only the UIViewController for the next scene, and this works quite well.
I mentioned that the application will allow someone to play one of two games. It turns out that, on the iPhone, one game is best played in portrait mode, and the other game works best played in landscape mode. I want to be able to control what user interface orientation is used for a particular UIViewController when it becomes active.
I have experimented with using the supportedInterfaceOrientations property of the UINavigationController and application(_ application: supportedInterfaceOrientationsFor window:) for UIApplicationDelegate, and have been able to get things to work for a standard application. However, when I try to apply things to the way my game application works using setViewControllers(animated:), no methods that pertain to applying the desired user interface orientation seem to get called, and all scenes appear at the same orientation.
What suggestions do people have for how I could get a scene in my application to appear with a particular user interface orientation?
Try making your presented view controller call the following method on it's view will appear, or view did appear:
class func attemptRotationToDeviceOrientation()
Some view controllers may want to use app-specific conditions to
determine what interface orientations are supported. If your view
controller does this, when those conditions change, your app should
call this class method. The system immediately attempts to rotate to
the new orientation.
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621400-attemptrotationtodeviceorientati

How can I detect if the Launch Storyboard happened?

does anyone know if there is a way to tell if the launch storyboard executed? Or is there a way to detect if the launch image was displayed?
Basically my app consists of just one view controller with some views (all created programmatically), plus the launch storyboard which I added to the project recently which seems to be working OK.
The reason why it would be useful for me to know if the launch storyboard happened is that my app is a game which basically consists of a big 2D scene that you can zoom into and pan around using the usual touch gestures. When the game starts for the first time it starts fully zoomed-out and centered on the scene: so that is what the launch image matches. But the player pans around and zooms-in on various things, so when they press the home button they could be zoomed into any part of the picture. So when they touch the app's icon to relaunch, if the launch storyboard happens it needs to put their pan/zoom position back to the initial centered zoomed-out position (otherwise as soon as the app gets going it would appear to the user to suddenly snap from the initial position represented in the launch image to where they were, not giving a smooth user experience).
What I do at the moment is -- if applicationDidEnterBackground: method gets called I assume that when the user relaunches the app, by touching the icon, that the launch storyboard will be executed. But the launch storyboard doesn't always happen after it hibernated via applicationDidEnterBackground:, especially if it is only a few minutes since the user pressed the home button.... and those times when the launch storyboard does not happen I need not inconvenience the player and put them back to the initial zoomed-out centered position, I could just let them carry on at the position they were.
I've not used storyboards before, but as I understand it launch storyboard are a special case where you can't make any connections to code because your app's not actually running yet, so I can't think of a way to set a variable, for example, to show the story board happened.
Anyone got any ideas?
Cheers,
Jon
Just use the viewDidLoad method provided by the UIViewController.
Just override the method in your ViewController and then you can perform the task you want when your View(Storyboard) is loaded.
Example:
#implementation MyViewController:UIViewController
-(void)viewDidLoad{
//Insert your code here
}
#end

How to be notified when UISplitViewController pops to master on iPhone mode?

I am using a UISplitViewController on iPhone 6 Plus, and I wish to be notified when in portrait mode the user goes back to the master view controller (i.e. when the split view acts like a navigation controller).
Moreover I wish to be notified in my UISplitViewDelegate the same way I am already notified when the user pushes the detail view controller (via the splitViewController:showDetailViewController:sender: callback)
Do you know the easiest (and cleanest) way to do it?
Thanks in advance.
In depends on what version of iOS we are trying to do that.
iOS 8.0~8.2 way your project should not be compatible with iOS 7 and will work ONLY iOS 8 and UP the best way is to use Size Classes (Trait collections) presented on WWDC 2014 and there was a video about this way, Session 214 "View Controller Advancements in iOS 8"
Briefly it now has a property
This property indicates if detail view is collapsed.(Session 214, Minutes 12:30 ->)
#property (getter=isCollapsed) BOOL collapsed;
A bit of KVO magic and you could have a callback of when detail view is available.
For old iOS 7 way you can detect current orientation of device and get current state of your detail.
Couple methods(rotation callbacks) you have:
willRotateToInterfaceOrientation:duration:
didRotateFromInterfaceOrientation:
interfaceOrientation
!!! BASED on Session 214(43:20) it is not recommended to use these methods with iOS 8 and UP because they are deprecated!!!
I'm not sure this 2 way are the best and easiest but they work properly as expected.And little advice to look WWDC videos, they are pretty helpful.
To be notified on MasterViewController's return, you'll have to create a delegate and set it to the DetailViewController, in performSegueWithIdentifier for example.
If so, when you will go back to master, the delegate will be fired.
Because show detail is adaptive, i.e. it either pushes on the master nav in compact width or sets as the split's secondary in regular width and you only want to know when the detail is popped, you'll instead need to use the master navigation controller's delegate which you can also set your app delegate to be.

IOS: turn off camera

In my app I use iphone camera, but the process is very low when I open it; then I want to start the process when I shows a splashscreen.
The problem is that when splashscreen ends I don't want to show camera.
Then while I show splashscreen I want to start process of camera and quit it before splashscreen disappear. Is it possible?
First up, Apple specifically advise against using splash screens in their Human Interface Guidelines document. I don't know if your app would get rejected for it, but best not to try.
Second, it sounds like you need to optimise the startup of your application and probably the first view controller. To do this, you need to put off loading/initialising everything you can until it's actually needed (known as "lazy initialisation). All code in applicationDidFinishLaunching: in your app delegate and your view controller's init method, loadView, viewDidLoad, viewWillAppear, viewDidAppear should be reviewed for stuff that could be done later.

UISplitViewControllerDelegate problem

I have been creating a new iPad application based on the SplitViewController. I have modeled the app after the MultipleDetailViews sample app from Apple. The problem I have is that the splitViewController:willHideViewController:withBarButtonItem:forPopoverController: method is not called at startup when the device starts in Portrait mode. This method is called during the startup sequence in the MultipleDetailViews sample app. It is during this call that the button to be presented in the toolbar in the main view should be added.
My application is working fine otherwise, i.e. I get the UISplitViewControllerDelegate calls in every other case. I can't figure out though why I am not getting the willHideViewController message during startup.
Any thoughts? Is there some other way I can get at the button to be displayed in the toolbar when starting in portrait mode if not through this willHideViewController message?
Thanks in advance,
-Eric

Resources