changing view controller when the device orientation changes - objective-c

Im making an ios app in xcode 4 and I need a way of changing what is displayed on the screen when the device orientation changes.
does anyone know how to do this?
thanks.

probably you need to check first how to detect if the device orientation changes
see here: how to detect orientation change
then on the example on the link, you should know ho to change the view which is displayed on the screen. You can do it by using methods such as addSubview, presentModalViewController, pushViewController(for NavigationController)
eg:
[self presentModalViewController:aView animated:YES];
[self addSubview:aView];
[self.navigationController pushViewController:aView animated:YES];

Related

QLPreviewController delegate method doesn't get called in iOS 10, but does get called if ran earlier than iOS 10

Here is my code. This may sound like redundant question but my scenario is different as I am not adding QLPreviewController as a subview but present as a controller.
After downloading from dropbox, I present it like-
self.pdfViewController = [[QLPreviewController alloc] init];
self.pdfViewController.delegate = self;
self.pdfViewController.dataSource = self;
[self presentViewController:self.pdfViewController animated:YES completion:nil];
and I also have QLPreviewControllerDataSource, QLPreviewControllerDelegate listed as the protocol. Besides, it is working if being run in earlier than iOS 10.0.
Please help me.
It looks like iOS 10 has changed the way that QLPreviewController is presented. On iOS 9 when I preview an image by presenting the QLPreviewController modally I see a nice zoom effect and the initial state of the preview is with a black background and the navigation and toolbar hidden. I can tap the image to make the bars visible (which changes the background to white). Tapping again toggles the state.
On iOS 10 the same code results in the white background view appearing and the zoom animation being incorrect (it seems to appear from off the bottom of the screen).
I found that implementing this practically undocumented new data source method for iOS 10 fixed the issue:
- (UIView* _Nullable)previewController:(QLPreviewController *)controller
transitionViewForPreviewItem:(id <QLPreviewItem>)item
{
return [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:MIDPhotoImageRowIndex_Image inSection:MIDPhotoSectionIndex_Image]];
}
The view I return is the same view that previewController:frameForPreviewItem:inSourceView: is using as the reference for the original content's frame (i.e. the image view in my table cell).
The documentation for this delegate method at the time of writing just says "No overview available".
Implementing that method did mean that the previewController:frameForPreviewItem:inSourceView: is now called on iOS 10. I just wish there was a way to default to the original black background without navigation bars.

Need a way to force a UIInterfaceOrientation rotation without iOS device rotating

