I bumped into a problem where UIViewController.navigationController becomes nil and I'm desperately trying to find an answer to this one.
The UINavigationController gets setup in the application delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.rootViewController = [[RootViewController alloc] initWithNibName:#"RootView" bundle:nil];
UINavigationController* navigationController = [[UINavigationController alloc] initWithRootViewController:self.rootViewController];
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
return YES;
}
When the RootViewController is appearing, the self.navigationController member is set and I can use it to hide the navigation bar, like so:
- (void)viewWillAppear:(BOOL)animated {
NSLog( #"self = %#, self.navigationController = %#", self, self.navigationController );
[self.navigationController setNavigationBarHidden:YES animated:NO];
}
The debug output shows values for self and self.navigationController.
When a button is clicked in this controller, self remains the same value indeed but self.navigationController is now nil:
- (IBAction)buttonClicked:(id)sender {
NSLog( #"self = %#, self.navigationController = %#", self, self.navigationController );
// here, self.navigationController is nil, so
// [self.navigationController pushViewController:...] doesn't work :-(
}
I've seen dozens of questions regarding this problem and the answer is always that the UIViewController is not part of a UINavigationController. Since accessing the navigationController in viewWillAppear works fine, I believe something else must be going on. Do you have any pointers? I'll happily provide more detail if necessary.
Try this in the app delegate:
[(UINavigationController *)self.window.rootViewController pushViewController:yourViewController animated:NO];
the rootviewcontroller is actually UINavigationController if you po to debug it. This works for me.
Your code shows that you are only using the navigationController's view but just pray that navigationController life is handled by some magic hand which is not the case.
You need someone to be the explicit owner of the navigationController here.
In fact, the following line:
[self.window addSubview:navigationController.view];
seems to indicate that what you want is for the window's rootViewController to be navigationController:
self.window.rootViewController = navigationController;
But also, it seems that the application's delegate is to be an owner of navigationController as well so navigationController should, in fact, be an ivar of your app's delegate.
In short, fix your object graph (and it will coincidentally do the extra retain you manually did and fix your bug)
I had a problem with a nil view controller and found that it was not connected properly in storyboard to the app delegate.
As always, it helps to formulate the question just to find the solution some minutes later.
I fell prey to ARC, I guess. Once I retained the UINavigationController in the application delegate, it worked fine.
Related
I'm sorry if this has already been answered. I'm a bit of a noob. From all the examples I've found I can't seem to work this out.
I'm building a mac osx application using Xcode 5. My interface that I've created in Interface builder has three tableviews. My data to populate these views is json format and it is being received successfully using NSURLConnection.
What is the best way to popular the tableviews? A lot of the tutorials I've followed use an arraycontroller that is dragged and dropped using IB. From what I understand I don't need an array controller and need to hand code controllers for each of tableviews. Is that correct? If so, please explain how I go about this and setup the bindings.
Also, when clicking on a column in tableview 1 I'm planning on making it adjust the data in tableview 2 & 3 so if that impacts anything please let me know.
Thanks in advance!
Update 6 Jan
Here is my code currently..
ContactsTableViewController.h
#import <Foundation/Foundation.h>
#interface ContactsTableViewController : NSObject <NSTableViewDataSource, NSTableViewDelegate> {
IBOutlet NSTableView *contactsTableView;
}
-(id) init;
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView;
-(NSView *) tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row;
#end
ContactsTableViewController.m
#import "ContactsTableViewController.h"
#implementation ContactsTableViewController
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
NSLog(#"numberOfRowsInTableView called");
return 1;
}
- (id) init {
NSLog(#"init called");
[contactsTableView setDelegate:self];
[contactsTableView setDataSource:self];
[contactsTableView reloadData];
return self;
}
- (void) initme {
NSLog(#"customfunc");
}
-(NSView *) tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSLog(#"viewForTableColumn called");
NSTextField *result = [tableView makeViewWithIdentifier:#"name" owner:self];// result is now guaranteed to be valid,
result.stringValue = #"My Name"; //[self.nameArray objectAtIndex:row];
return result;
}
#end
In my appDelegate:
- (void) awakeFromNib {
NSLog(#"AWAKE");
ContactsTableViewController *contactsController = [[ContactsTableViewController alloc] init];
}
Currently nothing is showing in the tableview and numberOfRowsInTableView & viewForTableColumn aren't getting called.
Look at the UITableViewDataSource, you need to implement the protocol in one of your classes, probable your view controller, implement the UITableViewDelegate to get row selection events in the same class.
I have a Tabbed Based application and I want to use swipe gestures to navigate through the view controllers.
I tried:
- (IBAction)swipeLeftDetected:(UIGestureRecognizer *)sender {
UISecondViewController *second =[[UISecondViewController alloc] initWithNibName:#"UISecondViewController" bundle:nil];
second.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:second animated:YES];
}
paired with this:
- (void)viewDidLoad; {
UISwipeGestureRecognizer *swipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeLeftDetected:)];
swipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeRecognizer];
}
But it crashes on the swipe. Any help? Thanks!
Your code works well on my Xcode.
Only changed:
UISecondViewController *second =[[UISecondViewController alloc] initWithNibName:#"UISecondViewController" bundle:nil];
to:
SecondViewController *second =[[SecondViewController alloc] init];
because I don't have the UISecondViewController class.
Just verify there is UISecondViewController.xib in your project, otherwise it will crash.
But I'm not sure here is your problem.
I recently tried to authenticate the local user on iOS 6 with the new iOS 6 method, and it returns in the authenticate handler-
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error)
How do I display this view controller? (FYI- GC automatically dismisses the window when completed authenticating the local player)
I'm using storyboard in the project.
Thanks!
Once you set the handler, it will be called at different situations. In those calls, the viewController parameter can be nil or it can actually be an instance GKHostedAuthenticateViewController.
You need to check if the viewController is not nil, and in that case, you can display it as you would do with any other view controller.
Here you have some sample code from the Game Center Programming guide
- (void) authenticateLocalPlayer
{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error){
if (viewController != nil)
{
[self showAuthenticationDialogWhenReasonable: viewController
}
else if (localPlayer.isAuthenticated)
{
[self authenticatedPlayer: localPlayer];
}
else
{
[self disableGameCenter];
}
}];
}
Check out the GKLocalPlayer Class Reference and the Game Center Programming Guide
I'm working with Apple's Accelerometer Graph Example:
http://developer.apple.com/library/ios/#samplecode/AccelerometerGraph/Introduction/Intro.html
I'm pushing 2 Graph Views onto a navigation controller:
GraphViewController* graphViewController = [[GraphViewController alloc]initWithNibName:#"GraphViewController" bundle:nil];
[self.navigationController pushViewController:graphViewController animated:YES];
[graphViewController release];
The graph's are updated by an external method:
[motionManager startDeviceMotionUpdatesToQueue:motionQueue withHandler:^(CMDeviceMotion *motion, NSError *error) {
...
if(graphDelegate)
{
[self performSelectorInBackground:#selector(notifyGraphDelegateWithMotionEvent:) withObject:motion];
}
}
, which calls
[unfiltered addX:filteredValue y:unfilteredvalue z:10];
for each graph. The frequency of updates is 20 times per second
When I pop the view from the navigation controller, I get EXC_BAD_ACCESS after [super dealloc]
-(void)dealloc
{
// Since 'text' and 'current' are weak references, we do not release them here.
// [super dealloc] will take care to release 'text' as a subview, and releasing 'segments' will release 'current'.
[segments release];
[super dealloc];
}
This is a nasty error, and I really don't know how to troubleshoot something like that. It seems to be something about the order in which the views are de-allocated, as the crash happens after the view is popped. Any ideas on how to troubleshoot something like that?
Set NSZombieEnabled, MallocStackLogging, and guard malloc in the debugger. Then, when your App crashes, type this in the gdb console:
(gdb) info malloc-history 0x543216
Replace 0x543216 with the address of the object that caused the crash, and you will get a much more useful stack trace and it should help you pinpoint the exact line in your code that is causing the problem.
See this article for more detailed instructions.
I'm working with ShareKit, an open-source sharing foundation for iOS apps. There is a known bug that prevents the Kit from detecting what your root view controller is. The fix for that bug is adding [SHK setRootViewController:myViewController]; in the app delegate.
If the fix is in the UIApplication didFinishLaunching method, wouldn't the view controller just be self? What am I missing? I've also tried self.viewController, self.window.rootViewController and self.window to no avail.
EDIT: Here's the entire didFinishLoading:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[Chatter_BoxViewController alloc] initWithNibName:#"Chatter_BoxViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
[SHK setRootViewController:self.viewController];
return YES;
}
If it would only be "self" in the didFinishLaunching it would refer to the UIApplication, don't you agree? Are you initing correctly the viewController? Post some more code. :)
Comment to your Edit:
If you have your Window set normally in your XIB you don't need this:
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
Also, if you do this (presuming your viewController is retained as property):
self.viewController = [[Chatter_BoxViewController alloc] initWithNibName:#"Chatter_BoxViewController" bundle:nil];
You will have a leak. Just do this:
viewController = [[Chatter_BoxViewController alloc] initWithNibName:#"Chatter_BoxViewController" bundle:nil];