iOS Controller method which is called when app goes from inactive - ios

I am moving with Navigation bar by this method to another location:
- (void)shiftNavigationBar
{
self.navigationController.navigationBar.layer.zPosition = 0;
float currentVersion = 7.0;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= currentVersion) {
UINavigationBar *navBar = self.navigationController.navigationBar;
[self.navigationController.navigationBar
setFrame:CGRectMake(navBar.frame.origin.x, 92, navBar.frame.size.width, navBar.frame.size.height)];
}
}
I do this in viewDidAppear and when controller is loaded for first time it's okay. But when I click home button and returns to application navigation bar disappear (ok, it didn't disappear I have another bar in place where normally is navigation bar so it "hides" above this bar) and I need to shift navigation bar again but I tried different methods (Will/DidAppear and so) but It's looks that noone is execute when returning from inactive. I know there is method for this in AppDelegate but what method I can use in controller?
Edit:
Methods which I tried but It didn't worked:
1- Add method shiftNavigationBar to AppDelegate and call it in applicationDidBecomeActive
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[self shiftNavigationBar];
}
- (void)shiftNavigationBar
{
((UINavigationController*)self.window.rootViewController).navigationBar.layer.zPosition = 0;
float currentVersion = 7.0;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= currentVersion) {
UINavigationBar *navBar = ((UINavigationController*)self.window.rootViewController).navigationBar;
[((UINavigationController*)self.window.rootViewController).navigationBar
setFrame:CGRectMake(navBar.frame.origin.x, 92, navBar.frame.size.width, navBar.frame.size.height)];
}
}
2- Call controller method from AppDelegate
- (void)applicationDidBecomeActive:(UIApplication *)application
{
UINavigationController *navController = (UINavigationController*)self.window.rootViewController;
[(MAListRepositoriesVC*)navController.topViewController doMyLayoutStuff:self];
}
// I added shiftNavigationBar to method doMyLayoutStuff
3- Observer in controller
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doMyLayoutStuff:) name:UIApplicationDidBecomeActiveNotification object:nil];
Everytime method was called but everytime navigation bar is back at top. It looks like methods are called too soon and after that there are next changes and returns navigation bar back to top.
Edit2:
As right answer I choose answer with replace navigation bar with custom view because that's what I must do. I tried many solutions which I can find but nothing help me. So I created custom view which looks like I need and mainly which is there where I put it.

Can you post a screenshot or mock of the UI?
Probably you should look for a custom view and replace the navigation bar entirely as modifying it is clumsy and will surely break on future updates. Besides it may risk to get your app rejected.

Try this... It's just a temporary fix you said that seems that your methods get called to early when then just delay them a little bit using the method:
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
Just add probs 0.5-1.0 for the seconds, target is obviously self and selector will be #selector(shiftNavigationBar), userInfo nil, and repeats NO.
I'm not to sure about the bar that you mention appears above your desired bar. Do you call that bar using a method? does it load from a nib file? if you can clarify a little bit more I could help you more with this issue. I have an app that uses a custom navigation bar it works just fine but I need a bit more of detail to help you.
Hope this suggestion helps you at least to troubleshoot your issue.