I need to know if there is a way to tell a iOS7 device to set a views orientation without the device being rotated. Some way in code to trigger the device to calling the code that tells it which way to display the view.
If the device is in landscape and remains held in landscape orientation while a certain change happens I want to force a change to show the view in portrait orientation, at which point the user would need to turn the device to look at it properly. I'll explain why below
Looking at my app might make my description clearer - it is free to download
I have a number of view controllers (embedded in navigationControllers) and only one of them needs to be rotated into landscape and then only under certain conditions.
Solutions here on StackOverflow seem to be to make a category on UINavigationController giving it shouldAutorotate and supportedInterfaceOrientations methods and then use those methods in the individual viewControllers to block or allow rotations.
This has worked for me .... however
On the one view controller I wish to rotate , I don't want it to rotate all the time.
This view controller is the diveSiteDetailsController, (if you have downloaded the app you need to select dive sites on the first page then click the '+' to see it). It has a UISegmentedController and 4 subviews (3 tableviews and 1 other UIView). The current version on the App Store works fine now i've solved this - but looking at it may help you understand my issue better).
On diveSiteDetailViewController the UISegmentedController is used to switch between the 4 subviews.
All the subviews are used to enter data about the same dive site but as there is a lot of potential data, I have broken it into logical chucks each of which is a subview - location, data (depths,currents, visibility), type of environment and notes.
The .hidden property of each subview is used to make them appear and disappear.
I only want the second subview to rotate (the data view - it has some sliders on it that are easier to work with if in landscape).
restricting this rotation is easy - iI achieved it like this
- (NSUInteger)supportedInterfaceOrientations{
if (self.dsDataRangeSlidingTV.hidden) {
return UIInterfaceOrientationMaskPortrait;
}
return UIInterfaceOrientationMaskAllButUpsideDown;
}
Now the view will only rotate to landscape when the data table view is displayed.
However, once in landscape, if I chose a different subview with the UISegmentedController then they are, obviously, shown in landscape also as the iOS device hasn't done a rotation. This is the situation I am trying to avoid.
Rotating the iOS device will return those views to portrait as expected but i need to trigger the device to to reevaluate its display when I use the UISegmentedController to switch from the data subview to another subview and its that triggering that I don't know how to do.
any suggestions greatly received.
Heres a workaround that is working for me
I've added the following few lines to the end of my method that responds to the UISegmentedControl being tapped.
UIViewController *aDummyController = [[UIViewController alloc]init];
[self presentViewController:aDummyController animated:NO completion:nil];
[self dismissViewControllerAnimated:NO completion:nil];
adding a new viewController and popping it off triggers the rotation . This is a kludgey way of achieving what I wanted.
I found the solution in this post
Is there a documented way to set the iPhone orientation?
all credit to Josh who although not the accepted answer is the one that 99 people currently have up voted.
I still have a bug in that, if I were holding the device in landscape (although the display is portrait view) whilst on the screen that segues into the diveSiteDetailsController then the initial view the diveSiteDetailsController display will be in landscape.
To get around this I created a Bool property called notThisTime on the diveSiteDetailsController and set it to true in the prepareFor Segue on the viewController that called it.
i then did changed supportedInterfaceOrientation to
- (NSUInteger)supportedInterfaceOrientations
{// DLog(#"Running %# '%#'", self.class, NSStringFromSelector(_cmd));
if (self.notThisTime){
return UIInterfaceOrientationMaskPortrait;
}
if (!self.dsDataRangeSlidingTV.hidden) {
return UIInterfaceOrientationMaskAllButUpsideDown;
}
then at the end of the ViewDidLoad method I added
self.notThisTime = NO;
I would still love to hear from anyone with a suggestion how better to handle this. pushing and popping a dummy view to get the iPhone to do an orientation check seems like a work around for something that should just be available as a standard method call.
One final Note - the iOS simulator does not like this - you need to check on the device - it sometimes tries to draw the iPhone container in landscape while the screen is drawn vertically - however it does work fine on the iPhone

Minimum steps to get autorotate to work

I've trying to implement autorotate but my app is not listening to me!
The app has a tab bar controller which supervises 3 view controllers. The tab bar is created programatically in the app delegate. Each of the view controllers has this standard simple method:
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
The app delegate looks like this:
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:gameVC, settingsVC, helpVC, nil];
self.window.rootViewController = self.tabBarController;
In addition, in the target summary area I have all 4 orientations for both the iPad and iPhone activated.
In the simulator, no rotation occurs with either device. I seem to be missing something. Perhaps one more setting is needed? Something out of order? There is nothing else in the project related to rotating views.
The only thing that you seemed to not have said in your response that I can think of is changing the device orientations under your info.plist. I know from personal experience that if you click on the supported device orientations in the target summary area, it might not actually change it in the Info property list. Check and make sure that all four are selected in the property list by doing the following:
Go to your Info.plist
Look under Supported interface orientations and Supported interface orientations (iPad)
Make sure that it has 4 strings under both: Portrait (bottom home button), Portrait (top home button), Landscape (left home button), Landscape (right home button)
User a ViewController for super purpose, and then inheritance it in the each of view controllers. In the super ViewController add this
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
return YES;
}
So, you just need to do once to make them autorotate
from http://developer.apple.com/library/ios/#DOCUMENTATION/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/TabBarControllers.html#//apple_ref/doc/uid/TP40011313-CH3-SW26
Tab bar controllers support a portrait orientation by default and do
not rotate to a landscape orientation unless all of the contained view
controllers support such an orientation. When a device orientation
change occurs, the tab bar controller queries its array of view
controllers. If any one of them does not support the orientation, the
tab bar controller does not change its orientation.
#Zack #AlexanderZats This was subtle. I was reading this SO answer which brought me here This 2nd link is a great discussion of different possible reasons an app may not rotate. The last point caught my attention. Sure enough, I was overriding initWithNibName and not calling super on it. I think this ultimately meant that the the VCs were not in the responder chain. A huge thanks to all who gave me ideas and suggestions!

splash screen with loading indicator using storyboard

