I created a app with the iOS 4.1 SDK.
In this app I did the following: I add two UIView's as subview to the Window of the Application. One view is a navigationcontrollerview and the other is a view that displays advertisement. The user can then navigate trough the app will the ads are displayed uninterrupted.
Now comes the problem: I wanted to add the option to rotate the device from portrait to landscape and back. The problem is only the first view (in my case the navigationcontrollerview) is rotated without problem. The second view is never rotated. Documentation from Apple confirms that this is the default behavior.
So, how do I get my second view rotated? Do I need to restructure my application? And why is the second view never rotated? That doesn't make any sense to me.
Are you just adding the second view (the one that doesn't rotate) as a plain UIView? If so, try adding it after containing it within a UIViewController class. I had the same issue in my app not so long ago. instead of [window addSubview:sampleView], I did [window addSubview:[sampleViewController view]]; where sampleViewController subclasses UIViewController.
In general, always use corresponding view controllers and contain views in them and then add subviews to avoid these issues.
Related
I'm a new iOS programming and I'm developing a simple iPhone game that needs to run in landscape only. I've ...
set supported orientations in the target settings
added the req. plist item (initial interface orientation)
overridden shouldAutorotateToInterfaceOrientation to return YES only for landscape modes
.. and the app "looks" correct, but there are a few odd things going on.
Issue 1 - I'm trying to manually position my views and not rely on autolayout. I've got a UIView in a NIB that I'm loading that needs to be positioned 150px from the right edge of the screen. I have to get the UIViewController's view's height (not width) to correctly position it - like it's not rotated to landscape at this point in the code.
Issue 2 - Implementing a UINavigationController to go from the title screen to the game interaction. When I'm pushing the interaction UIViewController to the stack, it slides in from the right like it's supposed to. When I go back to the title by popping the interaction, it slides UP to the title. It's seems like it's rotating back to portrait?
I think there is something very basic that I'm missing, but I can't find it in my app code. I've gone over the lists for a landscape app but they don't mention more than the list above.
Are there any other things/settings/methods to override that I should be on the look out for?
You need to set shouldAutorotateToInterfaceOrientation in your other viewControllers as well. Especially the ones displayed inside your UINavigationController.
Issue 2:
Don't use many UIViewController's. Use one view controller. Create one main UIViewController and for other UIViewController's just do:
[mainviewcontrl presentModalViewcontroller: child_viewcontrl animated: YES];
For delete a child view controller, use
[child_viewcontrl dismissModalViewControllerAnimated: YES];
The view in question was created in IB on the root view and wired to a class method in the rootViewController. In the view's class method I am overriding drawRect to draw it's content. I am calling setNeedsDisplay on the view from the rootViewController after the user changes some parameters.
On the iPhone this works fine. On the iPad, however, drawRect in the class method does not get called.
I suspect that the reason for this has to do with the drawing cycle. On the iPhone the view used by the user to change the parameters is pushed onto the screen over the view in question. On the iPad this view is added to the root view above, not over, the view in question. So when the view used to change the parameters is closed, unlike on the iPhone, a drawing cycle is not generated and so the invalidated view is not updated.
I'm stuck. Any help would be appreciaetd.
Found the problem...
I discovered that in the xib being used by the root view controller for the iPad, the wiring for the UIView object in question was some how corrupted. When I hovered over the bullet of it's iIBOutlet variable in the .h file an UISegmentedControl elsewhere in the xib would be selected instead of the intended UIView. Right-Clicking the UISegmentedControl did not show that the link existed. The only way I was able to get it fixed was to remove all the wiring from both objects and rewire them properly. Calling setNeedsDisplay now works. as expected, on the iPad just like it does on the iPhone.
My app has 4 tabs. All the view controllers support rotation, and indeed are rotated when I rotate the device. For one of the view controllers, I need to reposition some of the subviews upon rotation. I do this in willRotateToInterfaceOrientation of that view controller, and it works fine.
The problem comes when I switch to a different tab, then rotate the device, then go back to the original tab. It apparently has not received the rotation notification, since willRotateToInterfaceOrientation has not been called. So it seems as though only the "active" view controller gets notified that the device has rotated.
The question: how do you get all the view controllers (controlled by a TabBarController) to rotate?
Unfortunately this is a bug in iOS 3.x. It works fine in iOS 4.x. I've seen apps that manually keep track of orientation changes and then do the rotation manually for inactive viewcontrollers. Sucks.
Looking through the iOS 3.2 docs to make sure this works, there is a viewControllers property in UITabBarController. Try something like this:
for (UIViewController * viewController in tabBarController) {
// Do stuff here with each 'viewController'.
}
I recommend that you do something with the UIViewController's -shouldAutorotateToInterfaceOrientation: method but you may have another way in which you plan on achieving the rotation.
You should also check for the interface orientation in viewWillAppear method of the controller whose subviews frame you are changing.Because when you move to the new tab and rotate the device and now when you tap another tab the viewWillAppear method will we called and there you can change the frames accordingly.
I also faced the same problem which i sorted out using this approach
I'm doing an iPad tech demo and I'm running into a serious technical problem.
I have an app concept that leverages UISplitViewController, but NOT as the primary controller for the entire app.
The app flow could be described roughly as this:
Home screen (UIViewController)
List->Detail "Catalog" (UISplitViewController)
Super Detail Screen (UIViewController but could conceivable also be a child of SplitView).
The problem is in the flow between Home and Catalog. Once a UISplitViewController view is added to the UIWindow, it starts to throw hissy fits.
The problem can be summarized at this:
When a UISplitView generates a popover view, it appears to then be latched to its parent view. Upon removing the UISplitView from the UIWindow subviews, you will get a CoreGraphics exception and the view will fail to be removed.
When adding other views (presumably in this case, the home screen to which you are returning), they do not autorotate, instead, the UISplitView, which has failed to be removed due to a CG exception, continues to respond to the rotation instead, causing horrible rendering bugs that can't be just "dealt with". At this point, adding any views, even re-adding the SplitView, causes a cascade of render bugs.
I then tried simply to leave the SplitView ever present as the "bottom" view, and keeping adding and removing the Home Screen from on top of it, but this fails as SplitView dominates the Orientation change calls, and Home Screen will not rotate, even if you call [homeScreen becomeFirstResponder]
You can't put SplitView into a hierarchy like UINavigationController, you will get an outright runtime error, so that option is off the table. Modals just look bad and are discourages anyway.
My presumption at this moment is that the only proper way to deal with this problem is so somehow "disarm" UISplitViewController so that it can be removed from its parent view without throwing an unhandled exception, but I have no idea how.
If you want to see an app that does exactly what I need to do, check out GILT Groupe in the iPad app store. They pulled it off, but they seem to have programmed an entire custom view transition set.
Help would be greatly appreciated.
Apple states:
The split view controller’s view
should always be installed as the root
view of your application window. You
should never present a split view
inside of a navigation or tab bar
interface.
This does mean it should be root view and not subview of another view. Even though they add:
You should never present a split view inside of a navigation or tab bar interface
That does not mean you can add it as a subview of any other controller either. (sorry)
I have a feeling that what you are experiencing is the byproduct of trying to do so. I am actually surprised that GILT Groupe's app did not get rejected. Apple has a tendency to enforce these HIG guidelines rather strictly lately. They (as you found out already) cause a rather nasty runtime error when you attempt to add them to a NavigationController.
I've solved this for myself... actually worked around... by presenting all other possible full screen views as modals of the SplitView...
This is an unsavory way of doing things in my book, but Apple leaves you little choice if you want to leverage a SplitView only "sometimes" within an app.
I had some success by creating a second UIWindow. I associate the UISplitViewController with that, and switch it out with the main window when I want to show the splitview. It seems to work they way I wanted, except for a slight delay in rotations and a log message about "wait_fences".
Unless your developing for jail-broken devices then bending apples rules/wishes isn't a good idea. Like Jann and Jasconius state above this means keeping a splitView controller view root, not over-using modals (vague) and not using multiple windows.
Also, the Gilt app is only available in the US
I'v been trying to find a solution too and have ended up programatically removing views from the window like Tuannd talks about but the landscape rendering bug is unforgivable.
#Jasconius, What is the max number of modals are you are presenting at any time?
I am struggling with this same issue. I've been trying various things poking at the UISplitViewController as a black box and see how it reacts.
I seem to have come up with a solution to my case which seems to be working satisfactorily.
The key appears to be that the first view added to the UIWindow is the only view properly initialized. All the problems I've had tend to stem from incorrect notification of the orientation of the device. The first view added, apparently has this correctly configured.
In my case I didn't want the UISplitView as the first view. The following is working for me.
The app delegate application:didFinishLaunching method is special. Adding the view to the UIWindow must occur here. If it is done elsewhere it does not get configured properly.
Essentially the magic sauce, is have the split view be the first view added to the window. Its then ok to remove it as long as you retain the UISplitViewController. From then on you can swap other views in and out, including the UISplitView and most things seem to be ok.
I've still run into a few issues. Popovers on views other than the split view are confused on the views frames and location of toolbar buttons and will display in the wrong location. I place then in a specific location and that seems to handle that case.
If a popover on the split view is still displayed, and you try to view another view, the orientation of the second view is confused and shows up sideways. If that view is accessed before the popup is displayed, all is well. I've fixed this my manually dismissing the popover before switching to any other view.
Here's the code if it helps. All the controllers are instance variables of appDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// This also seems to work as good magic. Seems to set orientation and size properties that persist.
[window addSubview:splitViewController.view];
[splitViewController.view removeFromSuperview];
[self switchToNewViewController:firstController];
[window makeKeyAndVisible];
return TRUE;
}
- (void)switchToNewViewController:(UIViewController *)newViewController {
[popoverController dismissPopoverAnimated:FALSE];
if (newViewController != currentViewController) {
[currentViewController removeFromSuperview];
currentViewController = newViewController;
[window addSubView:newViewController.view];
}
}
Just wanted to say that I was running into these same issues, found this forum topic, and followed the advice from g051051 above. This is working perfectly for me. I am not seeing any glitch, and no messages about wait_fences in the console of the device.
I simply used IB to create two UIWindow objects in the main XIB, created as normal the UISplitViewController and then also an instance of my other controller derived from UIViewController (which I am using for full screen display). I have simply hooked them up by attaching the rootViewController for each UIWindow to its appropriate controller.
In application:didLaunch...: method I can decide which window to send the makeKeyAndVisible method and which to set to hidden. When the user want to switch back and forth I simply have to send makeKeyAndVisible to one and set the hidden property on the other, that's all there is to it.
As indicated all of the rotation related messages are sent to each controller appropriately, regardless of which one is associated with the currently visible window.
Anyway, works great for me, and actually quite easy to set up.
First of all, my code isn't complex - in fact it's just two sample programs from "Beginning iPhone Development: Exploring the iPhone SDK", combined into one program. I took the ViewSwitcher application, which switches between a blue view and a yellow view, and replaced the YellowViewController with the CameraViewController from the camera application.
I have three ViewControllers total. SwitchViewController just switches between BlueViewController and CameraViewController.
Inside CameraViewController, I'm trying to use a UIImagePickerController to choose an image. The picker is presented with presentModalViewController. The catch is that I want to do this in landscape orientation.
Everything works fine under 2.2.1, and everything works fine in 3.0 in portrait mode.
In 3.0 under landscape orientation, however, things break. If I set SwitchViewController to landscape orientation, my screen goes white when I try to present the picker. If I rotate the iPhone a few times, I can see a corner of the picker, which apparently was displayed off screen.
If I set CameraViewController to landscape orientation, the picker doesn't come up at all.
I think this page may have a clue when it says "The most prominent change [in 3.0] I can see is that the modal view controller will always use the application frame instead of the parent view controller's frame." I don't understand exactly what that means, though.
Any help is greatly appreciated. Thanks!
I believe what that means is this: Modal views always use the full screen, even if the parent view controller that invokes them controls a view that is only part of the screen. This makes sense for standard modal views like the camera picker, but I can see why someone who creates a custom modal view might want it to be smaller.
Not sure if that really helps solve your problem though.
Question -- what happens if you bring up the modal view and THEN rotate the phone?
You must have used addSubview in your parent view controller, try using presentModalViewController:.