UIViewController auto rotate not working - iphone

I have found quite a lot on this subject but I just can't figure it out. Any help would be massively appreciated!
I have an app set up with a UITabBarController. Within one of the tabs, I am showing a UITableView which is using a UINavigationController to allow for hierarchy. All the tables rotate just fine when the orientation is changed, until I get to what is effectively the final view in the hierarchy.
The final view is not a UITableView, just a basic UIView. But I can not get this page to rotate successfully! I have remade the view from with the absolute basics required and it still doesn't want to work! The code is below but it is currently pretty much a standard template with nothing in it now.
Also, I am not using InterfaceBuilder, and shouldAutorotateToInterfaceOrientation is on all views. This is the only one I am having problems with.
SomeView.h
#import <UIKit/UIKit.h>
#interface SomeView : UIViewController
{
NSString *someID;
NSString *someName;
}
#property(nonatomic,retain) NSString *someID;
#property(nonatomic,retain) NSString *someName;
#end
SomeView.m
#import "SomeView.h"
#import "AppDelegate.h"
#implementation SomeView
#synthesize someID, someName;
-(void)loadView
{
}
-(void)viewDidLoad
{
[super viewDidLoad];
}
-(void)viewDidUnload
{
[super viewDidUnload];
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return YES;
}
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
NSLog(#"willAnimateRotationToInterfaceOrientation");
}
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
-(void)dealloc
{
[super dealloc];
}
#end
UPDATE 10th Nov 2011
I'm still having this issue, however looking through documents and bits this seems to be my problem (http://developer.apple.com/library/ios/#qa/qa1688/_index.html)
The view controller's UIView property is embedded inside UIWindow but alongside an additional view controller.
You may find a situation where shouldAutorotateToInterfaceOrientation is called once at startup for a given view controller but is never called again when the device is rotated. Because view controllers are tightly bound to the views they manage, they are also part of the responder chain used to handle events. View controllers are themselves descendants of the UIResponder class and are inserted into the responder chain between the managed view and its superview. So it is common practice to have one primary view controller in your application as part of the responder chain. You would typically add one primary view controller such as a UINavigationController, UITabBarController or a generic UIViewController to your UIWindow. For example, this is done by calling:
[myWindow addSubview:primaryViewController.view];
If you add an additional view controller's UIView property to UIWindow (at the same level as your primary view controller) via the following:
[myWindow addSubview:anotherController.view];
this additional view controller will not receive rotation events and will never rotate. Only the first view controller added to UIWindow will rotate.

My UITabBarController stopped to autorotate, when I added a new navigationController to it with a tableViewController and didn't notice, that my custom navigation controller's shouldAutorotateToInterfaceOrientation returns YES only for one orientation. The solution is to check shouldAutorotateToInterfaceOrientation function in each Controller inside TabBarController.
May be it will help to somebody.

And I have figured it out...
I was looking at the code which pushes the UIViewController onto the stack and I had not fully initied the UIViewController.

Related

UINavigationItem lifecycle

