Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

Counter across 3 View Controllers?

Need a way to make a counter work across at least 3 View Controllers.

One view controller has an -(IBAction)plus1 button and an -(IBAction)plus 0 button.


Those buttons segue to the next view controller, that has an -(IBAction)plus2 button and an -(IBAction)plus 0 button.... etc.


Each segues and adds all button clicks through to the last view controller with an IBOutlet UILabel *count text label.


I got this to work on one view controller. Can't figure out how to make it work across 3VCs. Someone suggested Singleton?

Thanks for any thoughts.


Thanks JDL55

Posted on Feb 2, 2013 9:11 PM

Reply
34 replies

Feb 3, 2013 9:53 AM in response to Frank Caggiano

Singleton sounds like over-complication. You need to decide where the model should be, and reference it from each view controller. In your case, the model is just a single integer value, so very simple. The easiest approach is just to add a property to the application delegate (AppDelegate.h).


@property (nonatomic) int currentValue;

Adjust the value during segues (FirstViewController.m, SecondViewController.m, etc.). This assumes each segue has been assigned an identifier in the storyboard.


- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

{

AppDelegate *appDelegate = UIApplication.sharedApplication.delegate;

if ([segue.identifier isEqualToString:@"plus1"]) {

appDelegate.currentValue += 1;

}

else if ([segue.identifier isEqualToString:@"plus0"]) {

// no action

}

}


In the final view, set the label text (LastViewController.m).


- (void)viewDidLoad

{

[superviewDidLoad];

AppDelegate *appDelegate = UIApplication.sharedApplication.delegate;

self.currentValueLabel.text = [[NSNumber numberWithInt:appDelegate.currentValue] stringValue];

}


You should immediately see there is lots of room for improvement if this is a real app, but do the simple case first to make sure you get the concept. Potential areas for improvement after that:

  1. Encode the math operation increment in the identifier so instead of if/else in prepareForSegue, you can just parse the identifier and perform the math operation. Then you could add buttons/segues without changing any code.
  2. Allow an initial value to be set, then continue to perform math operations on the current value as the user goes up/down the view controller stack. Show the current value on each view.
  3. Use a single UIViewController class for all of the storyboard views with math operation buttons.
  4. Instead of attaching the model to the application delegate, which is not very flexible, pass the model into a property of each view controller during prepareForSegue.

Feb 3, 2013 11:12 AM in response to JDL55

...so thanks for taking the time. Below is what I'm visually trying to accomplish.

It's not an incrementCounter. Each button passes a certain value to the label.

User uploaded file

This is what it looks like now on a single view controller and it works great:

I'll start working on prepare for Seque: Got my fingers crossed... Ha.

If you see something else just let me know. Thanks Llessur999and Frank!

I'll report back.

User uploaded file

Feb 3, 2013 12:00 PM in response to JDL55

The concept I described will work fine for that. On each storyboard view, set the segue identifier for either the yes or no button to some standard text like correctAnswer. Then do this in prepareForSegue.


- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

{

AppDelegate *appDelegate = UIApplication.sharedApplication.delegate;

if ([segue.identifier isEqualToString:@"correctAnswer"]) {

appDelegate.currentValue += 1;

}

}


I notice your storyboard only has a single segue from one scene to the next. To use the concept I described, you need two segues: one for each button. The source for each segue should be the button itself, not the view controller. Otherwise you will need to add button action handlers and programmatically invoke the segue. That is more complicated and not necessary.


Since these are modal segues, you need some way for the user to dismiss the stack of modal views. For example, a button on the final view.


Your storyboard looks like it has a navigation controller off the screenshot to the left. So you could be using push segues rather than modal segues if you want the user to be able to backtrack to a prior question using the navigation bar.


Using a separate view for each question is fine if this is just an exercise or the question count is very small. But if this is a real questionairre app, stacking up modal views like that is not optimal. The question text and correct answer for all questions should be part of your model. A single question view would show the question text for the current question.

Feb 3, 2013 10:53 PM in response to Llessur999

Hello, I'm progressing really well but reached a stumbling block. I'm getting red circle errors...

Use of undeclared identifier "appDelegate". Use of undeclared identifier "initWithNIbName".

Since I use segues, I don't think I need to use the IBActons correct? I used your first idea, plus1 for the Yes segue and plus0 for the No segue since they need different values.

I know we're close, do you see anything I need to do? See screen shots below.

User uploaded file

User uploaded file

Feb 4, 2013 10:31 AM in response to JDL55

These are basic compile errors.


Use of undeclared identifier "appDelegate".

The type AppDelegate is not understood. You need to add #import "AppDelegate.h"to reference AppDelegate in a module.


Use of undeclared identifier "initWithNIbName".

Function prepareForSegue is missing a closing brace, so the compiler is attempting to resolve initWithNibName within the scope of prepareForSegue.


Since I use segues, I don't think I need to use the IBActons correct

For this particular scenario you can use only segues.


Do I need to tell the AppDelegate where to display the data?

