Xcode how to switch between scenes - objective-c

I'm new at Objective-C development and looking for a way to change from scene programmatically.
I've searched a lot but I keep getting errors.
Could anyone tell me what I'm doing wrong?
Any help would be very appreciated.
My code so far:
- (IBAction)retryOrNextLevel:(id)sender {
if (retryInteger == 1){
[self viewDidLoad];
}
if (retryInteger == 2){
Level2 *secondLevel = [[Level2 alloc] initWithNibName:#"Level2" bundle:nil];
[secondLevel setTitle:#"Level2"];
secondLevel.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:secondLevel animated:NO completion:Nil];
}
}
retryInteger is an integer that is set to 1 if you have failed the level and to 2 if you have completed the level of the game. I use the same button for two things: if you have failed the level you can replay the level and when you've completed the level you can go to the next level. Level2 is a UIViewController.
Thanks in advance!
Matis

First off, you should never call [self viewDidLoad]; this is called automatically when would you guess your viewDidLoad.
Second what is retryInteger? I am going to assume for this answer it is some sort of value you have set if the user has already hit this button, or done something.
So lets see what we can do with your code :
- (IBAction)retryOrNextLevel:(id)sender {
if (retryInteger == 1){
[self callSomeMethodThatIsntVIEWDIDLOAD];
}
if (retryInteger == 2){
Level2 *secondLevel = [[Level2 alloc] initWithNibName:#"Level2" bundle:nil];
[secondLevel setTitle:#"Level2"];
// Pushing onto a navigationController would seem more logical (Would recommend)
// [self.navigationController pushViewController:secondLevel animated:YES];
// Otherwise you want this
[self presentViewController:secondLevel animated:YES completion:nil];
}
}
There are several things assumed in this answer and until you update your question this answer is based on assumptions. First of what is Level2 is it a UIViewController? What error you are getting? As your original code should present the view controller so I am going to assume that when it tries to present it you are doing something in Level2 controller that is breaking it, cause if something goes wrong in Level2 controller then it will show on this line [self presentViewController:secondLevel animated:NO completion:Nil]; I would also make it so it is animated as if you say NO it makes this modalTransitionStyle pointless.
If you update your question I will try to improve this answer but until you do there isn't much else I can do. If your app quits on crash then use an Exception Breakpoint that is stops on when an exception is thrown.
EDIT
You are not calling [self retryOrNextLevel] you want to be calling [self retryOrNextLevel:myButton] your not calling the correct method. Where ever you are calling it you are missing the parameters so you are missing :myButton.
Your method is
- (IBAction)retryOrNextLevel:(id)sender;
and not
- (IBAction)retryOrNextLevel;
note the sender parameter.
To make sure that the class responds to retryOrNextLevel you can do
if([self respondsToSelector:#selector(retryOrNextLevel:)] {
// Code for calling the method.
}

I have found the answer! The error was because I forgot to declare something and the reason nothing happened when I touched the button was because I didn't drag a segue between the view controllers. This video answered my question: http://www.youtube.com/watch?v=XApYtAlRDVE.

Related

shouldPerformSegueWithIdentifier always executed first

I have a function called "openButtonPressed" which gets executed when a button on the ui gets pressed. Now I would like to show a loading view at first and then execute the segue. For some reason, the segue always gets called first.
Does somebody have a clue?
Thank you!
- (void)openButtonPressed:(id)sender
{
NSDebug(#"Open Button");
[self showLoadingView];
static NSString* segueToContinue = kSegueToContinue;
if ([self shouldPerformSegueWithIdentifier:segueToContinue sender:self]) {
[self performSegueWithIdentifier:segueToContinue sender:self];
}
}
- (void)showLoadingView
{
if(!self.loadingView) {
self.loadingView = [[LoadingView alloc] init];
[self.view addSubview:self.loadingView];
}
[self.loadingView show];
}
It looks like LoadingView is being initialized without a size.
It also may be executing the segue very quickly and you don't have any time to see the loading view.
You can set a breakpoint at the performSegue call and use the view debugger in Xcode to confirm if the view was loaded and what size the view was initialized to.
Maybe the view is not in the right place or in the right size. But even if you fix that you need to add a delay so the user can see the loading... I replicated your scenario here and after giving the view a initWithFrame it appears, but cannot be seen because is too fast...
If you want to show the loading for a period of time before the performSegue, you could use the performSelector:withObject:afterDelay.
If you need to wait an action from the loadingView, create a completionHandler in the loadingView initializer...
The hints below were very helpful to me and totally a better solution but I ended up using [CATransaction flush]; to force the refresh.
- (void)openButtonPressed:(id)sender
{
NSDebug(#"Open Button");
[self showLoadingView];
[CATransaction flush];
static NSString* segueToContinue = kSegueToContinue;
if ([self shouldPerformSegueWithIdentifier:segueToContinue sender:self]) {
[self performSegueWithIdentifier:segueToContinue sender:self];
}
}

performSelectorInBackground causes random crash when view is dismissing

I'm having some random crashes at this part of my code:
-(void) goBack {
[self performSelectorInBackground:#selector(addActivityIndicator) withObject:nil];
[self.navigationController popViewControllerAnimated:YES];
}
- (void)addActivityIndicator {
#autoreleasepool {
UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
UIBarButtonItem * barButton = [[UIBarButtonItem alloc] initWithCustomView:activityView];
[activityView startAnimating];
self.navigationItem.leftBarButtonItem = barButton;
}
}
When I want to exit the screen where these method exists, the other ViewController have to process some data. To inform the user that processing is occurring I add an activity indicator to the left button in my navigation bar.
The problem is that sometimes I get an exc_bad_access in addActivityIndicator method. The frequency is very random, sometimes the XCode shows the error at the end of #autoreleasepool, sometimes at the line self.navigationItem.leftBarButtonItem = barButton;
I imagine that sometimes my viewController is destroyed but the thread is still running and try to access the navigationItem of a object that don't exists anymore. But I'm not sure if that is the problem and I don't know how to fix it.
I'm using ARC in my project and this problem occurs in all iOS versions that I tested.
Please, anyone can explain me what is happening and how can I fix this?
Thanks.
You should never do UIKit stuff in the background.
By calling [self performSelectorInBackground:#selector(addActivityIndicator) withObject:nil]; you are updating the UI on a background thread. You should only ever update the UI on the main thread.
Edit
Based on your comment you are trying to have the UI update before the view pops. The way to do that would be:
[self addActivityIndicator]
[navigationController performSelector:#selector(popViewControllerAnimated:) withObject:[NSNumber numberWithBool:YES] afterDelay:0];
You could also look into dispatch_after

Xcode 4: Loading a Nib in a method in a different file

sorry if this question is elementary, but I've been stuck on this bug for the past 2 days & I haven't been able to get past it. I'm using Xcode 4.3.2
I'm trying to load a nib named AController.xib in a method called "- (void) process" in the file named BController.m
To be clear, I copied ./A/AController.xib (which is a UIView), ./A/AController.m, ./A/AController.h to the directory ./B
I only mention this because I'm not sure if it matters for my question.
Currently, my flow works as flows (which could be my problem):
A view loads with a "buy" button
the user clicks the "buy" button which has an IBOutlet named "buyNow"
"buyNow" calls "buy", which then calls "process"
process then tries to load the nib with the following (option 1):
AController *blah;
for (id object in bundle){
if ([object isKindOfClass:[AController class]])
blah = (AController *) object;
}
assert(blah != nil && "blah can't be nil");
[self.view addSubView: blah];
The error I get here is "Thread 1: signal SIGABRT" in main.m
I've also tried (option 2),
AController *myView = [[AController alloc] initWithFrame:self.view.bounds];
[self.view addSubview:myView];
[AController release];
And (option 3)
AController * vc = [[AController alloc] initWithNibBundle:#"AController" bundle:nil]; [self.nc pushViewController:vc animated:NO];
I get the same error for all 3 choices. Each option was tried in the method "process". "process" is written in B.m. Can anyone offer some help so that I may figure this out? Any guidance as to why these options failed would be very helpful for my understanding and would be much appreciated. Thanks for helping a noob!
If AController is a UIView subclass, it cannot load a NIB. Verify it is in fact a controller, but from the initWithFrame and the way you are adding it to a view, it looks like it is not, or is being handled incorrectly.

Can't get UIImagePickerController to work?

-(void) openPhotoLib:(id)sender {
[self dismissModalViewControllerAnimated:YES];
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
[imagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
[imagePicker setDelegate:self];
[self presentModalViewController:imagePicker animated:YES];
NSLog(#"openPhotoLib called");
}
Nothing happens except for the NSLog, even though my view controller is a subclass of UINavigationController and UIImagePickerDelegate. Does anyone have any insight or experience with UIImagePickerController?
I should note that I'm primarily using an iPhone for testing.
SOLUTION: Make a new class that subclasses ONLY UINavigationBarDelegate and UIImagePickerDelegate. In that classes's viewDidAppear, put the code to modally present the imagePicker. Create an instance of this class inside the method (inside another class, import the .h file and all) and the modally present that class.
^ I take it back. The modal animations was the real problem. Trying to use another class instance for this messes up the method implementations of UIImagePicker.
The problem is due to dismissModalViewControllerAnimated and presentModalViewController being called one after the other.
The dismiss action takes some time as it has to animate the view being dismissed. During the animation, it is still the top Modal View. So, you cant present another model view during that time. If you try then the call fails and does nothing.
To fix, use [self dismissModalViewControllerAnimated:NO]; i.e no animation.
If you still want animation then follow one of these solutions:
Problem opening new ViewController after UIImagePickerController
Correct way of showing consecutive modalViews
EDIT:
If using storyboards, you should define prepareFroSegue in your delegate:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"AddPlayer"])
{
PlayerDetailsViewController
*playerDetailsViewController =
(PlayerDetailsViewController *) segue.destinationViewController;
playerDetailsViewController.delegate = self;
}
}
(source)
Am I not sure, but I think that the call:
[self dismissModalViewControllerAnimated:YES];
might interfere with the following call
[self presentModalViewController:imagePicker animated:YES];
since both are done within the same run loop.
I would suggest doing like this:
define a method to encapsulate the call to presentModal...
- (void)presentPicker:(...)picker {
[self presentModalViewController:imagePicker animated:YES];
}
replace the original call to presentModal... with:
[self performSelector:#selector(presentPicker:) withObject:picker afterDelay:0.0];
Explanation: by using performSelector the way I suggest, we are simply enqueuing the call to presentPicker in the run loop (without any delay actually, since we specify 0.0 as a delay value). In this way, we give UIKit a chance to dismiss the modal view and do all necessary clean up before we try and present the next modal view.
The reason to define presentPicker as a method is that performSelector only allows to specify one single argument (instead of the two that presentModal... requires).
Hope this helps.

How do I use UIImagePickerController just to display the camera and not take a picture?

I'd like to know how to open the camera inside of a pre-defined frame (not the entire screen). When the view loads, I have a box, and inside it, I want to display what the camera sees. I don't want to snap a picture, just basically use the camera as a viewfinder. I have searched this site and have not yet found what I'm looking for. Please help.
Thanks!
Thomas
Update 1:
Here is what I have tried so far.
1.) I added UIImageView to my xib.
2.) Connect the following outlet to the UIImageView in IB
IBOutlet UIImageView *cameraWindow;
3.) I put the following code in viewWillAppear
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentModalViewController:picker animated:YES];
NSLog(#"viewWillAppear ran");
}
But this method does not run, as evident by the absence of NSLog statement from my console. Please help!
Thanks,
Thomas
Update 2:
OK I got it to run by putting the code in viewDidLoad but my camera still doesn't show up...any suggestions? Anyone....? I've been reading the UIImagePickerController class reference, but am kinda unsure how to make sense of it. I'm still learning iPhone, so it's a bit of a struggle. Please help!
- (void)viewDidLoad
{
[super viewDidLoad];
// Create a bool variable "camera" and call isSourceTypeAvailable to see if camera exists on device
BOOL camera = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
// If there is a camera, then display the world throught the viewfinder
if(camera)
{
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
// Since I'm not actually taking a picture, is a delegate function necessary?
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentModalViewController:picker animated:YES];
NSLog(#"Camera is available");
}
// Otherwise, do nothing.
else
NSLog(#"No camera available");
}
Thanks!
Thomas
Update 3:
A-HA! Found this on the Apple Class Reference.
Discussion
The delegate receives notifications
when the user picks an image or movie,
or exits the picker interface. The
delegate also decides when to dismiss
the picker interface, so you must
provide a delegate to use a picker. If
this property is nil, the picker is
dismissed immediately if you try to
show it.
Gonna play around with the delegate now. Then I'm going to read on wtf a delegate is. Backwards? Whatever :-p
Update 4:
The two delegate functions for the class are
– imagePickerController:didFinishPickingMediaWithInfo:
– imagePickerControllerDidCancel:
and since I don't actually want to pick an image or give the user the option to cancel, I am just defining the methods. They should never run though....I think.
add
[picker
dismissModelViewControllerAnimated:YES];
to delegate method bodies.
It will dismiss the view.