Setting values for an array of UILabels which in turn is in NSMutableDictionary - objective-c

I have an array of UILabels as follows:
#interface ClueLabels : NSObject
#property (nonatomic) IBOutlet UILabel *label1, *label2, *label3, *label4, *label5, *label6, *label7, *label8;
#property (nonatomic, readonly) NSArray *labels;
+ (ClueLabels *)clueLabels;
- (void)labelsDidLoad;
#end
Which is implemented as a singleton:
#implementation ClueLabels
#synthesize label1 = _label1;
#synthesize label2 = _label2;
#synthesize label3 = _label3;
#synthesize label4 = _label4;
#synthesize label5 = _label5;
#synthesize label6 = _label6;
#synthesize label7 = _label7;
#synthesize label8 = _label8;
#synthesize labels = _labels;
+ (ClueLabels *)clueLabels {
static ClueLabels *singleton;
static dispatch_once_t once;
dispatch_once(&once, ^{
singleton = [[ClueLabels alloc] init];
});
return singleton;
}
- (void)labelsDidLoad {
_labels = [[NSArray alloc] initWithObjects:_label1, _label2, _label3, _label4, _label5, _label6, _label7, _label8, nil];
NSLog(#"Clue labels did load");
}
#end
Then stored in an NSMutableDictionary as follows:
NSMutableArray *keyArray;
NSMutableArray *valueArray;
keyArray = [NSMutableArray arrayWithObjects:#"answerButtons", #"clueLabels", nil];
valueArray = [NSMutableArray arrayWithObjects: [AnswerButtons answerButtons], [ClueLabels clueLabels], nil];
NSMutableDictionary *externals = [NSMutableDictionary dictionaryWithObjects:valueArray
forKeys:keyArray];
Now I want to write to the labels (for instance, to clear them), but they are buried in a singleton and stored as an array inside key-valued dictionary. Conceptually, I need to send a message to one object in the dictionary using key-value syntax, and then loop over the objects in that array, clearing them with setText#"" for instance. Is this a nested message with a loop? I can't quite figure out how to structure this task. Thank you.
EDIT: I got to thinking and decided (maybe incorrectly) that because the labels are in a singleton, I don't need to access them through the dictionary at all, I just need to access the singleton and the dictionary will be updated automatically. So I decided my question/problem is more "how to talk to my singleton". I looked around SO some and then figured out I could do something like this:
NSLog(#"%#", [ClueLabels clueLabels].labels)
Which returns the details of each label. I'd like to be able to get the count of labels so I can loop over them and set their titles to arbitrary strings. Can I build around [ClueLabels clueLabels].labels or do I need a different approach? Thanks.

U can either loop on the UILabels as u said. Or u can use "makeObjectsPerformSelector" method on the array, which will (as its name says) make all the objects in it perform the selector u sent. Thus saves the looping part. But either way u need to get a hold of the array of labels contained in the dictionary

Mission accomplished! Here's how I did it:
// Clear the fields
for (NSInteger idx = 0; idx < 8; idx++) {
UILabel *l = [[ClueLabels clueLabels].labels objectAtIndex:idx];
[l setText:#""];
Obviously, I'm still learning!

You could also do something like this:
for (UILabel *theLabel in clueLabels)
{
[theLabel setText:#"];
}

Related

Singleton dictionary is empty, and I don't see why

I have an iOS app that matches incoming text fields to standard fields used to import records. My problem is that a NSMutableDictionary that uses those fields is empty! Here is the code that saves the mapping:
-(void)mapUserFields: (id) sender { // move contents of each textField when user has finished entering it
SingletonDictionary *sd = [SingletonDictionary sharedDictionary];
UITextField *tf = (UITextField *)sender; // textfield contains the pointer to user's half of the equation
int tagValue = (int)tf.tag; // get the tag value
[sd.dictionaryOfUserIndexes setObject:tf.text forKey:[NSString stringWithFormat:#"%d", tagValue]]; // value found in textField id'd by tag
NSLog(#"\nfield.text: %# tagValue: %d nsd.count: %d\n",tf.text, tagValue, sd.dictionaryOfUserIndexes.count);
}
This is the result of the NSLog:
field.text: 1 tagValue: 38 nsd.count: 0
This is the definition of the singleton in the .h file:
#property (nonatomic, retain) NSMutableDictionary *dictionaryOfUserIndexes;
This is the code to initialize the singleton in the .m file:
//-- SingletonDictionaryOfUserIDs --
+ (id) sharedDictionary {
static dispatch_once_t dispatchOncePredicate = 0;
__strong static id _sharedObject = nil;
dispatch_once(&dispatchOncePredicate, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
-(id) init {
self = [super init];
if (self) {
dictionaryOfUserIndexes = [NSMutableDictionary new];
}
return self;
}
#end
I believe my problem is because the sd.dictionaryOfUserIndexes is not initialized, but I am not sure if that's true, and if so, how to initialize it (I tried several different variants, all of which created build errors). I looked on SO and Google, but found nothing that addresses this particular issue. Help would be greatly appreciated!
There are a few things we could improve in this code, but the only thing wrong with it is the reference to dictionaryOfUserIndexes in the init method. The code as posted wouldn't compile, unless: (a) you have a line like:
#synthesize dictionaryOfUserIndexes = dictionaryOfUserIndexes;
so that the backing variable is named without the default _ prefix, or (b) you refer to the ivar with the default prefix, as in:
_dictionaryOfUserIndexes = [NSMutableDictionary new];
The other way -- preferable in most every context except within an init method -- is to use the synthesized setter, like:
self.dictionaryOfUserIndexes = [NSMutableDictionary new];
But with that change alone (so it will compile) your code runs fine, adds a value to the dictionary and logs an incremented count.

Updating contents of class variable NSMutableArray

I'm new to Objective C and iOS development in general, so if I missed some information that needs to be included please let me know and I'll do my best. At the same time, if any of my assumptions are incorrect or if I set this up totally dumb, please don't hesitate to yell at me. Thanks!
I have a class object called feeds. Here's how I initialize it in the .h file:
#interface ClassViewController : ContentViewController <UITableViewDelegate,
UITableViewDataSource> {
NSMutableArray *feeds;
}
#property (nonatomic, strong) NSMutableArray* feeds;
and in the .m:
#dynamic feeds;
I'm trying to get results from JSON and load them up into the feeds. Let's assume that resArr has correct data in it:
NSArray *resArr = [results objectForKey:#"data"];
if([self->feeds count]) {
[self->feeds removeAllObjects];
[self->feeds addObjectsFromArray:resArr];
}
else {
self->feeds = [[NSMutableArray alloc] initWithArray:resArr];
}
Now, this works fine the first time (i.e., the first time I put data into the array), but I get the following error subsequent times: *** -[NSMutableArray addObjectsFromArray:]: array argument is not an NSArray
I'm clueless. Any ideas?
Edit: JSON structures
1:
{"code":200,"data":[{"name":"ABM"},{"name":"ACC"}]}
2:
{"code":200,"data":{"100":{"subject":"ABM","title":"Decision Making in Agri-Food"},"130":{"subject":"ABM","title":"Farm Management I"}}}
you should use self.feeds instead of self->feeds, here's a good read Dot (“.”) operator and arrow (“->”) operator use in C vs. Objective-C
are you sure the subsequent times are actually NSArrays?
edit additon
Looks like the first JSon is just a array of names, where's as the second is a dictionary keyed with 100, 200, etc. So the JSON converts this into a dictionary keyed to 100, 200, ...
NSArray* resArr = [results objectForKey:#"data"];
if([self.feeds count]) {
[self.feeds removeAllObjects];
if([resArr isKindOfClass:[NSDictionary class]])
[self.feeds addObjectsFromArray:resArr.allValues];
else
[self.feeds addObjectsFromArray:resArr];
}
else {
self.feeds = [[NSMutableArray alloc] initWithArray:resArr];
}

Using NSString value to refer to UILabel

Have mercy, newbie here.
I have a NSString value that I construct on the fly, which is the name of a UILabel instance. I want to send the label a message to update its text. But, the two data types don't match. Here's enough code (I think):
In header file:
IBOutlet UILabel *Clue1; // IBOutlet and IBAction are IDE flags
IBOutlet UILabel *Clue2; // IB = interface builder
IBOutlet UILabel *Clue3;
In implementation file:
- (IBAction) newPuzzle:(id)sender { // Clear all fields & get new clue
[Clue1 setText:#""]; // Clear the fields
[Clue2 setText:#""];
[Clue3 setText:#""];
// Send up a randomly chosen new clue
NSArray *clues = [NSArray arrayWithObjects:#"222", #"333", nil];
NSInteger randomIndex = arc4random()%[clues count];
NSString *aClue = [clues objectAtIndex:randomIndex];
// The clue will be split into component digits and each piece sent to a different label
for (NSInteger charIdx = 0; charIdx < aClue.length; charIdx++) {
NSString *cluePos = [NSString stringWithFormat:#"Clue%d", charIdx + 1];
NSLog(#"%#", cluePos); // works
[cluePos setText:#"test"]; // Xcode notes the type mismatch
}
}
There are some similar questions on SO, but none are close enough for me to recognize that they apply to my case, at least as far as I can tell. Using the terminology of another language (R), I need to "coerce" the class of cluePos from NSString to UILabel. I'm on Xcode 4.2.1 and OSX 10.7.2.
TIA.
You can't coerce a string into a label because they are fundamentally different. The string doesn't have any knowledge of your view controller class or it's properties (some of which happen to be labels).
You can however use the valueForKey: method to get a property of an object by name, where the name is specified as a string. So to get a property called Clue1 on my view controller I'd say:
UILabel *label = [self valueForKey:#"Clue1"];
Or in your case, this:
NSString *cluePos = [NSString stringWithFormat:#"Clue%d", charIdx + 1];
UILabel *label = [self valueForKey:cluePos];
label.text = #"test";
(I'm assuming 'self' in this case refers to the view controller, but you can call this on any object that has properties.)
Another way to do this is to turn your string into a selector using NSSelectorFromString. That would look like this:
SEL selector = NSSelectorFromString(#"Clue1");
UILabel *label = [self performSelector:selector];
For your purposes either solution works equally well, however the advantage of using the selector is that you can pass arguments to the method call (so you could call a method that returns an object, not just access a property or IBOutlet).
Note that both of these methods will raise an exception if you try to access a property or call a method that doesn't exist. You can test if the property exists before calling it by saying:
SEL selector = NSSelectorFromString(#"Clue1");
BOOL labelExists = [self respondsToSelector:selector];
if (labelExists)
{
UILabel *label = [self performSelector:selector];
label.text = #"test";
}
else
{
//do something else
}

Objective C Reassignment/Memory Management Crash

As a relative Objective-C beginner, I'm obviously still not grasping certain memory management rules. I can't figure out how to make this not crash:
#interface MyClass { NSArray *playerArray4th; }
- (void) viewDidLoad { playerArray4th = [self getAudioPlayersForSoundFile:#"rimshot" ofType:#"aif"]; }
- (NSArray*) getAudioPlayersForSoundFile:(NSString*)soundFileName ofType:(NSString*)soundFileType {
//code instantiating objects....
NSArray *toRet = [[NSArray alloc] initWithObjects:toRetTickPlayer,toRetTickPlayerCopy,toRetTickPlayerCopy2,toRetTickPlayerCopy3, nil];
return toRet;
}
Then later, in a different function:
NSArray *currentArray = playerArray4th;
[currentArray release];
currentArray = nil;
currentArray = [self getAudioPlayersForSoundFile:fileName ofType:ofType];
And it crashes when trying to access the array again:
- (void) playSound:(NSString*)soundType {
AVAudioPlayer *currentPlayer;
if ([soundType isEqualToString:#"4th"]) {
if (playerArray4thCounter >= [playerArray4th count]) playerArray4thCounter = 0;
NSLog(#"Playing from array: %#",playerArray4th);
currentPlayer = [playerArray4th objectAtIndex:playerArray4thCounter];
playerArray4thCounter++;
}
}
Try to learn about properties and about using getters and setters. Don't take shortcuts unless you know exactly what's going on.
So define the playerArray4th property in your header file:
#property (nonatomic,retain) NSArray *playerArray4th;
And then in your .m file create getter/setter:
#synthesize playerArray4th;
Then, always use self.playerArray4th for assigning and getting the variable. The prior objects will be released when needed.
So this will not leak:
self.playerArray4th = [NSArray arrayWithObjects:#"text",#"text",nil];
self.playerArray4th = [NSArray arrayWithObjects:#"new array",#"text",nil];
because the second assignment releases the first array.
Furthermore, read about using autorelease. In short, if you alloc, copy or new, you should either release or autorelease. There's a lot to read about this here on SO which I will not repeat here now.
Don't forget to put self.playerArray4th = nil; in your dealloc method.

Objective C: What is the best way to create and use a dynamic boolean array?

I have been struggling with the best way of creating, accessing and updating values from a dynamic boolean array for more than a week now.
#interface myDelegate : NSObject
{
NSMutableArray *aShowNote;
}
#property (nonatomic, copy) NSMutableArray *aShowNote;
This is how I have initialised my array:
NSMutableArray *aShow = [[NSMutableArray alloc] init];
for (i=0; i < c; i++)
[aShow addObject:[NSNumber numberWithBool:false]];
self.aShowNote = aShow;
This seems to work OK but I'm baffled why each element is initialised with the same address.
But then what I've discovered in my research so far is that is seems that you need to replace the object if you want to change its value:
myDelegate *appDelegate = (myDelegate *)[[UIApplication sharedApplication] delegate];
NSInteger recordIndex = 1;
NSNumber *myBoolNo = [appDelegate.aShowNote objectAtIndex:recordIndex];
BOOL showNote = ![myBoolNo boolValue];
[appDelegate.aShowNote replaceObjectAtIndex:recordIndex withObject:[NSNumber numberWithBool:showNote]];
but this approach just seems to be over complicated (and it crashes too).
Terminating app due to uncaught exception'NSInvalidArgumentException', reason: '-[__NSArrayI replaceObjectAtIndex:withObject:]: unrecognized selector sent to instance 0x5b51d00
Any pointers to improve this code (and of course to make it work) would be very gratefully received.
Thanks
Iphaaw
the problem is that copy in a property copies the assigned object. And copy creates immutable objects.
Change your property to read: #property (nonatomic, retain) NSMutableArray *aShowNote;
And I think there is not much to improve, from what I know this is the way to go if you want an NSArray with booleans.
Why not use plain C for this simple case?
BOOL *aShow = malloc(sizeof(BOOL)*c);
for (i=0 ; i<c ; i++)
aShow[i] = false;
You just have to remember to free(aShow) when you are done with it.
It is not possible to change value of a NSNumber. It not mutable class.
Then, when you ask for two same value, the same object is return.
In your array init, why you don't initialized directly the array to avoid copy problem:
aShowNote = [[NSMutableArray alloc] init];
for (i=0; i < c; i++) {
[aShowNote addObject:[NSNumber numberWithBool:false]];
}
I'm baffled why each element is initialised with the same address.
Why? NSNumbers are immutable. The runtime only needs one NSNumber object to represent FALSE.