As far as I understand the SDK documentation UIViewController's navigationItem lifecycle is bound to the controller itself and not to the controller's view. I.e. in the default implementation it is created on-demand and destroyed with the view controller - with all contents like button items and titleView. Given that both button items and the titleView may be represented by UIView instances - does that mean that once created these views will stay in memory until controller is destroyed and live through all memory warnings?
What is the sense behind this design decision? Is impact for memory usage considered too small to bother? Is it really small for an application which is using customized nav bar buttons/titles everywhere?
It is easy to explicitly bound some of the navigationItem properties to the controller's view lifecycle - like setting titleView in -viewDidLoad and dropping it in -viewDidUnload (self.navigationItem.titleView = nil). But the navigationItem property documentation suggests to avoid this pattern. Are there any other potential problems other than the given example with back button?
Added a category (snippet2) to track the retain count and the destruction of the navigation items, feel free to do the same :) Seems like it is not deallocated with the memory warning. An explanation would come from a common sense that view controllers don't have to be used with the navigation controller: that should be why the nav-item is added with a separate category (snippet1) and it's lifecycle must be managed with a nav-controller, not the view controller instance itself.
In the case the custom nav-items are so heavy that you need to release it whenever possible,
i would leave the default implementation, add custom nav-items category and manage this items manually as i wish (again through overriding required UINavigationController methods like nav-controllers didReceiveMemoryWarning, pushViewController:animated:, popViewControllerAnimated:animated:). I can't imagine such a case when it is really needed however.
snippet 1
#interface UIViewController (UINavigationControllerItem)
#property(nonatomic,readonly,retain) UINavigationItem *navigationItem; // Created on-demand so that a view controller may customize its navigation appearance.
#property(nonatomic) BOOL hidesBottomBarWhenPushed; // If YES, then when this view controller is pushed into a controller hierarchy with a bottom bar (like a tab bar), the bottom bar will slide out. Default is NO.
#property(nonatomic,readonly,retain) UINavigationController *navigationController; // If this view controller has been pushed onto a navigation controller, return it.
#end
snippet 2
#implementation UINavigationItem (Logs)
- (id)init
{
NSLog(#"I'm initialized (%#)", [self description]);
self = [super init];
return self;
}
-(void) release
{
NSLog(#"I'm released [%d](%#)", [self retainCount], [self description]);
[super release];
}
-(void) dealloc
{
NSLog(#"I'm deallocated [%d](%#)", [self retainCount], [self description]);
[super dealloc];
}
#end

Pushing a UIViewController from a UIView

I need to push a UIView into my UINavigation controller. I am doing it by
[self.view addSubview:showContactFlow];
And on a button click in UIView I need to push another UIViewController over the UIView. From the UIView I am not able to access self.navigationcontroller How can I do this?
Edit:
I have set the UIView as the view of a new UIViewController I am pushing into, the before mentioned UIViewController . Now I would like to know, how to handle the UIView button event inside its UIViewController, in which's view it is set.
Add a UINavigationController ivar to the UIView and assign it to the main view controller's. Then you should be able to access it from the UIView.
Edit:
Your UIView subclass:
// CustomView.h
#interface CustomView: UIView {
// ...
// your variables
// ...
UINavigationController *navController;
}
#property (nonatomic, assign) UINavigationController *navController; // assign, because this class is not the owner of the controller
// custom methods
#end
// CustomView.m
#implementation Customview
// synthesize other properties
#synthesize navController;
// implementation of custom methods
// don't release the navigation controller in the dealloc method, your class doesn't own it
#end
Then before the [self.view addSubview:showContactFlow]; line just add [showContactFlow setNavController:[self navigationController]]; and then you should be able to access your hierarchy's navigation controller from your UIView and use it to push other UIViewControllers.
You should try to work with an MVC approach. So your controller has access to all that stuff and can keep pushing and popping views, so the view doesn't need to know too much about the controller.
Otherwise, and for this case you can solve it fast by using delegation. So:
showContactFlow.delegate = self;
[self.view addSubview:showContactFlow];
So later in the UIView, you can just say:
[self.delegate addSubview:self];
This is gonna work, but it's not likely to be the best approach you should use.
On button click, you can present a view controller like,
-(void)buttonFunction{
ThirdVC *third= [[ThirdVC alloc]initWithNibNme];......
[self presentViewController:third animated:NO];
}
Using Core animation you can make NavigationController's pushviewController like animation on writing code in ThirdVC's viewWillAppear: method.
where do you add the UIButton is it in showContactFlow view or in the ViewController's view??
In regard to the modalViewControllers issue the correct method is
[self presentModalViewController:viewController animated:YES];
the standard animation in upwards

Lifecycle methods of UIViewController subclass in renamed storyboard are never called

I'm not instantiating my controls/views manually. I've set up a storyboard like this:
-> UITabBarController-> UINavigationController-> HomeViewController
I switched the controller class of the last to my own HomeViewController.
Here's the code:
#interface HomeViewController : UIViewController {
UIView *buttonView;
UITableView *buttonTableView;
}
#property (nonatomic, retain) IBOutlet UIView *buttonView;
#implementation HomeViewController
#synthesize buttonView;
Inside the main view for HomeViewController there's a subview (UIView) that is hooked up to the outlet by ctrl-dragging one of those lines.
But I can't for my life find a method on UIViewController to override that will be invoked somewhere in the life cycle. So far I've tried putting my stuff in
// didn't expect it to work
-(id)init
// didn't expect it to work
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
// had hopes for this one, but no such luck
-(id)initWithCoder:(NSCoder *)aDecoder {
// this one looked promising...
-(id)awakeAfterUsingCoder:(NSCoder *)aDecoder {
// not much hope
-(void)awakeFromNib {
// my main candidate for a long while...
-(void)viewDidLoad {
When I run the interface loads perfectly, I get my tabs, my navigation bar, my view. But no matter how much NSLog() I do in HomeViewController there's never any output in the console.
Am I missing something about the lifecycle of the storyboard? Is my controller not instantiated or used?
viewDidLoad is called with a storyboard, something is wrong with your storyboard setup. Sounds like it is loading a standard UIViewController rather than your HomeViewController subclass. Make sure the HomeViewController class setup in the storyboard.
The problem was that I recently renamed the storyboard and didn't realise Xcode does not rename the Main Storyboard setting. I've cleaned the project numerous times and still it seems the old storyboard is present and loaded.

Xcode Error: Incompatible Objective-C types. Expected 'struct UIView'

I am starting a multiview app with two views: NewGame and Players. I thought I was setting everything up properly, but apparently not.
MainViewController.h
#import <UIKit/UIKit.h>
#class NewGame; #class Players;
#interface MainViewController : UIViewController {
IBOutlet NewGame *newGameController;
IBOutlet Players *playersController;
}
-(IBAction) loadNewGame:(id)sender;
-(IBAction) loadPlayers:(id)sender;
-(void) clearView;
#end
MainViewController.m
#import "MainViewController.h"
#import "NewGame.h"
#import "Players.h"
#implementation MainViewController
-(IBAction) loadNewGame:(id)sender {
[self clearView];
[self.view insertSubview:newGameController atIndex:0];
}
-(IBAction) loadPlayers:(id)sender {
[self clearView];
[self.view insertSubview:playersController atIndex:0];
}
-(void) clearView {
if (newGameController.view.superview) {
[newGameController.view removeFromSuperview];
} else if (playersController.view.superview) {
[playersController.view removeFromSuperview];
}
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[self loadNewGame:nil];
[super viewDidLoad];
}
A couple of images...
http://i.stack.imgur.com/GwXMa.png
http://i.stack.imgur.com/XHktH.png
Views are objects that represent what appears on screen. View controllers are objects that perform application logic relating to those views. A view hierarchy is a collection of views. You are attempting to add a view controller to a view hierarchy as if it were actually a view.
Roughly speaking, you should have one view controller for every "screen" of your app. This view controller can manage any number of views. Its main view is accessible through its view property.
A quick fix to get your application operational would be to add the main view of your view controllers instead of the view controllers themselves. So, for example, this:
[self.view insertSubview:playersController atIndex:0];
...would become this:
[self.view insertSubview:playersController.view atIndex:0];
Having said that, this is not a good solution long-term, and you should investigate a more structured way of organising transitions from view controller to view controller. UINavigationController is a good option for beginners.
I suppose playerController is view controller. Then add
[self.view addSubview: playerController.view];
OR if not then subclass them for UIView
NewGame and Players both need to subclass UIView. If they're ViewControllers, not UIViews, you'll need to use newGameController.view instead of newGameController.

Initialization for ViewController under NavController in TabBarController

I have the relatively common setup of a TabBarController whose tabs contain NavigationControllers which have TableViewControllers as their roots. I'm trying to perform some logic on initialization of one of these TableViewControllers but can't seem to find what init function gets called.
My goal is to add a listener in the TableViewController (that I have subclassed) which can respond to events by updating the navigationController.tabBarItem.badgeVluew property.
I've tried putting code into initWithStyle: as well as init but neither of them end up getting called. I've also tried putting it in viewDidLoad, but that only gets called once the controller actually appears (I need to have it happen as soon as the controller is loaded / as soon as the tab bar item shows up).
Does anyone know where I would put this code for it to happen on initialization of the controller?
Also, this is all set up through interface builder / NIBs. I'm not adding the nav controller or tableviewcontroller manually, which is why it's not clear what init function I need to override.
If you select one of your UITabBarItems in IB, you will see 'View loaded from "YourView"'. Click into this "gray" View. In the Inspector window you will see in the Attributes Tab (the tab on the left) the title and the NIB name which will be loaded (lets call it "YourNibName").
Now select the right tab of the inspector (Identity) and change the Classname (Combo next to Class) to your "YourViewController" class, which you must create in xcode. Don't use the standard ViewController, which is already selected. The InterfaceBuilder loads your nib and attaches it to your ViewController.
Open YourNibName and change FilesOwner's Class (Inspector, right Tab) to "YourViewController", too.
Your TabBar's NIB contains a FilesOwner, too. Create a ViewController for this FilesOwner and set its Class to this Controller (i.e. TabBarController)
In "TabBarController" you can find out which Tab was selected by using this code:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
if ([viewController.nibName isEqualToString:#"NIBName1"]){
// Do something here, if you like. (i.e. Save the state in a string or int)
}
if ([viewController.nibName isEqualToString:#"NIBNAme2"]){
// Do something here, if you like. (i.e. Save the state in a string or int)
}
...
}
Here you can do something "global" or preinitialize something. This is ONE thing you can do.
INIT OF YOUR VIEWS:
If you select a Tab and the view (which is handled by YourViewController) will be shown for the first time, "viewDidLoad" will be called in "YourViewController"
- (void)viewDidLoad {
// Here you can add views programatically
[self.view addSubview:myNavigationController.view];
[self.view bringSubviewToFront:myNavigationController.view];
// And if you like, do some INIT here
[super viewDidLoad];
}
I hope this is what your question was about.
Now something about the badge. It's a hack, but works fine for me.
Header file:
Add an outlet to your controller, which is representing your TabBarController:
#interface yourController : UIViewController <UITabBarControllerDelegate> {
UITabBarController *tabBarController;
}
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
#end
Connect this outlet in IB with your TabBar.
Implementation:
In your TabBarControllerClass you can overwrite 'initWithNibName':
#synthesize tabBarController;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Do some init here
// select your desired item (it will be loaded)
// then you can assign the badge
tabBarController.selectedIndex = 1;
tabBarController.selectedViewController.tabBarItem.badgeValue = #"222";
// and select the item you will start with
tabBarController.selectedIndex = 0;
// if you like you can add a notification, which you can activate from anywhere else
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(itemBadgeChanged:)
name:#"itemBadgeChangedNotification"
object:nil];
}
return self;
}
if you don't use nib, use '- (void)loadView { ... }' instead.
You are using a subclass of the TabBar controller, maybe you can use 'self.selectedIndex = 1;' instead of 'tabBarController.selectedIndex = 1;', and so on. Just try this out
Hope this helps!