One possibility would be to put the navBar into a different superview, and then to adjust the frame of the new superview. For example, in your navigation controller viewDidLoad (or awakeFromNib, if it's loaded from a Storyboard/XIB):
// Move the current self.view to contentView, create a new self.view,
// and add the contentView to this new view.
self.contentView = self.view; // navBar is a subview of self.view
self.view = [[UIView alloc] initWithFrame:self.contentView.frame];
CGRect contentFrame = CGRectMake(0,92,self.view.frame.size.width, self.view.frame.size.height-92);
self.contentView.frame = contentFrame;
[self.view addSubview:self.contentView];
(You will need to add a new property to the interface: #property (strong, nonatomic) UIView *contentView;). I leave it to you to add in the code for the different device versions.
This seems to survive backgrounding/becoming active, so you don't need code in your AppDelegate.

Related

iOS UITableViewCell contents move on first scroll

You can see on the gif below that on the first scroll of UITableView cell's content moves a tiny bit. You can barely see it, margins become 1 pixel wider.I've never encountered this before. It seems like there's some layout issue before the first scroll and it resolves itself after the fact. There's no warning in XCode, these custom cells are pretty straightforward, with no layout code overrides.
I don't know where to start, how do I catch this glitch?
UPDATE. I've implemented an obvious workaround for now:
- (void)scrollTableToFixGlitch {
[self.tableView setContentOffset:CGPointMake(0, 1)];
[self.tableView setContentOffset:CGPointMake(0, -1)];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self scrollTableToFixGlitch];
}
Still looking into the problem. I've tried generic UITableViewCells, nothing changed. Seems like it's the problem with View Controller's root view or tableview layout, and not with the table cells.
UPDATE 2.
I ruled out all the animations out of the question, problem lies somewhere in a different region. The glitch is easy to recreate on a much simplified project. My Tab Bar controller is based on MHCustomTabBarController with custom segues and some other additions. Here's what you do to recreate a glitch. Setup a project where your initial VC is embedded in Navigation Controller. The next controller either MHCustomTabBarController or a subclass is pushed to the navigation stack. First visible VC in tab bar is generic Table View Controller. That's it. Glitch appears only if tab bar controller is pushed in navigation stack.
Here's some code that I think matters from tab bar controller:
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (self.childViewControllers.count < 1) {
[self performSegueWithIdentifier:#"viewController1" sender:[self.buttons objectAtIndex:0]];
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if (![segue isKindOfClass:[MHTabBarSegue class]]) {
[super prepareForSegue:segue sender:sender];
return;
}
self.oldViewController = self.destinationViewController;
//if view controller isn't already contained in the viewControllers-Dictionary
if (![self.viewControllersByIdentifier objectForKey:segue.identifier]) {
[self.viewControllersByIdentifier setObject:segue.destinationViewController forKey:segue.identifier];
}
[self.buttons setValue:#NO forKeyPath:#"selected"];
[sender setSelected:YES];
self.selectedIndex = [self.buttons indexOfObject:sender];
self.destinationIdentifier = segue.identifier;
self.destinationViewController = [self.viewControllersByIdentifier objectForKey:self.destinationIdentifier];
[[NSNotificationCenter defaultCenter] postNotificationName:MHCustomTabBarControllerViewControllerChangedNotification object:nil];
}
And a custom segue code:
#implementation MHTabBarSegue
- (void) perform {
MHCustomTabBarController *tabBarViewController = (MHCustomTabBarController *)self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *) tabBarViewController.destinationViewController;
//remove old viewController
if (tabBarViewController.oldViewController) {
[tabBarViewController.oldViewController willMoveToParentViewController:nil];
[tabBarViewController.oldViewController.view removeFromSuperview];
[tabBarViewController.oldViewController removeFromParentViewController];
}
destinationViewController.view.frame = tabBarViewController.container.bounds;
[tabBarViewController addChildViewController:destinationViewController];
[tabBarViewController.container addSubview:destinationViewController.view];
[destinationViewController didMoveToParentViewController:tabBarViewController];
}
#end
UPDATE 3
During my research I've found that - viewWillAppear is not called the first time when child controller appears. But it's called in all subsequent times.
Maybe the scrollviews contentSize is wider than your scrollView's frame(width specifically in this case) causing scrolling for both directions.
You can either try to decrease the contentSize width to the scrollView's width and
self.scrollView.alwaysBounceHorizontal = NO;
If this doesn't work, the solution would be to disable horizontal scrolling programatically by the help of the UIScrollView delegate
self.scrollView.delegate = self;
[self.scrollView setShowsHorizontalScrollIndicator:NO];
//for the below UIScrollView delegate function to work do the necessary step in the bottom of my answer.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.x > 0)
scrollView.contentOffset = CGPointMake(0, scrollView.contentOffset.y);
}
And in your .h file you should change the interface line to below by adding UIScrollViewDelegate
#interface ViewController : UIViewController <UIScrollViewDelegate>
You most probably know this delegate part very well but for others it might be needed:D
Original answer
Ah, I've finally found the origin of this behaviour. I was almost sure this is happening due to some of the preparation methods are not called properly. As I stated in the update 3 I've found that -viewWillAppear method is not called in TableViewController when my TabBarController is pushed to the navigation stacked. I've found a ton of coverage of this matter on SO, it's a very well known issue apparently.
I've added a simple fix just to check if I'm right in my Custom Segue:
//added this line
[destinationViewController viewWillAppear:YES];
[tabBarViewController.container addSubview:destinationViewController.view];
And it works like a charm, no flickering! Now I have to figure out a more suitable place for this call, since explicit calls to methods like this can break a lot of stuff.
Probably the best place is in -navigationController:willShowViewController:animated: method of UINavigationControllerDelegate.
Anyway, problem solved. Hope it will help someone with the same issue.
UPDATE Actually, I was not completely correct on that. -viewWillAppear is called on my tab bar controller when it's pushed to navigation stack. It's not being translated to TableViewController. So there's no need to access NavigationControllerDelegate. Simple fix to a custom segue is enough.

ABPeoplePickerNavigationController with UITabBarController is not showing correctly in iOS8

I have an application in which UITabBarController as a rootViewController with two controllers. One is an empty controller and the second one is Picker controller extends from ABPeoplePickerNavigationController. The problem is view is going behind the tab bar and due to that view is cutting off from the bottom. I just highlighted the area in the screenshot:
Any help would be highly appreciated.
Officially ABPeoplePickerNavigationController doesn't support subclassing: link here
Subclassing Notes
The ABPeoplePickerNavigationController class does not support subclassing.
However, the problem is that the view of your ABPeoplePickerNavigationController subclass is extending under the tab bar.
You can, for example, change it's size at runtime in this way
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
CGRect rect = self.view.bounds;
rect.size.height = rect.size.height - 40;
self.view.frame = rect;
}
Note, the 40 is only an example, you should calculate the height of your tab bar controller because it can change for other screen dimensions and rotations.
Or, better, you can search for the underlying UITableView instance and set the contentInset property.
EDIT
This seems to have problems with the status bar, since the ABPeoplePickerNavigationController is unable to extend the navigation bar under the status bar if you change it's frame
In both this cases, however, your app will probably be rejected, because you are subclassing a class that explicitly forbids it.
A better and "legal" way to add it is to use a container view controller
Look at This Example
Create a new view controller, add a container view, then add the ABPeoplePickerNavigationController in this way:
-(void)viewDidLoad
{
[super viewDidLoad];
// create the ABPeoplePickerNavigationController instance
ABPeoplePickerNavigationController *controller = [[ABPeoplePickerNavigationController alloc] init];
// add a new child view controller to self
[self addChildViewController:controller];
// alert the child that it has been added to the father
[controller didMoveToParentViewController:self];
// update the child view frame to fit into the containerView
controller.view.frame = self.containerView.bounds;
// translate autoresizing mask into constraints, this is not needed but I usually do because is more pratical
[controller.view setTranslatesAutoresizingMaskIntoConstraints:YES];
// add the `ABPeoplePickerNavigationController` view to the container
[self.containerView addSubview:controller.view];
}
Even in this way ABPeoplePickerNavigationController has problems with the status bar (it doesn't extends correctly under it), so I constrained the container view to the Top Layout Guide and changed the color of the main view of ContainerViewController to fit with the color of the navigation bar
In viewWillAppear
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.topViewController.extendedLayoutIncludesOpaqueBars = YES;
}
In viewDidAppear
- (void)viewDidAppear:(BOOL)animated
{
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
CGRect tabBarFrame = self.tabBarController.tabBar.frame;
self.view.frame = CGRectMake(0, statusBarFrame.size.height, self.view.frame.size.width, CGRectGetMinY(tabBarFrame) - statusBarFrame.size.height);
}
A little late to the party, but here's my answer to the problem or at least some additional input for you to work out a complete solution.
The problem to me seems to be that because UIPeoplePickerNavigationController is not subclass-able or at least is not recommended to be by Apple, you can't use it entirely anyway you like. What I mean is UIPeoplePickerNavigationController is meant to be used as a modal view, which should be presented full screen on iOS and on top of every other view controller. You shouldn't try to use it as a push view controller on a navigation controller stack.
So my suggestion is quite straight-forward. You should simply use your UITabBarController as the receiver of the presentViewController:animated:completion: method. That will take care of presenting the modal on top of the tab bar.
You can access the UITabBarController through the tabBarController property of your current view controller:
ABPeoplePickerNavigationController *peoplePickerController = [ABPeoplePickerNavigationController new];
[peoplePickerController setPeoplePickerDelegate:self];
[self.tabBarController presentViewController:peoplePickerController animated:YES completion:^{}]
Note: I'm writing this from memory so there may be some method naming mistakes.

