Change a property within NSArray - ios

So i got a question how to do the following:
I got an NSArray with Objects.
NSArray *array = [[NSArray alloc] initWithObjects:object1, object2, nil];
object1 and object2 are Subclasses of UIButton. This subclass has a BOOL property (BOOL prop;) which is working fine.
object1.prop = YES;
NSLog (#"What does the BOOL say? %#", object1.prop ? #"YES" : #"NO");
-> Console outputs YES.
Sothis is all working fine, but how can i change the value of prop within the NSArray? For sure things like [[array objectAtIndex:0] anf]; = YES; won't work because of the Syntax, but i don't really know the syntax how to do it.
I'd love if anybody helped me!
Thanks in advance.

There are a number of related ways designed to avoid the compiler warnings. The simplest 'inline' approach is (assume your subclass of UIButton is called MyButton):
((MyButton *) array[0]).prop = YES.
or (using older syntax):
((MyButton *) [array objectAtIndex: 0]).prop = YES.
or
MyButton *button = (MyButton *) array[0];
button.prop = YES.

[(YourSubclass *)[array objectAtIndex:0] setProp:YES];
In general the cast is optional, since NSArray holds objects of the type id. But for code clearness it can harm to cast, so you will see later what you where intending to do.

Related

How to initialize an NSArray of NSString?

I'm using a property of NSArray type. Then I'm trying to initialize or setting values for the NSArray. When I use shorthand assignment, I'm getting the output. But when I'm trying with long initialization style, I'm not getting the result. What should be the right way for the latter??
Here is the code snippet:
#property NSArray * moods;
//shorthand assignment
self.moods=#[#"Happy",#"Sad"];
NSLog(#"Hello %#",[self moods]);
This is working. But when I tried:
//long initialization style
[[self moods]initWithObjects:#"Happy",#"Sad", nil];
NSLog(#"Hello %#",[self moods]);
This isn't doing the same way. Suggest me something please.
The second example should be:
self.moods = [[NSArray alloc] initWithObjects:#"Happy",#"Sad", nil];
alloc must always be called before init to actually allocate the memory for the object. [self moods] is going to return nil until you assign something to self.moods.
Edit:
If you really want to avoid the assignment by property dot notation syntax for whatever reason, you can use the setter method instead:
[self setMoods: [[NSArray alloc] initWithObjects:#"Happy",#"Sad", nil]];
The answer above is completely correct. I would love just to add a comment for the sake of completeness but I can't so I'll add an extra answer to give all the options.
You can use the convenience initializers if you always get confused with the order of the alloc and init. Or if you want to have cleaner code.
self.moods = [NSArray arrayWithObjects:#"Happy",#"Sad", nil];
But the answer above it's perfect and I personally prefer the more explicit alloc init pattern.
just some alternative approach without dots... ;)
[self setMoods:#[#"Happy", #"Sad"];

Obj C and makeObjectsPerformSelector - am I overlooking something?

being still kinda new to obj-c, I was playing around with the makeObjectsPerformSelector method.
I have two arrays containing UISteppers and UITextfields respectively:
_stepper = [NSArray arrayWithObjects:
_stepMa, _stepMafree, _stepDe, _stepDefree, _stepFl, _stepFlfree,
_stepEn, _stepEnfree, _stepEnBl, _stepEnBlfree, _stepVo, _stepVofree,
_stepVe, _stepVefree, _stepIn, _stepInfree, _stepOt, _stepOtfree,
_stepIn170, _stepIn170free, _stepZy, _stepZyfree,
nil];
_fields = [NSArray arrayWithObjects:
_MaFeld, _MaFeldfree, _DeFeld, _DeFeldfree, _FlFeld, _FlFeldfree,
_EnFeld, _EnFeldfree, _EnBlFeld, _EnBlFeldfree, _VoFeld, _VoFeldfree,
_VeFeld, _VeFeldfree, _InFeld, _InFeldfree, _OtFeld, _OtFeldfree,
_InFeld170, _InFeld170free, _ZyFeld, _ZyFeldfree,
nil];
In some method I want to reset them:
- (void) resetFields
{
[_stepper enumerateObjectsUsingBlock: ^(UIStepper* stepper, NSUInteger idx, BOOL *stop)
{
stepper.value = 0;
}];
[_fields enumerateObjectsUsingBlock: ^(UITextField* field, NSUInteger idx, BOOL *stop)
{
field.text = #"0";
}];
}
which works as expected.
trying to shorten that code a bit I tried my luck with the mentioned method:
- (void) resetFields
{
[_stepper makeObjectsPerformSelector:#selector(value) withObject:0];
[_fields makeObjectsPerformSelector:#selector(text) withObject:#"0"];
}
which had no effect... I guess there is something I did not consider, but what?
Thanks!
To bypass the problem of int to be a C type and not an object, use KVC (Key-Value Coding). If you call setValue:forKey: on a NSArray object, the method setValue:forKey: is call on each of the objects of the array. And with a bonus, KVC is managing all the primitive stuff.
[_stepper setValue:#0 forKey:#"value"];
[_fields setValue:#"0" forKey:#"text"];
performSelector calls (all kinds of them) can only take Objective-C objects (ones that can be represented by id type). C types like double, BOOL, int etc. will not work, so you can not set value this way unless you change its type to NSNumber*.
For setting text property, you need to use setText: selector; text is the getter. Since this property type is Objective-C class NSString, performSelector will work.

NSMutableArray Allocate then replaceObjectAtIndex

I have a NSMutableArray that i define in the header file as:
#property (strong, nonatomic) NSMutableArray *tempPhotosArray;
Then i allocate as:
_tempPhotosArray = [[NSMutableArray alloc] init];
What i'd like to know is if i then go to replaceObjectAtIndex the program will complain on an out of bounds. I want to keep only a set number of items in that array, so is it possible to do a insert or replace? i.e. if at index 0 it is empty do an insert, if there is an object already replace it?
Thanks
i think i agree with Hani Ibrahim. Since you said you only want to keep a set number of objects in the array. So how many you want?
// add these code when you initialize the array
int aSetNumber = 5;
_tempPhotosArray = [[NSMutableArray alloc] init];
for (int i = 0; i < aSetNumber; i++)
{
[_tempPhotosArray addobject: [NSNull null]];
}
i guess then you can do whatever you want, i don't know what exactly you want to do in this case, but i would check if the object in that position is NSNUll, if so, replace that, if not, i don't know what you want them
//use these code when you trying to insert the real object
if([[_tempPhotoArray objectAtIndex:anIndex] isKindOfClass: [NSNull class]])
{
//replace it here
}
As to why you are getting an error, what everyone else wrote is accurate, but....
The description of what you want doesn't match what an NSArray is. It sounds like you want a list of up to 5 items and never more than 5. It might be that if you try to add a 6th item the "oldest" goes away. Like a "recently opened" file history. You can make this type of functionality with an NSArray, but that's not what it is out of the box.
I would suggest making your own object class. I'm not going to write all the code for you, because this sounds suspiciously like programming homework, but I will point you in the correct direction.
FivePack <-- our class
NSArray *storage; <-- where we house the data
// a public method which lets you add things.
- (void)addItem:(id)item {
int indexOfLastItemInArrayToSave = 4;
if (storage.length < 4)
indexOfLastItemInArrayToSave = length-1;
NSRange range = NSMakeRange(0, indexOfLastItemInArrayToSave);
NSArray *temp = [storage subArrayWithRange:range];
// now create a new array with the first item being "item" that
// was passed in and the rest of the array being the contents of temp.
// Then save that to storage.
}
What you want to do with the data and writing something to get it from your new object is up to you, because I'm not sure how you want to do it.
There are no objects in the array when you initially created it, so there is nothing to replace.
Like this?
if([_tempPhotosArray count] > 0)
//replace object
else
//add object to array

Unrecognized Selector on BOOL Property

I've been racking my brain over this seemingly simple issue. I have a XYZObject class where I declare:
#property BOOL checked;
In my View Controller, I import the object and whenever I use 'checked', the app compiles fine but breaks at runtime wherever 'checked' is used, for example:
XYZObject *tableitem = [myDictionary[currentCategory] objectAtIndex:indexPath.row];
if (tableitem.checked) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
This was working fine until I deleted and re-added the XYZObject class, so I've been debugging under the assumption that something in the file path is what's screwing things up. But I can click on 'checked' in my VC and under Quick Help it shows the proper reference to XYZObject. This is the exact error:
[__NSCFString checked]: unrecognized selector sent to instance
EDIT/UPDATE:
With some help I've realized the issue is that when I changed my datasource from manual declaration in the ViewController, to importing a Plist, I completely scrapped my XYZObject and didn't account for it. Here is the original way I declared my dictionary:
XYZCategory *category1 = [[XYZCategory alloc]init]; category1.categoryArray = #"First Category"; [categoryArray addObject:category1];
XYZObject *object1 = [[XYZObject alloc]init]; object1.objectName = #"My String"; [objectArray addObject:object1];
myDictionary[category1.categoryArray] = objectArray;
When I switched to the Plist, the code changed to:
NSString *path = [[NSBundle mainBundle] pathForResource:#"myDictionaryPlist" ofType:#"plist"];
NSMutableDictionary *plistDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
objectArray = plistDictionary[#"First Category"];
myDictionary[category1.categoryArray] = objectArray;
And then for reference, XYZObject makes the following declarations:
#property NSString *objectName;
#property BOOL checked;
So the dictionary problem would be that I'm just pulling the direct strings for the objectArray, instead of a set of XYZObjects. I'm going to keep testing but I'm pretty sure I just have to re-define objectArray to be a set of objects based on what's pulled from the Plist.
But I also think that since I'm using the Plist now to create a dictionary (that is popped into a table where the Keys are sections and Values are rows), I can simplify things by removing the XYZCategory and XYZObject all together. Not sure if that's possible but I'm going to work towards it.
As the error message is suggesting, tableitem is actually a NSString, contrary to what you expect.
You are probably populating the dictionary in the wrong way.

NSMutableArray property initialization and updating

Suppose I have a #property that is an NSMutablearray that is to contain scores used by four objects. They will be initialized as zero and then updated during viewDidLoad and throughout operation of the app.
For some reason, I can't wrap my mind around what needs to be done, particularly at the declaration and initialization steps.
I believe this can be a private property.
#property (strong, nonatomic) NSMutableArray *scores;
#synthesize scores = _scores;
Then in viewDidLoad I try something like this but get an error. I just need help with syntax, I think. Or I'm missing something very basic.
self.scores = [[NSMutableArray alloc] initWithObjects:#0,#0,#0,#0,nil];
Is that an appropriate way to initialize it? Then how do I add (NSNumber *)updateValue to, say, the nth value?
Edit: I think I figured it out.
-(void)updateScoreForBase:(int)baseIndex byIncrement:(int)scoreAdjustmentAmount
{
int previousValue = [[self.scores objectAtIndex:baseIndex] intValue];
int updatedValue = previousValue + scoreAdjustmentAmount;
[_scores replaceObjectAtIndex:baseIndex withObject:[NSNumber numberWithInt:updatedValue]];
}
Is there a better way of doing this?
You are initializing in viewDidLoad, However you should do it in init.
These both are similar, and perfectly valid.
_scores = [[NSMutableArray alloc] initWithObjects:#0,#0,#0,#0,nil];
or,
self.scores=[[NSMutableArray alloc]initWithObjects:#0,#0,#0, nil];
Your last question... Then how do I add (NSNumber *)updateValue to, say, the nth value?
If you addObject: it will be added at last. You need to insertObject:atIndex: in your required index, and all following objects will shift to next indices.
NSInteger nthValue=12;
[_scores insertObject:updateValue atIndex:nthValue];
EDIT:
After your edit,
NSInteger previousValue = [[_scores objectAtIndex:baseIndex] integerValue];
NSInteger updatedValue = previousValue + scoreAdjustmentAmount;
[_scores replaceObjectAtIndex:baseIndex withObject:[NSNumber numberWithInt:updatedValue]];

Resources