Reputation: 19571
I"m quite new at this .....sorry in advance. I have created an app in xCode that contains 10 View controllers. Each view controller has an image view and a round rect button.
I would like to set it up so that pushing the button on a view will segue to one of the other 9 views but the view that loads needs to be random.
What I've done so far:
-I placed 10 view controllers on the storyboard and set the classes of each to scene1-10
-I created 10 Objective-C class-ViewControllers (.h and .m files) named scene1-10
-I have created segues between them in this fashion scene1-->scene2-->scene3-->scene4 -->scene5-->scene6-->7scene-->scene8-->scene9-->scene10-->scene1
-I have set the class of each segue to segue1-10 naming them after the scene they are going to (the segue between scene1 and scene2 would be segue2)
-I made all the .h files look like this (scene # changes depending on the file):
#import <UIKit/UIKit.h>
@interface scene1 : UIViewController
- (IBAction) randomButton;
@end
-I made all the .m files look like this (scene # changes depending on view
#import "scene1.h"
@interface scene1 ()
@end
@implementation scene1
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction) randomButton : (id) sender
{
int i = arc4random() % 10;
if(i == 1)
{
[self performSegueWithIdentifier: @"segue1"
sender: self];
}
else if(i == 2)
{
[self performSegueWithIdentifier: @"segue2"
sender: self];
}
else if(i == 3)
{
[self performSegueWithIdentifier: @"segue3"
sender: self];
}
else if(i == 4)
{
[self performSegueWithIdentifier: @"segue4"
sender: self];
}
else if(i == 5)
{
[self performSegueWithIdentifier: @"segue5"
sender: self];
}
else if(i == 6)
{
[self performSegueWithIdentifier: @"segue6"
sender: self];
}
else if(i == 7)
{
[self performSegueWithIdentifier: @"segue7"
sender: self];
}
else if(i == 8)
{
[self performSegueWithIdentifier: @"segue8"
sender: self];
}
else if(i == 9)
{
[self performSegueWithIdentifier: @"segue9"
sender: self];
}
else
{
[self performSegueWithIdentifier: @"segue10"
sender: self];
}
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
@end
-I linked the received action "randomButton" listed for each view controller to the button on the view controller
What happens:
I get an "Incomplete Implementation" warning on the "@implementation scene1" line of each .m file but the app does build and load.
Once loaded if I press the button the app freezes and the debug window of xCode pops up with the following:
2012-05-05 07:11:11.789 randomTester[2448:f803] -[scene1 randomButton]: unrecognized selector sent to instance 0x686d370
2012-05-05 07:11:11.792 randomTester[2448:f803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[scene1 randomButton]: unrecognized selector sent to instance 0x686d370'
*** First throw call stack:
(0x13cc022 0x155dcd6 0x13cdcbd 0x1332ed0 0x1332cb2 0x13cde99 0x1914e 0x190e6 0xbfade 0xbffa7 0xbed8a 0x3e2cf 0x3e5e6 0x24dc4 0x18634 0x12b6ef5 0x13a0195 0x1304ff2 0x13038da 0x1302d84 0x1302c9b 0x12b57d8 0x12b588a 0x16626 0x1f5d 0x1ec5)
terminate called throwing an exception(lldb)
I realize I could be doing this completely wrong. I Have searched all over but cant find exactly what I need so Ive had to piece together bits from similar code.
Thank you for your valued time and assistance!
UPDATE
Ok, Round Two:
You guys are awesome I tottally didnt realize I had done that with the declaration and the implementation of the IBAction. That fixed all the Implementation errors. However, I appear to have an underlying issue.
My .h files now look like this:
#import <UIKit/UIKit.h>
@interface scene1 : UIViewController
- (IBAction) randomButton;
@end
My .m flies now look like this:
#import "scene1.h"
@interface scene1 ()
@end
@implementation scene1
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)randomButton
{
int i = arc4random() % 10 + 1;
NSString *segueIdentifier = [NSString stringWithFormat:@"segue%d", i];
[self performSegueWithIdentifier:segueIdentifier sender:self];
}- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
@end
No implementation errors and it builds fine but when loaded if I press the button the app freezes and the debug window of xCode pops up with the following:
2012-05-05 13:00:09.236 randomTester[2699:f803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver (<scene1: 0x6868f90>) has no segue with identifier 'segue5''
*** First throw call stack:
(0x13cb022 0x155ccd6 0xdd61b 0x2592 0x13cce99 0x1814e 0x180e6 0xbeade 0xbefa7 0xbdd8a 0x3d2cf 0x3d5e6 0x23dc4 0x17634 0x12b5ef5 0x139f195 0x1303ff2 0x13028da 0x1301d84 0x1301c9b 0x12b47d8 0x12b488a 0x15626 0x224d 0x21b5)
terminate called throwing an exception(lldb)
It seems obvious that the App is looking at the scene1 view controller and not seeing a "segue5" (which was the random segue called by the new IBAction). This is because there is no segue5 on the scene1 view controller as it just has one segue going to scene2 as described in the first part of my question above.
To try and fix this I created 10 segues on scene1 with classes of segue1-10. I connected segues 2-10 to scenes 2-10 respectively. It seems logical that the button could call segue1 as i'ts random and therefore point back to itself. Since I could not create a segue going from scene1 to scene1 I created a second segue to scene2 and just gave it a class of segue1.
No errors and the App loads but hitting the button causes the App to freeze this time displays this in debug:
2012-05-05 13:45:04.467 randomTester[2803:f803] -[scene1 randomButton:]: unrecognized selector sent to instance 0x68b3760
2012-05-05 13:45:04.479 randomTester[2803:f803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[scene1 randomButton:]: unrecognized selector sent to instance 0x68b3760'
*** First throw call stack:
(0x13cb022 0x155ccd6 0x13cccbd 0x1331ed0 0x1331cb2 0x13cce99 0x1814e 0x180e6 0xbeade 0xbefa7 0xbdd8a 0x3d2cf 0x3d5e6 0x23dc4 0x17634 0x12b5ef5 0x139f195 0x1303ff2 0x13028da 0x1301d84 0x1301c9b 0x12b47d8 0x12b488a 0x15626 0x224d 0x21b5)
terminate called throwing an exception(lldb)
Question one: Any Idea how these segues need be set up to make this work?
Question two: Would it be better to:
-Create 10 view controllers named scene1-10 each with a image view and a button
-Set the button on each initiate a segue to an 11th view controller named sceneRandomizer (I would not give these segues classes)
-Connect 10 segues from sceneRandomizer going back to scenes 1-10 (Classes would be segue1-10)
-Write code in "sceneRandomizer" to automatically initiate a random segue from the 1-10
group very much like a redirect (I would have no idea how to write that....just throwin that out there)
In theory this would alleviate the second error I ran into where the segue called wasn't present on the view controller that called it.
If this alternate setup would work would it create any significant lag going from one segue right into another one like that?
Update
It's Working now
Finally got this to work. It seems to be a "round-about" method and I'm sure there is a much more efficient and accepted way to do this but this is what I did:
The code in .h and .m is the same as the last attempt but I'll reiterate it just to e clear:
My .h file looks like this:
#import <UIKit/UIKit.h>
@interface scene1 : UIViewController
- (IBAction) randomButton;
@end
My .m file looks like this:
#import "scene1.h"
@interface scene1 ()
@end
@implementation scene1
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)randomButton
{
int i = arc4random() % 10 + 1;
NSString *segueIdentifier = [NSString stringWithFormat:@"segue%d", i];
[self performSegueWithIdentifier:segueIdentifier sender:self];
}- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
@end
What I did different:
Ok so this time - I dragged 11 view controllers onto the storyboard
I named one "startPage" and named the others "scene1-10"
I created 1 .h and one .m file (coded as shown above) and named them StartPage.h and
StartPage.m
I set the class all 11 of the views on the storyboard to the one view controller (StartPage)
I made 10 segues, 1 going from StartPage to each of the 10 scenes then
I made 10 segues from scene 1, 1 going from scene1 to each of the other 9 scenes and gave them classes of segue2-10 (named after the scene they go to) Please
note Because I could not create a segue from scene1 to scene1 I made a second segue from scene1 to scene2 and simply gave it a class of segue1.
I repeated the last step for each of the other 9 scenes
This does work. It builds and runs with no errors. The StartPage comes up first. Hitting the button will load a random view from the group (scene1-10) and hitting the button on the resulting view will load another view from the group randomly and so on.
The down side to doing it this way is the time it takes to set up all the segues. This storyboard now has 110 segues controlling the movement between only 11 views. .....that's an hour of my life I can never get back.
For newbies like me:
If you choose to use this method be very careful when naming your segue identifiers. Lets say your laying out the segues for scene 5 and you mistakenly name 2 segues segue2 and forget to name one segue3. If the end user is on scene5 and the random button calls segue3 the app will crash because scene5 doesn't have a segue named segue3. I only mention this because it could be very difficult to find such an error during testing as everything would appear to work great unless it just happened to call segue3 when you were on scene5.
If anyone sees any drawbacks to using this method that I am unaware of by all means please bring it up. And if you have a better method I am all ears :)
Upvotes: 3
Views: 2702
Reputation: 1767
I used to do something like this using modal viewControllers from a single parent. A push of the button would tell the parentView to dismissModalViewController, and then in the parent's viewWillAppear, generate a random number and pushModalViewController. I did it all with xibs, so I'm not sure how you would go about it using modal segues. If you figure that out, let us know.
Upvotes: 0
Reputation: 27536
The declaration and the implementation of the method randomButton
should have the same signature. So, you should declare it like this in the .h
file:
- (IBAction)randomButton:(id)sender;
or change it in the .m
file like this:
- (IBAction)randomButton
{
int i = arc4random() % 10;
// ...
}
As a side note, you can simplify your code by generating the segue identifier using the value of i
:
- (IBAction)randomButton
{
int i = arc4random() % 10 + 1;
NSString *segueIdentifier = [NSString stringWithFormat:@"segue%d", i];
[self performSegueWithIdentifier:segueIdentifier sender:self];
}
Also, note that I changed the line where i
was generated to produce values from 1
to 10
. Your original code produces values from 0
to 9
.
Upvotes: 1
Reputation: 17381
The error & compiler warnings are telling you that even though you say you implement randomButton you don't actually.
You are implementing
-(void)randomButton:(id)sender;
which is not the same as -(void)randomButton;
Two different signatures. Make either side match the other and it should work out.
Upvotes: 2