How do I transition to a different view controller programmatically? - ios

Hey guys I'm trying to get my app to load a new view when a certain method is called. Inside the method I have the code:
ViewController *GameOverViewController = [[ViewController alloc] init];
[self presentViewController:GameOverViewController animated:YES completion:nil];
which is taken straight from How to switch views programmatically in a ViewController? (XCode iPhone).
Anyways, when I try to switch from my view controller called Game to a view controller called GameOverViewController I just get a ton of errors. Mainly
"Unknown receiver 'ViewController'; did you mean 'UIViewController'
And my app crashes. I'm obviously doing something wrong but I have no idea what that is exactly. Do I have to declare the GameOverViewController in my Game.h or in my appDelegate or something?
EDIT: Both view controllers are in the same main.Storyboard file if that matters

The unknown receiver message means that it can't find the class definition for the view controller class called ViewController. Is that really the name of the class you're using for your "game over" view controller? And if so, have you done the #import "ViewController.h" at the start of this .m file?
The fundamental problem is that it cannot find the class called ViewController.
Setting that aside, we don't generally instantiate new view controllers using alloc and init anymore, as that answer may have implied. That was a technique used with NIBs (and only worked if the NIB name matched the class name).
For new developers, I might encourage you to start with storyboards. Any modern tutorial should walk you through how to use storyboards. (Google "iOS storyboard tutorial" and you'll probably get lots of hits.)
If, for example, your storyboard has a scene with a "storyboard identifier" of GameOverViewController, then you might programmatically instantiate it and present it with something like:
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"GameOverViewController"];
[self presentViewController:controller animated:YES completion:nil];
Or, if your storyboard had a segue from the current scene to the next scene, you'd make sure that segue had its own storyboard identifier, e.g. GameOverSegue, and then you'd perform it like so:
[self performSegueWithIdentifier:#"GameOverSegue" sender:self];
But find yourself a good introduction/tutorial on storyboards, as stumbling through Stack Overflow for answers will not be a very fruitful exercise.
For historical purposes, it's worth noting that if you were using NIBs, you can use the construct you referenced to in your question (but you'd have to make sure that (a) the class name was right; and (b) you did the #import that class header). And if the destination NIB had a different name than the class, you'd have to do something like:
UIViewController *controller = [[NSBundle mainBundle] loadNibNamed:#"nibname" owner:self options:nil]`;
[self presentViewController:controller animated:YES completion:nil];
But this is all academic unless you're using NIBs. If using storyboards, use one of the above patterns if you need to transition to a new scene programmatically.

You are in over your head. You should slow down and do some more studying or things are going to crash all over the place. I suggest going through a good book on iOS development and doing the exercises.
On to your question:
In the code you posted you're using a class "ViewController". There is no system-defined class "ViewController", although it is a common class name in example projects, and I think some of the project templates in Xcode even create a root view controller with that class name.
The name you give to your variable ("GameOverViewController" in the code above) is local and not really important. That's just a variable name. You can rename it lateForDinner if you want to, and it won't make any difference as long as you change that name everywhere in the scope in which it's defined.
The fact that you're being told that the class "ViewController" is unknown suggests that it hasn't been defined anywhere in your program.
You need an #interface and #implementation section that defines the ViewController class. Usually those will be in files called ViewController.h and ViewController.m, respectively. They might look something like this:
//ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController: UIViewController //Defines it as a subclass of UIViewController
// properties and methods of ViewController defined here
#end
And the .m file:
//ViewController.m
#implementation ViewController
// Implementation of ViewController methods go here.
#end
It's also possible to include the interfaces of multiple classes in a single header file, and the implementations of multiple classes in a single .m file, although I dislike this practice intensely. Keep it simple - one class per .h/.m file pair, with the file names identical to the class name.

Related

Objective-c How To Segue from within a UITableView Sub Class

Yesterday I posted this question so all of the code can be found there for the structure of my problem.
The Problem... This Time
I have come from other languages to OBJ-C and some of the OOP structures are making me cringe a little bit (I don't like packing every possible function into a single UIViewController as some seem to do). I was originally going to make a full page UITableViewController with an embedded NavigationController however the use cases of this project would not allow me to use the default navbar. So I had to put in my own navbar and use a regular UITableView (resized to be pretty much full screen) instead of the simpler option, the UITableViewController... (I am aware this all could be solved by using it, but I cannot)
Instead I have a regular UIViewController with a property containing my own custom TasksTableView.h subclass. The subclass extends UITableView as seen in the link I posted above.
The actual problem is that I cannot seem to Segue or change views from inside of this UITableView because every function which does so, seems to need to come from the UIViewController class.
I Have Tried
Calling a manual segue in the didSelectRowAtIndexPath method of my UITableView subclass.
[self performSegueWithIdentifier:#"profile" sender:sender];
Which produces an obvious error telling me that performSegueWithIdentifier does not exist on this class, which it doesn't so thats fine. Obviously it belongs to the UIViewController class that instantiated my UITableView sub class...
I have tried importing the view controller that actually renders and holds the property of my table view subclass itself and trying to push the view to the stack.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *target = [storyboard instantiateViewControllerWithIdentifier:#"SingleTaskViewController"];
AllTasksViewController *allTasksView = [[AllTasksViewController alloc] init];
if(target) {
[target setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[allTasksView presentViewController:target animated:YES completion:nil];
}
}
This gives me the error Warning: Attempt to present <SingleTaskViewController: 0x7fa15d5359f0> on <AllTasksViewController: 0x7fa15d5363c0> whose view is not in the window hierarchy!
... Even though it must be in the heirarchy because it is the view that contains and instantiated this UITableView.
I have also tried manually invoking the didSelectRowAtIndexPath from the UIViewController that holds the UITableView sub class but again it was the same kinda thing. It invoked, however obviously since I had to pass in the index, it is me picking it instead of the table telling me what was actually selected.
What I Want
I would really like to keep the UITableView sub class seperate from the UIViewController and not bring the delegate methods and protocols to the view controller. I would prefer to keep the logic separated. All I need is a way to segue or transition to the Single Task View in question and send some data with it about what was pressed.
There are good tutorials out there, but the basic idea is...
Main "ViewController" class - contains a Table View, and a "manual" Segue to a "Profile" View controller
Separate Datasource and Delegate classes for the table view
Custom Protocol / Delegate to send the "didSelectRow" action
When the main vc loads, it creates instances of the Datasource and Delegate classes, and assigns them to the table view.
It also "conforms to" a custom Protocol in the Delegate class. This allows the Delegate class to "call back" to the main vc when a row is tapped.
I put together a very simple example demonstrating this approach that can be seen here: https://github.com/DonMag/OCTableViewExample

View Controller With two different XIB's..Tricky

I have a tricky question here..Please help..
I have One ViewController Called "DemoViewController" two different Xib's (Demo1Controller.xib & Demo2Controller.xib) are linked to the DemoViewController, Will load the Xib based upon the Condition..
I have navigation controller implemented in AppDelegate, Currently I am pushing this view controller(DemoViewController) with the XIB Demo1Controller, When User Taps a button in Demo1Controller, I need to load the same Viewcontroller i.e, DemoViewcontroller with Xib Demo2Controller..
Can this Possible?? Or Do i need to maintain the Different Classes for two Xib's
Let me know if you have any questions...
As a ViewController is just an object like any other object you can stack as many of them as you need on top of each other. Creating as many instances of that object as you want.
When you instantiate them you can do:
UIViewController *viewController = [[DemoViewController alloc] initWithNibName:#"Demo1Controller" bundle:nil];
or
UIViewController *viewController = [[DemoViewController alloc] initWithNibName:#"Demo2Controller" bundle:nil];
As long as the IBOutlets and delegate are set up correctly on both .xib's and they are set up using the same Custom class in IB. (Third icon from the left in the inspector panel, at the top.) If you fail to set them up properly it will simply crash on build and run.
And you can also check out a similar question I answered with a different approach some time ago.
Another approach
TRY THIS
- (id)init
{
if (YES)
self = [super initWithNibName:#"VC1" bundle:nil];
else
self = [super initWithNibName:#"VC2" bundle:nil];
return self;
}
I'm not sure if this is helpful to you, or appropriate for your situation, but I would probably suggest a slightly different approach (obviously, without knowing very much about your specific situation).
I would probably suggest that, rather than having one class have two different nibs, instead you have one class that has all of the common behaviour that these two 'screens' share, and then two concrete subclasses of this common parent class for each of the 'screens'. I am assuming that there are slight behavioural differences between the two.
You could then create an instance of your concrete subclasses with the specific nib name, as usual:
SubclassOneViewController *viewController = [[SubclassOneViewController alloc] initWithNibName:#"SubclassOneViewController" bundle:nil];

Storyboards start game button

I'm looking how to make a start button in storyboards, but it hasnt the initwithnib code to use, like in XLB. I'm using the sprite kit. So far my code is like this.
Viewcontroller.h = mainmenu
#interface ViewController : UIViewController
- (IBAction)PlayBtn:(id)sender;
#end
And in the viewcontroller.m I want to connect it to myScene where the game is. In the myScene.m.
for that I tried using following.
- (IBAction)PlayBtn:(id)sender {
MyScene *game = [[MyScene alloc] initWithNibName:nil bundle:nil];
[self presentViewController:game animated:YES completion:NULL];
}
Although getting error at the initWithNibName doesnt exist. I have only used xlb before but wanted to make a game with sprite-kit.
Here is a screenshot of my storyboard. All graphics are copyrigth protected.
Cheers!
You can mix and match nib files and scenes from storyboards if you like. If you want to do that, create a nibfile (XIB file) and save it to your project. Then use initWithNibName:bundle: as always.
If you haven't created a nib, that won't work.
If you want to load your view controller scene from your storyboard you have a couple of options.
You can set up a segue by control-dragging from your current view controller to another view controller, naming the segue with a unique identifier, and then invoking the segue directly using the view controller method performSegueWithIdentifier:sender:
Another option is to give the scene itself a unique identifier, then instantiate it with instantiateViewControllerWithIdentifier:, and display it using presentViewController:animated:completion: just like you are trying to do now.

NSInvalidArgumentException after manually triggering seque

I'm in the "awesome" position to have to support someone else's code even though before this project I have never worked in objective C or made iOS apps, so please excuse me if I'm doing something really obvious or stupid.
I needed a new view controller with a custom class. First I drew the view on my storyboard. Then I created a new class which I derived off of UIViewController. Then, I set the view's custom class to this new class I made. I hooked up the single button on the view to the code so I could make it close the view, then I made a (manual/modal) segue so I could call this new view from my main menu. All of this should be hooked up fine because I've used it before, but I'll show you how I call the segue anyway:
[self performSegueWithIdentifier:#"ScoreCard" sender:self];
Now, my problem is that when I press the button to run the above, I get the following error:
-[Scores _setViewDelegate:]: unrecognized selector sent to instance 0x9b4c460
Scores is the name of my custom UIViewController class. Its .h file looks pretty simple for now:
#import <UIKit/UIKit.h>
#interface Scores : UIViewController
- (IBAction)goBack:(id)sender;
#end
The .m file doesn't do anything besides what Xcode put in there by default and my implementation of goBack:
- (IBAction)goBack:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
}
Does anyone know what I'm forgetting? I successfully added another view controller yesterday in the same way and that one works just fine. Why doesn't this one?
The error that you're getting, -[Scores _setViewDelegate:]: unrecognized selector..., seems to be caused by setting the class of a UIView to a class that's not a subclass of UIView. Make sure that you've set the class for your view controller, not the view, to your custom class.

UITableViewController being initialised twice

I was trying to modally present a UINavigationController with a UITableViewController as it's root view but kept crashing the app when pressing the button to present the modal view.
- (IBAction)flipToDefaultsViewController:(id)sender
{
RootTableViewController *controller = [[RootTableViewController alloc] initWithNibName:#"RootTableViewController" bundle:nil];
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:controller];
nc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:nc animated:YES];
}
The app crash with the message:
[RootTableViewController numberOfSectionsInTableView:]: message sent to deallocated instance 0x5677b5
When I loaded up Instruments to take a further look it was apparent that two instances of my UITableViewController were created, one with the owner of the UINavigationController and the other by UIKit. The UIKit created instance was the one that was deallocated and causing the crash.
When I changed the initialisation from initWithNibName:bundle: to init the UITableViewController loaded fine (my .xib file was the same name as the class).
My question is why would this happen?
Should you not initialise a UITableViewController this way when adding it to a UINavigationController? I've had a hunt around the documentation with no joy so far.
Using iOS 5 with ARC but target deployment is 4.0.
I haven't worked out why the object was being initialised twice, however I did review the steps that I used to create the .xib file and it looks like there is a problem with copying a view from a Storyboard to Interface Builder. In hindsight this makes sense, but as the view appears to copy without error and everything else seems to look okay it's easily done.
It would appear that similar problems were experienced by others with similar results.
By creating a completely clean subclass of UITableViewController with a nib file (⌘-N) and copying code from the initial class into the new one I'm able to use the initial code above to alloc/init my modal view.
BTW I was mistaken in my opening post about the nib file loading correctly when using init. It wasn't and in fact this behaviour doesn't happen for UITableViewController apparently where as other classes having a class name the same as the .xib file will attempt to load the .xib first.
If this is a button, than you should not initialize anything when the button is pressed. You should initialize beforehand and simply present the modalViewController when the button is pressed.
Is there any reason why the rootViewController and the navigation controller cannot be initialized in the appdelegate didFinishLaunchingWithOptions method?

Resources