Hello I am new to the Objective C and I have a problem.
I have a View controller from where I call the level_1 of my game.
GameViewController *level1 = [self.storyboard instantiateViewControllerWithIdentifier:#"GameIdentifier"];
[self.navigationController pushViewController:level1 animated:YES];
It works fine.
... BUT I want to make two different levels (level_2 and level_3) using the same ViewController (GameViewController) (I will call them from the same class),
but I don't know how to pass an argument (for example an int) to the GameViewController (that argument will be the current level, for example 2 or 3).
You need to add an instance variable (and some accessors to get/set it) to your GameViewController class, and then assign the level number to your "level1" or "level2" instances.
Here is some sample code, showing you how you should write your GameViewController class:
// GameViewController.h
#interface GameViewController : UIViewController
#property (nonatomic, readwrite, assign) int level;
#end
// GameViewController.m
#implementation Test
#synthesize level;
- (void)viewDidLoad
{
[super viewDidLoad];
if (level == 1)
{
// Do something for level 1
}
else if (level == 2)
{
// Do something for level 2
}
}
#end
Then, you need to pass the level number to your view controller:
GameViewController *level1 = [self.storyboard instantiateViewControllerWithIdentifier:#"GameIdentifier"];
level1.level = 1;
[self.navigationController pushViewController:level1 animated:YES];
Related
How can I access the value from an inputField located in a second viewController?
The class name of the second view controller is SettingsViewController and the outlet name for the inputField is setRateInput.
I tried this but it didn't work…
double taxRateFromInput = [[self.settings.setRateInput text]doubleValue];
when I NSLog it comes out as The value is: (null)
Any idea what I'm doing wrong?
Here is the implementation file for the main viewController:
#import "SettingsViewController.h"
#interface ViewController ()
#property (strong, nonatomic) SettingsViewController * settings;
#end
#implementation ViewController
// lazy instantiation
-( SettingsViewController *) settings
{
if (_settings == nil) {
_settings = [[SettingsViewController alloc]init];
}
return _settings;
}
- (IBAction)calculatePrice:(id)sender {
double taxRateFromInput = [[self.settings.setRateInput text]doubleValue];
#end
In theory, you could create a global. Create a new class, call it something like taxRate (.h and .m)
In taxRate.h, add the following code:
#import <Foundation/Foundation.h>
#class MyTaxRate;
#interface TaxRate : NSObject {
}
#property (nonatomic, retain) double * taxRateFromInput;
+(TaxRate*)getInstance;
#end
Then, in your controller, put a "#import taxRate.h" in there. In your .m file, add the following:
#import "TaxRate.h"
#implementation TaxRate
#synthesize taxRateFromInput;
static TaxRate *instance =nil;
+(TaxRate *)getInstance
{
#synchronized(self)
{
if(instance==nil)
{
instance= [TaxRate new];
}
}
return instance;
}
#end
Note: This is extremely similar in structure to what I'm purposing.
if you have the reference from the object view controller you can just access by the property from your attribute.
You instantiated a new SettingsViewController, but you didn't do anything to instantiate its textfield setRateInput. You can do it when you instantiate it:
_settings = [[SettingsViewController alloc]init];
_settings.setRateInput = [UITextField alloc] initWithFrame:someFrame]];
or, as a beter solution, instantiate the text field in -init of SettingsViewController
- init {
if (self = [super init] {
self.setRateInput = [UITextField alloc] initWithFrame:someFrame]];
}
return self;
}
If you use nib files, this would be a lot easier.
Note: setRateInput is a bad name for a property. Consider rateTextField instead.
Edit I forgot to add that you have to add the text field as a subview to its parent view.
So it will be like,
_settings = [[SettingsViewController alloc]init];
_settings.setRateInput = [[UITextField alloc] initWithFrame:someFrame] autorelease];
[_settings.view addSubView:_settings.setRateInput];
In this case, the setRateInput is retained by its super view. You're not using ARC, so you can call autorelease on your text field.
The better solution: Use - (void) loadView; inside SettingsViewController. Loading the view is the responsibility of the correspondent view controller.
- (void) loadView {
self.setRateInput = [[UITextField alloc] initWithFrame:someFrame] autorelease];
[self.view addSubView:_settings.setRateInput];
}
Edit: xib files and storyboards can help you out. Give these tutorials a try.
You are on the right track, also well done with your lazy instantiation (as
a demonstration that you grasped the concept, I mean).
But note, that outlets don't get connected until viewDidLoad is called. So if you
just alloc/init your viewController (lazily), the outlet to your textfield is pointing to nil.
The outlet doesnt get connected until your controller's view property is accessed, ie the view is displayed.
What you could do is give the settings viewController a handle to your calculating viewController and let it set a public property on the calculating viewController that represents the rate.
This is a common pattern - delegation - where one viewController (settingsViewcontroller) calls a method on its delegate (calculating viewController).
You wouldn't need the settingsViewcontroller property in your calculating viewController then, but just instantiate a new settings viewController every time you want it to be brought up, giving it a reference to your calculating viewController.
Another possibility - maybe even better - is to define a model object that does calculation and takes care of the rate it needs to calculate. Then you could give your settingsViewcontroller a reference to that model object (probably instantiated in your
other viewController), so that it can change the rate on it.
PS: also re think how you instantiate viewControllers generally. The designated initialiser is -initWithNibName:bundle: - so usually, you wouldn't just alloc/ -init them.
If you use storyboards (you probably should!), use storyboard's -instantiateViewControllerWithIdentifier: or use the above mentioned designated initialiser.
I'm a relatively new iPhone developer and am making great progress building my 2nd iPhone app. In the app I'm building now I'm doing some code separation with some protocols and delegates so that I car re-use some of my code in a variety of places throughout my code.
Here's what I want to happen:
CITRootViewController creates an instance of a CITReportCreator class, passing itself as a property so that the reportCreator can open additional view controllers and such.
CITReportCreator class is declared as implementing the CITImageCaptureDelegate protocol, which is declared in the CITImageCaptureViewController file.
CITImageCaptureViewController defines the delegate protocol and has a method that passes back data and references to the child view controller so that CITReportCreator can interact with it's data, close the related XIB, etc.
I believe I'm getting the delegate and protocol established correctly, and verified that my 'delegate' object still contains data when it is called, but I'm getting a EXC_BAD_ACCESS method when my view controller tries to pass data back to the delegate in this line of code:
[self.delegate childViewControllerDidFinish:self];
Here's a good portion of the rest of my code. I had this working by using CITRootViewController as my delegate instead of the CITReportCreator class, but now that I'm separating the code, something has broke.
CITReootViewController.m (the view controller that calls the Report Creator)
//create a nrew report
-(IBAction)createReport:(id)sender {
CITReportCreator *report = [CITReportCreator alloc];
[report createNewReport:self];
}
CITReportCreator.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "CITImageCaptureViewController.h"
#interface CITReportCreator : NSObject <CITImageCaptureDelegate>
#property (strong, nonatomic) NSArray *imageList;
#property (nonatomic) NSInteger imageIndex;
-(int) createNewReport:(UIViewController *)parent ;
//Delegate Methods
-(void) childViewControllerDidFinish:(UIViewController*)viewController;
#end
And CITReportCreator.m
#import "CITReportCreator.h"
#implementation CITReportCreator
{
UIViewController *parentController;
}
#synthesize imageList;
#synthesize imageIndex;
-(int) createNewReport:(UIViewController *)parent
{
//store a reference to the parent view controller
parentController = parent;
// init code....
//head to the first image capture view
[self startImageCapture];
return 0;
}
-(int)startImageCapture
{
//pull the image name from the array of images
NSString *imageName = [imageList objectAtIndex:imageIndex];
//prep the image capture controller
CITImageCaptureViewController *capture = [[CITImageCaptureViewController alloc] initWithNibName:#"CITImageCaptureViewController" bundle:nil];
//Assign the capture controller's delegate
capture.imageName = imageName;
capture.delegate = self;
//Display the capture controller
[parentController presentModalViewController:capture animated:YES];
return 0;
}
//a break point set here never gets hit.
-(void) childViewControllerDidFinish:(UIViewController*)viewController;
{
[viewController dismissModalViewControllerAnimated:YES];
}
#end
And finally, the CITImageCaptureViewControllers
CITImageCaptureViewController.h
#import <UIKit/UIKit.h>
#protocol CITImageCaptureDelegate <NSObject>
-(void) childViewControllerDidFinish:(UIViewController*)viewController;
#end
#interface CITImageCaptureViewController : UIViewController
{
id<CITImageCaptureDelegate> delegate;
}
#property (nonatomic,assign) id<CITImageCaptureDelegate> delegate;
#property (nonatomic, assign) NSString *imageName;
//continue button pressed method
-(IBAction)continueButtonPressed:(id)sender;
#end
And the .m file
#import "CITImageCaptureViewController.h"
#interface CITImageCaptureViewController ()
#end
#implementation CITImageCaptureViewController
#synthesize navItem;
#synthesize imageName;
#synthesize delegate = _delegate; //i think this may be part of the problem
//cutting out initWithNibName, viewDidLoad, etc...
- (IBAction)continueButtonPressed:(id)sender
{
[self.delegate childViewControllerDidFinish:self];
}
#end
I find nothing with delegates and protocols all that simple, but I'm guessing I'm missing a small change somewhere. Can you help me head in the right direction?
I just started to learn iOS programming and I have a problem with inheritance. There are 2 files.
First file
Header
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
int x;
}
#end
Implementation:
#import "ViewController.h"
#import "NewClass.h"
#implementation ViewController
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
x = 999;
NewClass *myClass = [[[NewClass alloc] init] autorelease];
}
#end
Second file
Header:
#import "ViewController.h"
#interface NewClass : ViewController
#end
Implementation:
#import "NewClass.h"
#implementation NewClass
-(id)init {
self = [super init];
if (self != nil) {
NSLog(#"%i",x);
}
return self;
}
#end
In ViewController I set x to 999, and in NewClass I want to get it, but when I call NSLog(#"%i",x); it gives me 0.
Where did I make a mistake?
You have a timing problem.
The init method gets called first (at all levels of the inheritance hierarchy, so in both ViewController and NewClass). This is when you print out your value of x, when it is still zero.
The viewDidLoad method only gets called much later, generally at a point after a view controller's view has been added to a superview. It's functionality that's specific to the UIViewController class.
To make your example work, put an init method in your ViewController class that looks like the one in your NewClass class, and set x there.
Also, you don't need to create a NewClass instance within ViewController. When you create a NewClass object, it is automatically a ViewController as well. In the same way that a dog is an animal automatically, and so is a cat. When you create a dog, you don't want to create an animal as well!
As sidyll says, you should probably do a bit more reading about how inheritance works, I'm afraid!
You need to review you OOP concepts. Object-Oriented Programming with Objective-C is a must.
Your class NewClass indeed inherits the x variable, but not it's value. When you create an instance of it, you're creating a shiny new instance whose values have nothing to do with the parent class.
Another point of view to help you is that x was set in a object of ViewController class. The NewClass inherits from ViewController class, not from an arbitrary instance (object, where you set x).
That's because -viewDidLoad is not called until well after -init returns. Your superclass should do configuration like that in its -init method.
This is the delegate
#protocol DropControllerDelegate;
#interface DropController : NSObject
id<DropControllerDelegate> delegate;
}
#property (nonatomic, assign) id<DropControllerDelegate> delegate;
+ (DropController*) sharedController;
#protocol DropControllerDelegate <NSObject>
- (void)openUserButtons;
- (void)startUpload;
- (void)uploadDone;
- (void)uploadFailed;
- (void)startDownload;
- (void)downloadDone;
- (void)subFolderLoaded;
#end
This is the singleton code:
static DropController *sharedCont = nil;
#pragma mark Singleton stuff
+ (DropController *) sharedController {
#synchronized(self) {
if (!sharedCont)
sharedCont = [[DropController alloc] init];
return sharedCont;
}
return sharedCont;
}
The code to set the delegate is (in both myControllerA and myControllerB):
DropController* dropHelper = [DropController sharedController];
dropHelper.delegate = self;
I'm able to receive the calls in one controller but not in the other controller (the code is a copycat), this is driving me crazy!
As i said in my comment, since you are using a shared instance of that object, it will overwrite the previously set delegate to the new object. So i would suggest to hold a array in your sharedController. Add objects to that array which will want to become the delegates to the sharedController.
Then iterate through that array and call the method over each object in it. Its pretty simple.
Edit:
When you assign the delegate in your viewcontroller A with the statement dropHelper.delegate = self; object A becomes the delegate. Calling the method [delegate openUserButtons]; will trigger the method in viewcontroller A. But when you do dropHelper.delegate = self; in viewcontroller B, [delegate openUserButtons]; will call the method in object B. It will not call both the methods from a and b. Since there is only one delegate variable and you are using a shared singleton object.
//
// MyGameViewController.h
//
#import < UIKit/UIKit.h >
#import "SecondViewController.h"
#interface MyGameViewController : UIViewController {
IBOutlet SecondViewController *secondViewController;
}
-(IBAction)goToSecondView;
#end
//
// MyGameViewController.m
//
#import "MyGameViewController.h"
#implementation MyGameViewController
-(IBAction)goToSecondView{
[self presentModalViewController:secondViewController animated:YES];
}
//
// MyGameView.h
//
#import < UIKit/UIKit.h >
#import "Sprite.h"
#interface MyGameView : UIView {…}
Currently I have implemented a button on the MyGameView.xib to invoke the secondViewController view and it works. But I want the secondViewController get invoked by programming inside MyGameView.m when there is interruption, not by pressing a button. Therefore, I think there are 2 approaches:
a) Either make the goToSecondView method available to MyGameView.m
b) Implement all the code in MyGameViewController.h and MyGameViewController.m to MyGameView.m.
Issues:
1) When tried to make a) happen, I have to make goToSecondView method starting with (void), not (IBAction). But then how to invoke it in MyGameView.m?
2) I tried to do b) and implemented all code to MyGameView.m. But presentModalViewController is a method of ViewController and does not work in UIView. So what is the solution?
As you stated, you can't call presentModalViewController in a UIView class. This seems like a great opportunity to use a delegate. You could do something along the lines of:
In MyGameView.h
#protocol MyGameViewDelegate
- (void)showSecondView;
#end
#interface MyGameView {
}
#property (nonatomic, assign) id <MyGameViewDelegate> delegate;
...
#end
In MyGameView.m, when you need to show the second view:
[self.delegate showSecondView];
In MyGameViewController.h:
#import "MyGameView.h"
#interface MyGameViewController : UIViewController <MyGameViewDelegate> {
...
In MyGameViewController.m:
#pragma mark MyGameViewDelegate methods
- (void)showSecondView {
[self goToSecondView];
}
Note that you'll also need to set MyGameViewController to be the delegate of MyGameView. You could do that in Interface Builder, or in code, depending on where you create the two objects.
To do it in code, for example in the MyGameViewController.h viewDidLoad method:
myGameView.delegate = self;