Best practices for handling changes to the UINavigationItem of child view controllers in a container controller?

Suppose I have a container controller that accepts an array of UIViewControllers and lays them out so the user can swipe left and right to transition between them. This container controller is wrapped inside a navigation controller and is made the root view controller of the application's main window.
Each child controller makes a request to the API and loads a list of items that are displayed in a table view. Based on the items that are displayed a button may be added to the navigation bar that allows the user to act on all the items in the table view.
Because UINavigationController only uses the UINavigationItems of its child view controllers, the container controller needs to update its UINavigationItem to be in sync with the UINavigationItem of its children.
There appear to be two scenarios that the container controller needs to handle:
The selected view controller of the container controller changes and therefore the UINavigationItem of the container controller should update itself to mimic the UINavigationItem of the selected view controller.
A child controller updates its UINavigationItem and the container controller must be made aware of the change and update its UINavigationItem to match.
The best solutions I've come up with are:
In the setSelectedViewController: method query the navigation item of the selected view controller and update the leftBarButtonItems, rightBarButtonItems and title properties of the container controller's UINavigationItem to be the same as the selected view controller's UINavigationItem.
In the setSelectedViewController method KVO onto the leftBarButtonItems, rightBarButtonItems and title property of the selected view controller's UINavigationItem and whenever one of those properties changes up the container controller's UINavigationItem.
This is a recurring problem with many of the container controllers that I have written and I can't seem to find any documented solutions to these problems.
What are some solutions people have found to this problem?
So the solution that I have currently implemented is to create a category on UIViewController with methods that allow you to set the right bar buttons of that controller's navigation item and then that controller posts a notification letting anyone who cares know that the right bar button items have been changed.
In my container controller I listen for this notification from the currently selected view controller and update the container controller's navigation item accordingly.
In my scenario the container controller overrides the method in the category so that it can keep a local copy of the right bar button items that have been assigned to it and if any notifications are raised it concatenates its right bar button items with its child's and then sends up a notification just incase it is also inside a container controller.
Here is the code that I am using.
UIViewController+ContainerNavigationItem.h
#import <UIKit/UIKit.h>
extern NSString *const UIViewControllerRightBarButtonItemsChangedNotification;
#interface UIViewController (ContainerNavigationItem)
- (void)setRightBarButtonItems:(NSArray *)rightBarButtonItems;
- (void)setRightBarButtonItem:(UIBarButtonItem *)rightBarButtonItem;
#end
UIViewController+ContainerNavigationItem.m
#import "UIViewController+ContainerNavigationItem.h"
NSString *const UIViewControllerRightBarButtonItemsChangedNotification = #"UIViewControllerRightBarButtonItemsChangedNotification";
#implementation UIViewController (ContainerNavigationItem)
- (void)setRightBarButtonItems:(NSArray *)rightBarButtonItems
{
[[self navigationItem] setRightBarButtonItems:rightBarButtonItems];
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:UIViewControllerRightBarButtonItemsChangedNotification object:self];
}
- (void)setRightBarButtonItem:(UIBarButtonItem *)rightBarButtonItem
{
if(rightBarButtonItem != nil)
[self setRightBarButtonItems:#[ rightBarButtonItem ]];
else
[self setRightBarButtonItems:nil];
}
#end
ContainerController.m
- (void)setRightBarButtonItems:(NSArray *)rightBarButtonItems
{
_rightBarButtonItems = rightBarButtonItems;
[super setRightBarButtonItems:_rightBarButtonItems];
}
- (void)setSelectedViewController:(UIViewController *)selectedViewController
{
if(_selectedViewController != selectedViewController)
{
if(_selectedViewController != nil)
{
// Stop listening for right bar button item changed notification on the view controller.
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self name:UIViewControllerRightBarButtonItemsChangedNotification object:_selectedViewController];
}
_selectedViewController = selectedViewController;
if(_selectedViewController != nil)
{
// Listen for right bar button item changed notification on the view controller.
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:#selector(_childRightBarButtonItemsChanged) name:UIViewControllerRightBarButtonItemsChangedNotification object:_selectedViewController];
}
}
}
- (void)_childRightBarButtonItemsChanged
{
NSArray *childRightBarButtonItems = [[_selectedViewController navigationItem] rightBarButtonItems];
NSMutableArray *rightBarButtonItems = [NSMutableArray arrayWithArray:_rightBarButtonItems];
[rightBarButtonItems addObjectsFromArray:childRightBarButtonItems];
[super setRightBarButtonItems:rightBarButtonItems];
}
I know this question is old, but I think that I found the solution for this problem!
The navigationItem property of a UIViewController is defined in a category/extension in the UINavigationController header file.
This property is defined as:
open var navigationItem: UINavigationItem { get }
So, as I just found out, you can override the property in the container view controller, in my case:
public override var navigationItem: UINavigationItem {
return child?.navigationItem ?? super.navigationItem
}
I tried this approach and it's working for me. All buttons, title and views are being shown and updated as they change on the contained view controller.
The accepted answer works, but it breaks the contract on UIViewController, your child controllers are now tightly coupled with your custom category and must use its alternative methods in order to work correctly...
I had this issue using the RBStoryboardLink container, and also on a custom tab bar controller of my own, so it was important it would be encapsulated outside of a given container class, so I created a class that has a mirrorVC property (usually set to the container, the one who will listen for notifications) and a few register / unregister methods (for navigationItems, toolbarItems, tabBarItems, as your needs see fit).
For example when registering/unregistering for toolbarItems :
static void *myContext = &myContext;
-(void)registerForToolbarItems:(UIViewController*)viewController {
[viewController addObserver:self forKeyPath:#"toolbarItems" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:myContext];
}
-(void)unregisterForToolbarItems:(UIViewController*)viewController {
[viewController removeObserver:self forKeyPath:#"toolbarItems" context:myContext];
}
The observe action will handle receiving the new values and forwarding them to the mirrorVC:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if(context == myContext) {
id newKey = [change objectForKey:NSKeyValueChangeNewKey];
id oldKey = [change objectForKey:NSKeyValueChangeOldKey];
//no need to mirror if the value is the same
if ([newKey isEqual:oldKey]) return;
//nil values comes packaged in NSNull
if (newKey == [NSNull null]) newKey = nil;
//handle each of the possibly registered mirrored properties...
if ([keyPath isEqualToString:#"navigationItem.leftBarButtonItem"]) {
self.mirrorVC.navigationItem.leftBarButtonItem = newKey;
}
//...
//as many more properties as you need forwarded...
else if ([keyPath isEqualToString:#"toolbarItems"]) {
[self.mirrorVC setToolbarItems:newKey animated:YES];
}
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
Then in your container, at the right moments, you register and unregister
[_selectedViewController unregister...]
_selectedViewController = selectedViewController;
[_selectedViewController register...]
You must be aware of a potential pitfall though: not all desirable properties are KVO compliant, and the ones that do aren't documented to be - so they can stop being or misbehave at any time.
The toolbarItems property, for example, is not. I created a UIViewController category based on this gist ( https://gist.github.com/brentdax/5938102 ) that enables KVO notifications for it so it works in this scenario. Note: the gist above wasn't necessary for UINavigationItem, iOS 5~7 sends out proper KVO notifications for it, with that category I would get double notifications for UINavigationItems. It worked flawlessly for toolbarItems!
Have you considered NOT wrapping your container view controller in a UINavigationController and just adding a UINavigationBar to your view? Then you can push your child view controller's navigation items directly to that navigation bar. Essentially your container view controller would replace a normal UIViewController.
I know this is an old thread, but I just ran into this issue and thought someone else might as well.
So for future reference, I did it as follows: I sent a block to the child view controller, which just sets the parent's UINavigationItem's right button. Then I created a UIBarButtonItem as normal in the child view controller, calling some method in that same controller.
So, in ChildViewController.h:
// Declare block property
#property (nonatomic, copy) void (^setRightBarButtonBlock)(UIBarButtonItem*);
And in ChildViewController.m:
self.myBarButton = [[UIBarButtonItem alloc]
initWithTitle:#"My Title"
style:UIBarButtonItemStylePlain
target:self
action:#selector(didPressMyBarButton:)];
...
// Show bar button in navigation bar
// As normal, just call it with 'nil' to hide the button
if (self.setRightBarButtonBlock) {
self.setRightBarButtonBlock(self.myBarButton);
}
...
- (void)didPressMyBarButton:(UIBarButtonItem *)sender {
// Do something here
}
And finally in ParentViewController.m
// Initialise child view controller
ChildViewController *child = [[ChildViewController alloc] init];
// Give it block for changing bar button item
__weak typeof(self) weakSelf = self;
child.setRightBarButtonBlock = ^void(UIBarButtonItem *barButtonItem) {
[weakSelf.navigationItem setRightBarButtonItem:barButtonItem animated:YES];
};
// Finish the parent-child VC dance
That's it. This feels good to me because it keeps the logic pertaining to the UIBarButtonItem in the view controller which is actually interested in it.
Note: I should mention that I am not a pro. This may just be a terrible way to do it. But it seems to work just fine.

How to customize iRate classes for "Rate this app" ios

I don't want to use the uialertview for the popup, that gives the user a chance to rate the app on ios. I want to use a customized popup, but this is not showing up. Besides using the iRate classes from the internet, I also create a xib, that contains the popup, that I want to appear and I changed in .h from :NSObject to :UIViewController. I commented all the code for the uialertview and in the method promptForRating, that will be triggered, I make the uiview from the xib visible, but apparently the uiview is nil.
- (void)promptForRating
{
rateView.hidden = NO;
}
Does anybody have a suggestion about making this popup show up?
I think I get it now.. if you want to display a view, it has to be embedded in a view controller. Or in another view that is already embedded.
What you can do is
Access the sharedApplication of the UIApplication
Get all the UIWindows of that UIApplication (in reversed order, because your myView should be on top)
Select the UIWindow that is the default
Add your myView as a subview of the UIWindow
At least this is what SVProgressHUD is doing.
Here is some sample code
if(!myView.superview){
NSEnumerator *frontToBackWindows = [[[UIApplication sharedApplication]windows]reverseObjectEnumerator];
for (UIWindow *window in frontToBackWindows)
if (window.windowLevel == UIWindowLevelNormal) {
[window addSubview:myView];
break;
}
}
The first line is to ensure that your view is not visible atm (maybe unnecessary in your context). To dismiss the view, remove it from its superview.

shouldAutorotate not being called

I am trying to define supported orientations depending on where the user is in my app, I am having a very difficult time doing so.
I have found out thus far that I should use the supportedInterfaceOrientationsForWindow: and shouldAutorotate methods that are now supported in iOS6, however neither method is ever called where I am defining them in my UIViewController.
This is what my code looks like
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientationsForWindow {
return UIInterfaceOrientationMaskPortrait;
}
In my Target Summary Supported Orientatoin I have de-selected all options.. thinking I would just define supported orientation in each of m ViewControllers... I would like to know if this is the correct thing to do?
Now I have read what I am trying to do is dependant on the structue of my app, so Here I will outline my app.
main UIViewController (3 buttons taking you to (3 different navigationControllerViews) Wrong! only one navigationController... sorry its been a long time since I looked at this code.)
secondary UIViewController (holds navigation controller)
other UIViewControllers (appear in secondarys NavigationController)
I would like every ViewController up untill the last one in the NavigationController stack to appear in portrate. The last view in the NavigationController is a special view that needs to be able to rotate its orientation to left or right if needed.
I would like to know if this is possible and if so why isnt the code that I have above working/being called.
Any help would be greatly appreciated.
// Update to question Re:
RootView loads with (three buttons, here is the Method that is called when a button is selected to load the View containing the navigation controller)
- (IBAction)buttonClick: (UIButton *) sender
{
//..
// v ----->
if ([sender isEqual:vUIButton]) {
VSearchViewController *vSearchViewController = [[VSearchViewController alloc] initWithNibName:#"VSearchViewController" bundle:nil];
[self.navigationController pushViewController:vehicalSearchViewController animated:YES];
}
//..
}
Then inside VSearchViewController I load the new views onto the UINavigation stack like this
//..
FModelsViewController *fModelsViewController = [[FModelsViewController alloc] initWithNibName:#"FModelsViewController" bundle:nil];
// Sets the back button for the new view that loads (this overrides the usual parentview name with "Back")
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back" style: UIBarButtonItemStyleBordered target:nil action:nil];
[self.navigationController pushViewController:fModelsViewController animated:YES];
//..
So in review I have set up the navigation controller in the appDelegate and all views in my app are on the navigationStack... I was wrong in saying there are 3 NavigationControllers.. there is only one and every view is added to the stack.. Sorry about that.. Its been a year and a half since I looked at this code..
Are you running the above code on iOS6? Those methods will only be called on iOS6.
Also maybe you could post some code to better illustrate how you are transitioning to these viewControllers so we can get a better understanding of the view hierarchy.
You might want to look at UIViewController's addChildViewController: method.
I think your last view could take advantage of the code written below. It senses the orientation of the device and will show a different view controller for the landscape view (I'm assuming that is what you are trying to do). This means your last view will have a portrait and a landscape option.
#implementation LastViewController
- (void)awakeFromNib
{
isShowingLandscapeView = NO;
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(orientationLastViewChanged:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
}
- (void)orientationLastViewChanged:(NSNotification *)notification
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) &&
!isShowingLandscapeView)
{
[self performSegueWithIdentifier:#"LandscapeLastView" sender:self];
isShowingLandscapeView = YES;
}
else if (UIDeviceOrientationIsPortrait(deviceOrientation) &&
isShowingLandscapeView)
{
[self dismissViewControllerAnimated:YES completion:nil];
isShowingLandscapeView = NO;
}
}
on the view controllers leading up to this view that you want locked into portrait, write this code:
- (BOOL)shouldAutorotate {
return NO;
}
as for your supported interface orientations, leave that how you have it.

Resources