I am having some trouble getting my head around how I can init a class and pass identical init params to multiple instances. I can get it to work with 1 outlet (instance1). But how can I also get it to work with instance 2 without re-writing the variables for each instance?
ViewController.h:
#import "CustomClass.h"
#interface MYViewController : UIViewController
#property (unsafe_unretained, nonatomic) IBOutlet CustomClass *instance1;
#property (unsafe_unretained, nonatomic) IBOutlet CustomClass *instance2;
#end
ViewController.m:
#import "CustomClass.h"
#implementation MYViewController;
#synthesize instance1, instance2;
- (void)viewDidLoad
{
[super viewDidLoad];
instance1.variable1 = option1;
instance1.variable2 = 4.5;
instance1.variable3 = instance1.value;
[instance1 addTarget:self action:#selector(instance1DidChange) forControlEvents:UIControlEventValueChanged];
A loop with some KVC should do it for you...
#implementation MYViewController;
#synthesize instance1, instance2;
- (void)viewDidLoad
{
[super viewDidLoad];
for (int i = 0; i =< 1; i++) {
MyClass *newInstanceVariable = [[MyClass alloc] init];
newInstanceVariable.variable1 = option1;
newInstanceVariable.variable2 = 4.5;
newInstanceVariable.variable3 = instance1.value;
[newInstanceVariable addTarget:self action:#selector(instance1DidChange) forControlEvents:UIControlEventValueChanged];
instanceVariableName = [NSString stringWithFormat:#"instance%i",i];
[self valueForKey:instanceVariableName] = newInstanceVariable;
}
}
Should be pretty close to what you're looking for.
Essentially, you want to add the instances to an NSSet or NSArray and either iterate over that or use Key-Value Coding to set the values. If these outlets are conceptually a group of things rather than just a bunch of independent objects that happen to have the same initial values, you can use IBOutletCollection instead of IBOutlet to have them all kept in an array to begin with.
Related
I have looked all over the place for anyone who has experienced this issue but have yet to find anything relevant, so I thought I'd ask it myself...
I have a custom object (HitterData) which I will use to populate cells in a UITableView, then two ViewControllers (one is hitterTableViewController, the other is a "detail" view controller labeled "AddPlayerViewController").
The problem is that I can add HitterData objects to the NSMutableArray in my Table VC, but only one, and then when I add another one using the detail view controller, the Mutable array is "reinitialized" and I can again only have one object at a time.
HitterObject:
#implementation HitterData.m
#synthesize hitterName = _hitterName;
#synthesize position = _position;
-(id)initWIthNameAndPosition:(NSString *)hitterName position:(NSString *)position {
if ((self = [super init])) {
self.hitterName = _hitterName;
self.position = _position;
}
return self;
}
HitterTableViewController.h
#import "HitterData.h"
#import "HitterDoc.h"
#import "AddPlayerViewController.h"
#interface HitterTableViewController : UITableViewController
#property (nonatomic, strong) NSMutableArray *hitters;
- (IBAction)backButton:(id)sender;
- (IBAction)addPlayerView:(id)sender;
-(void)addHitterObject:(HitterData *)hitter;
HitterTableViewController.m (only relevant to make this more readable)
#implementation HitterTableViewController
#synthesize hitters = _hitters;
- (void)viewDidLoad {
[super viewDidLoad];
self.hitters = [NSMutableArray array];
}
-(void)addHitterObject:(HitterData *)hitter {
if(_hitters != nil) {
[_hitters addObject:hitter];
} else {
_hitters = [NSMutableArray array];
[_hitters addObject:hitter];
NSLog(#"MutableArray is not valid");
}
}
AddPlayerViewController.h
#interface AddPlayerViewController : UIViewController <UITextFieldDelegate, UINavigationControllerDelegate>
#property (weak, nonatomic) IBOutlet UITextField *nameTextField;
#property (weak, nonatomic) IBOutlet UITextField *positionTextField;
#property (nonatomic) HitterTableViewController *hitterTable;
#property (nonatomic) NSString *hitterName;
#property (nonatomic) NSString *position;
//-(void)addNewHitterToHittersArray:(HitterData *)hitter;
- (IBAction)addPlayerToRoster:(id)sender;
AddPlayerViewController.m
#implementation AddPlayerViewController
#synthesize hitterTable;
- (void)viewDidLoad {
[super viewDidLoad];
hitterTable = [[HitterTableViewController alloc] init];
}
- (IBAction)addPlayerToRoster:(id)sender {
self.hitterName = [self.nameTextField text];
self.position = [self.positionTextField text];
HitterData *hitter = [[HitterData alloc] init];
hitter.hitterName = self.hitterName;
hitter.position = self.position;
[hitterTable addHitterObject:hitter];
ArraySingleton *arrayS = [[ArraySingleton alloc] init];
[arrayS initializeArray];
[arrayS addToHittersArray:hitter];
if (arrayS) {
NSLog(#"arrayS exists in AddPlayerVC");
} else {
NSLog(#"arrayS does not exist");
}
[self performSegueWithIdentifier:#"backToTeamTableViewController" sender:self];
}
Am I missing something here?
Guess based just on the code shown:
Every time you wish to add a player it looks like you create a new AddPlayerView/AddPlayerViewController. In turn that controller creates, in its viewDidLoad, a new HitterTableViewController - which of course has its own empty array. The code should instead be referencing the existing HitterTableViewController.
BTW: The common design pattern is MVC - model, view, controller - consider whether you are in your current situation because you've stored part of your model, the array, in your controller, and maybe both controllers should be referencing a common shared model object containing that array.
BTW: All those #synthesise statements are unneeded. In modern Obj-C synthesis is automatic and you rarely need these statements. Also it is generally recommended to not use the property backing variable directly, and certainly not when storing into the property as this breaks KVO. (There also appears to be a related typo in HitterData.m but as you don't report that as not working it is probably just a typo in your question and not code.)
HTH
AddPlayerViewController should know nothing about HitterTableViewController, return the new HitterData object with a delegate method.
- (IBAction)addPlayerToRoster:(id)sender
{
Hitter *hitter = [[Hitter alloc] init];
hitter.name = [self.nameTextField text];
hitter.position = [self.positionTextField text];
[self.delegate didAddHitter:hitter];
}
Then back in HitterTableViewController
- (void)didAddHitter:(Hitter *)hitter
{
[self.hitters addHitter:hitter];
[self dismissViewControllerAnimated:YES completion:nil];
}
I recently started learning Objective-C and Cocos-2D. I tried to define my own method for automating the creation of sprites.
I added my own class where I'll create other automation methods as well. Anyhow my .h file looks like this:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface ActionsClass : CCNode {
}
#property (nonatomic, strong) CCSprite* createSprite;
#property (nonatomic, strong) CCSprite* spriteName;
#property (nonatomic, strong) NSString* pngName;
#property (nonatomic) CGPoint* spriteCoordinate;
- (CCSprite *)createSprite: (CCSprite *)spriteName: (NSString *)pngName: (CGPoint *)spriteCoordinate;
#end
And the .m is:
#import "ActionsClass.h"
#implementation ActionsClass
#synthesize createSprite = _createSprite;
#synthesize spriteName = _spriteName;
#synthesize pngName = _pngName;
#synthesize spriteCoordinate = _spriteCoordinate;
- (CCSprite *)createSprite: (CCSprite *)spriteName: (NSString *)pngName: (CGPoint *)spriteCoordinate
{
if (!_createSprite)
{
_createSprite = [[CCSprite alloc] init];
_spriteName = [CCSprite spriteWithFile:_pngName];
_spriteName.position = ccp(_spriteCoordinate->x, _spriteCoordinate->y);
[self addChild:_spriteName];
}
return _createSprite;
}
#end
In the main .m file where I want to call the method:
[self createSprite: saif: #"saif.png": ccp(100,100)];
This would give the warning that xcode didn't find the instance method createSprite and defaults it to id
Thanks a lot and sorry if the font or the formatting of the question aren't super neat.
Your method declaration is wrong, so you wont be able to call it.
It should be:
- (CCSprite *)createSprite:(CCSprite *)spriteName pngName:(NSString *)pngName coord:(CGPoint *)spriteCoordinate;
And called like:
[self createSprite:someSprite pngName:somePNGName coord:someCoord];
Edit: I didn't see that you were trying to call this from another class. To do that you will need to import the ActionsClass header file, and call this method on an instance of ActionsClass, e.g.
ActionsClass *actionsClassObject = [[ActionsClass alloc] init];
[actionsClassObject createSprite:someSprite pngName:somePNGName coord:someCoord];
I'm very new to Objective C and am having trouble with some very basic things.
In AppDelegate.m, I'm getting the following errors:
Use of undeclared identifier 'health'
Use of undeclared identifier 'attack'
Code (respectively):
[Troll setValue:100 forKeyPath:health];
[Troll setValue:10 forKeyPath:attack];
I'm not really sure how to declare the identifiers.
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSObject *Troll = [[NSNumber alloc]init];
[Troll setValue:100 forKeyPath:health];
[Troll setValue:10 forKeyPath:attack];
return YES;
}
#end
AppDelegate.h
#import `<UIKit/UIKit.h>`
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#end
#interface Troll : NSObject {
NSNumber *health;
NSNumber *attack;
}
#end
Keys are strings, and not something else (like dangling syntactic garbage). Furthermore, '100' and '10' are not objects. Even after this, you don't want to set the properties of the class itself but of its instances. Try
[troll setValue:[NSNumber numberWithInt:100] forKeyPath:#"health"];
[troll setValue:[NSNumber numberWithInt:10] forKeyPath:#"attack"];
instead.
The first thing to say is that you are not instantiating a Troll object, but a NSNumber... why? You would have to do Troll *troll = [[[Troll alloc] init] autorelease];
The way to set and get attributes from classes uses to be by declaring properties on the class. This way the compiler will manage the memory for you (retains and releases). Another way would be accessing directly your ivars.
However, if you want to use the setValue:forKeyPath: you have to use a NSString for the second paremeter, which is the name of the variable.
#interface Troll : NSObject
{
NSNumber *_health;
NSNumber *_attack;
}
#property (nonatomic, retain) NSNumber *health;
#property (nonatomic, retain) NSNumber *attack;
#end
#implementation Troll
#synthesize health = _health;
#synthesize attack = _attack;
- (void)dealloc
{
[_health release];
[_attack release];
[super release];
}
- (void)customMethod
{
//using properties
[self setHealth:[NSNumber numberWithInteger:100];
[self setAttack:[NSNumber numberWithInteger:5];
//accessing ivars directly - remember to release old values
[_health release];
_health = [[NSNumber numberWithInteger:100] retain];
[_attack release];
_attack = [[NSNumber numberWithInteger:5] retain];
}
#end
Good luck!
Although you are defining a class called Troll with a 'health' and 'attack', you aren't instantiating one. You probably want one of the following in your application didFinishLaunchingWithOptions:
Troll *troll = [[Troll alloc]init];
troll.health = [NSNumber numberWithInt:100];
troll.attack = [NSNumber numberWithInt:10];
OR
Troll *troll = [[Troll alloc]init];
[troll setHealth:[NSNumber numberWithInt:100]];
[troll setAttack:[NSNumber numberWithInt:10]];
These two are equivalent.
i'm getting "use of undeclared identifier" errors in my .m file with the code below and can't seem to work it out.
NSArray *imageViews = [NSArray arrayWithObjects:img1, img2, img3, img4, img5, img6, img7, img8, img9, img10, img11, img12, img13, img14, img15, img16, img17, img18, img19, img20, img21, img22, img23, img24, img25, img26, img27, img28, img29, img30, img31, img32, img33, img34, img35, img36, img37, img38, img39, img40, nil];
In my .h file i have 40 images, all with referencing outlets:
#property (weak, nonatomic) IBOutlet UIImageView *imgX;
where X is a number from 1-40. In my .m file, the NSArray *imagesViews works fine as long as it's inside a method, but i can't declare it outside the method so that it is available to all methods. As an Objective-C novice, I don't where to go from here. I'd appreciate any help.
You don't have to initialize the array outside of a method to make it accessible from all methods. What you should do instead is declare it as a property and initialize it inside the viewDidLoad method.
In the .h file:
#property (strong, nonatomic) NSArray *imageViews;
#property (weak, nonatomic) IBOutlet UIImageView *img1;
// ...
In the .m file:
#synthesize imageViews, img1, img2, ...
// ...
- (void)viewDidLoad
{
[super viewDidLoad];
// ...
self.imageViews = [NSArray arrayWithObjects:self.img1, self.img2, ... , nil];
}
Also, note that because you have 40 image views, you should probably avoid declaring a property for each one of them. You can assign tags to them, and then retrieve them using the method viewWithTag.
In the header:
#interface MyClass : NSObject {
NSArray *imageViews;
}
#end
In the implementation:
#implementation MyClass
- (id) init
{
self = [super init];
if (self != nil) {
imageViews = [[NSArray arrayWithObjects:img1, nil] retain];
}
return self;
}
// now you can use imageViews also from other methods
- (void) dealloc
{
[imageViews release];
[super dealloc];
}
#end
I am having a very odd issue retrieving/retaining a variable in my iPhone application delegate (AppDelegate). Initially, I can step through and see that my values are passed to logfile (the NSString variable in question), but when logfile is retrieved from another class (see the code below), it faults.
Here is my AppDelegate.h file:
#import < UIKit/UIKit.h >
#interface AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *_window;
MainViewController *_mainViewController;
NSString *logFile;
}
#property (nonatomic, retain) NSString *logFile;
#property (nonatomic, retain) ProductClass *item;
#property (nonatomic, retain) UIWindow *window;
-(void)appendToLog:(NSString *)textToLog;
#end
Here is my AppDelegate.m:
#import "AppDelegate.h"
#import "MainViewController.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize logFile;
- (void) applicationDidFinishLaunching:(UIApplication *)application {
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
_mainViewController = [[MainViewController alloc] init];
UINavigationController *_navigationController = [[UINavigationController alloc] initWithRootViewController:_mainViewController];
//Initialize the product class
[self appendToLog:#"Application loaded"];
[_window addSubview:_navigationController.view];
[_window makeKeyAndVisible];
}
-(void)appendToLog:(NSString *)textToLog {
//Append the log string
if(logFile!=nil) {
NSString *tmp = [[logFile stringByAppendingString:textToLog] stringByAppendingString:#"\n"];
logFile = tmp;
}
else {
NSString *tmp = [textToLog stringByAppendingString:#"\n"];
logFile = tmp;
}
}
#end
When I use the call (from another class):
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *s = [appDelegate logFile];
"logfile" comes back as "out of scope" so the local variable "s" is mush.
What am I doing wrong here? It isn't making sense to me.
You should replace logFile = tmp; with self.logFile = tmp; because you need to use the "self." prefix when assigning an ivar in order for the code to call the proper settor method. As it is, you're just assigning the ivar to an autoreleased object instance, instead of retaining it. The "self." prefix ensure that the code does the right thing. Without it, you're just assigning the variable without retaining it.
I would recommend prefixing logfile with self in your assignment statements in your AppDelegate. For example, self.logfile = ...
From the UIApplication class reference - UIApplication assigns and does not retain the delegate.
You need to initialize your instance of AppDelegate first.