I'm working on an app for a blog site, and I'm trying to keep the Default.png launch image up with a spinning indicator while I load the initial headlines into the tableview.
I set up a viewcontroller/view in my storyboard with the launch image and indicator.
I then have the following in the viewDidLoad: method of my navigationController's rootview
[self.navigationController presentModalViewController:[self.storyboard instantiateViewControllerWithIdentifier:#"SplashLoader"] animated:NO];
And once the headlines are loaded I use:
[self.navigationController dismissModalViewControllerAnimated:NO];
Am I way off base here? Or is this the right way to be doing this?
I've seen people doing something like this in app delegate, but that was before storyboards... If I'm supposed to be doing this in the app delegate then how do I instantiate the view controller out of the storyboard?
Thanks,
Any advice or suggestions would be appreciated.
The way I've done it is to make a UIImageView using the Default.png image. In viewWillAppear: I add it to the view controller's view property. In viewDidAppear: I use a UIView animation to fade out the image view by setting its alpha to 0. Upon completion of the animation, viewDidAppear: removes the image view from its superview and releases it (sets it to nil).
You have to keep a record of how many time viewWillAppear: and viewDidAppear: have been called because you only want this animation to happen when the app launches. Also, you have to think about which image to use to create the image view. If this is an iPhone app, you want to use Default.png. If it's an iPad app, you want to use Default-Portrait~ipad.png or Default-Landscape~ipad.png, depending on the orientation of the device when the app launches.
I'm not sure how you would accomplish the same effect in the app delegate. That seems like an unnecessarily complicated approach to me.

iOS 5.1 + UISplitViewController in PortraitMode + UIActionSheet in MasterController = Assertion failure

I have an app based on a UISplitViewController that shows an ActionSheet in the MasterViewController of the Split. Before iOS 5.1, I had no problems presenting the action sheet in the popover presented by the split, but now, apparently there is something wrong with the new "slide-in" way to show the MasterController.
The thing is that when I'm trying to present the ActionSheet, using any [actionSheet show..] method, the app crashes with the following error (The exact Assertion is the following).
*** Assertion failure in -[UIActionSheet presentSheetInPopoverView:], /SourceCache/UIKit_Sim/UIKit-1914.84/UIActionSheet.m:1816
sharedlibrary apply-load-rules all
Error in re-setting breakpoint 1:
Catchpoint 2 (throw)Error in re-setting breakpoint 1:
Error in re-setting breakpoint 1:
Current language: auto; currently objective-c
I google this for a while, but no substantial answers.. some people say it can be a bug in the
new SplitViewController...
Ideas?
Thank you in advance!
UPDATE: I posted a possible generic workaround, check it out. If it works for you, leave a comment.... If its ok, I will mark it as correct in a couple of days
Based on the above, and with massive respect to the Apple engineer who helped me at WWDC, here is the solution which not only works around this bug, but also points the popover at the right button.
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]))
{
[actionSheet showFromBarButtonItem:self.actionSheetBarButtonItem animated:YES];
}
else
{
CGRect windowsRect = [self.navigationController.toolbar convertRect:self.actionSheetBarButtonItem.customView.frame toView:self.view.window];
[actionSheet showFromRect:windowsRect inView:self.view.window animated:YES];
}
I have this same problem too.
One workaround which prevents the crash at least is to show your UIActionSheet like this:
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
[self.actionSheet showFromBarButtonItem:sender animated:YES];
} else {
[self.actionSheet showInView:self.view.window];
}
So in portrait mode, the action sheet is displayed in the center of the window. Not ideal, but at least it doesn't crash. And when in landscape mode, it behaves as normal.
Just as omz commented, it seems this issue has been solved in iOS 5.1.1 by apple.
So I decide to just add it to known issue section of change log for my app, and the workaround is to suggest the users to upgrade to iOS 5.1.1.
Another option to keep the popover affect of pointing at a particular option, you can actually do the following:
1. Create your own UIPopover
2. Create your own UIViewController inside of the UIPopover.
3. Display the UIActionSheet inside the newly created UIViewController.
4. SetPopoverContentSize from the UIActionSheet's Size.
5. Lastly, Wire your UIActionsheet's Clicked method to dismiss the popover.
Takes a little more code, but gives you the same functionality you had before for the most part, and has a cool little slide-in effect for the UIActionsheet.
I think the following is a generic solution based on Tap Form's answer:
CGRect windowsRect = [actionSheetContainerView convertRect:viewToPresentActionSheet.frame toView:actionSheetContainer.window];
[actionSheet showFromRect:windowsRect inView:actionSheetContainer.window animated:YES];
This will resent the actionSheet in the window, but pointing the right direction