AppDelegate does not display the data, just store it.


The label on the destination view controller was called

IBOutlet UILabel *count;

it is not connected to anything right now just idle.

This IBOutlet is equivalent to currentValueLabel in my first post. Per the example I provided, you should set the label text in the viewDidLoad of the view controller containing this label.


An approach that might make learning this easier for you: Build this up from scratch, one step at a time, testing in the simulator as you add each bit of functionality. Then you know exactly what causes something to go wrong.

Feb 8, 2013 12:02 AM in response to Llessur999

Hi, I'm so close. Just a couple errors to correct and I can run it.

Made numbers next to lines just to identify, they are not in the code.

Line 22 (Use of undeclared identifier 'initWithNitName') Not sure where you wanted to place the bracket.

Line 58 (Property 'currentValueLabel' not found on object of type 'EndViewController*')

Thoughts?


FirstViewController.m

15 MrWizoAppDelegate *appDelegate = UIApplication.sharedApplication.delegate;

16 if ([segue.identifier isEqualToString:@"plus1"]) {

17 appDelegate.currentValue += 1;

18 }

19 else if ([segue.identifier isEqualToString:@"plus0"]) {

20 appDelegate.currentValue += 0;

21 }

22 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

23 {

24 self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

25 if (self) {

26 // Custom initialization

27 }

28 returnself;

=========================


EndViewController.m

56 [superviewDidLoad];

57 MrWizoAppDelegate *appDelegate = UIApplication.sharedApplication.delegate;

58 self.currentValueLabel.text = [[NSNumber numberWithInt:appDelegate.currentValue] stringValue];

59 }


60 - (void)didReceiveMemoryWarning

61 {

62 [superdidReceiveMemoryWarning];

// Dispose of any resources that can be recreated.

63 }


64 @end


Feb 8, 2013 12:47 PM in response to JDL55

Line 22 (Use of undeclared identifier 'initWithNitName') Not sure where you wanted to place the bracket.

Match up every brace. In the screenshot a few posts up, prepareForSeque does not have a closing brace. I am on a PC right now but like other IDE's, Xcode probably shows the brace range for the current insertion point in the gutter at the left side of the editor.


Line 58 (Property 'currentValueLabel' not found on object of type 'EndViewController*')

As I mentioned, currentValueLabel is the name of the IBOutlet for the UILabel on the last view controller. Change this to whatever your outlet is named.

Feb 10, 2013 11:38 AM in response to JDL55

I suspect one of two problems.


1. An error in prepareForSegue. Put a breakpoint on prepareForSegue and step through each line to make sure it works. If it doesn't even get to the breakpoint, it is probably #2.


2. You formerly had IBActions for the buttons. If you just deleted the functions (the code) but did not update the storyboard by disconnecting the event, pressing the button will fault because it tries to execute the function, which does not exist. Select the button and view the connections in the Connections Inspector. If you see any connected event such as the Touch Up Inside in the picture below, if the target no longer exists, disconnect the event by pressing the small X. Note: This is a common problem in many IDEs that have designers. If this is what caused the SIGABRT, in the debug output you will see something like this that tells you the reason: "'NSInvalidArgumentException', reason: '-[MiddleViewController yesButtonClick:]: unrecognized selector sent to instance."

User uploaded file


This is a good reason for my advice to build the app from scratch, add one piece of functionality at a time, test each addition. Then when a fault occurs, you know it was caused by the last incremental change.

Sep 21, 2013 1:07 AM in response to Llessur999

Hello,

Thanks so much. It is working great.

I have only one minor fix and that is how to clear the UILabel back to zero.

I thought maybe on last VC.....

.h file:

@property (retain, nonatomic) IBOutletUILabel *currentValueLabel;


-(IBAction)zero;


.m file:

-(IBAction)zero {

_currentValueLabel=0;

_currentValueLabel.text = [NSString

stringWithFormat:@"%@", _currentValueLabel];



It builds and runs but when I click on this new reset button nothing happens.

Any thoughts?

Thanks

Sep 21, 2013 10:19 AM in response to JDL55

You are setting the currentValueLabel UILabel property implicit backing variable _currentValueLabel to 0, which is effectively nil, clearing that backing variable. You are making it too complicated. To prevent confusion, I recommend you NEVER reference implicit backing variables (that start with underscore) unless you have a VERY GOOD reason and know exactly what you are doing. Referencing the property rather than the backing variable theoretically could be slower, but you would need a very computationally intensive app to detect the difference.


@property (weak, nonatomic) IBOutletUILabel *currentValueLabel;

- (IBAction)clearButtonClick:(id)sender;


-----


- (IBAction)clearButtonClick:(id)sender {

self.currentValueLabel.text = @"0";

}



Side Note: I notice the IBOutlet in your sample code specifies retain. I think that is a pre-ARC pattern. I assume you are not using ARC. See Legacy Patterns in the Resource Progrramming Guide.

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/LoadingRe sources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051i-CH4-SW35

Counter across 3 View Controllers?

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.