Values changing automatically - objective-c

Hi together I have a Problem:
I read out the actual Values of the Playing media song, and add it to a dictionary.
And then into an Array. If I add a new entry pressing the Button again, all Values of all entrys changing to the same.
Do you have an Idea why?
Here my Code:
- (IBAction)ActionButtonLU:(id)sender
{
MPMediaItem *currentItem = [musicPlayer nowPlayingItem];
NSString *titleString = [currentItem valueForProperty:MPMediaItemPropertyTitle];
NSString *artistString = [currentItem valueForProperty:MPMediaItemPropertyArtist];
NSString *albumString = [currentItem valueForProperty:MPMediaItemPropertyAlbumTitle];
if (titleString == nil) {
titleString = #"";
}
if (artistString == nil) {
artistString = #"";
}
` `if (albumString == nil) {
albumString = #"";
}
[_dictCat1 setObject:titleString forKey:#"Titel"];
[_dictCat1 setObject:artistString forKey:#"Artist"];
[_dictCat1 setObject:albumString forKey:#"Album"];
[_countCat1 addObject:_dictCat1];
[musicPlayer skipToNextItem];
}

You are modifying the same dictionary, _dictCat1, in every call. You need to create a dictionary locally and add that to _countCat1.
[_countCat1 addObject:#{ #"Title": titleString,
#"Artist": artistString,
#"Album": albumString }];

When you add an object to a collection (NSArray, NSDictionary, NSSet etc, including mutable counterparts), the collection doesn't copy it. With [_countCat1 addObject:_dictCat1]; you keep adding the same object to _countCat1 over and over. You need to create new NSDictionary and add that to _countCat1 collection.

Related

Parsing a JSON of unknown Structure Objective-C

I am trying to parse this json and am not sure of parsing this because the keys like "vis-progress-control" might change and I am trying to build a general code which parses this type of a json. I am sure that "type" key will be present in the json structure.
NSDictionary *dict = [sData JSONValue];
NSArray *items = [dict valueForKeyPath:#"assets"];
NSLog(#"%#", items);
for (NSString *key in [[dict objectForKey:#"assets"]allKeys]) {
for (NSString *subKey in [[[dict objectForKey:#"assets"] objectForKey:key]allKeys]) {
NSLog(#"Value at subkey:%# is %#\n",subKey,[[[dict objectForKey:#"assets"]objectForKey:key]objectForKey:subKey]);
}
}
I am using the SBJson Library on Github. My actual issue is How do I access "direction", "degrees" etc when I do not know the "vjs-progress-holder" key?
I have also a widgets array nested within a widgets array. How do I get these values as well?
Read about tree traversal. Sounds like you want to traverse the "tree" of nodes and when you find a particular leaf value you will then traverse back up one level and know the parent is the container you want.
So the idea is that once it's parsed from the JSON, forget it's JSON because now it's just a tree in arrays and dictionaries. Traverse it by getting all the keys in the dictionary via allKeys (returns an array of keys) and then iterate through them getting the associated values (using something like (psuedo code):
for ( NSString * key in allkeysarray) {
NSString * val = [dict objectForKey: key];
if ( [val isEqualToString: #"gradient"] )
{
// now you know that this dictionary is the one you're looking for so you can maybe break out of this loop and just use the keys you know reference these values.
break;
}
}
hopefully that's enough to get you going.
Assuming I'm understanding your objective here, it seems like you want to do something like this?
NSDictionary *outerDict = [sData JSONValue];
NSDictionary *assets = outerDict[#"assets"];
for (NSDictionary *asset in [assets allValues]) {
NSString *type = asset[#"type"];
if ([type isEqualToString:#"gradient"]) {
float degrees = [asset[#"degrees"] floatValue];
// and read whatever other values you need for gradients
}
if ([type isEqualToString:#"image"]) {
// and read the appropriate values for images here...
}
}
So I'll make a different assumption here, which is that you want an array of gradients. So then that looks basically like this:
NSDictionary *outerDict = [sData JSONValue];
NSDictionary *assets = outerDict[#"assets"];
NSMutableArray *gradients = [NSMutableArray array];
for (NSDictionary *asset in [assets allValues]) {
NSString *type = asset[#"type"];
if ([type isEqualToString:#"gradient"]) {
// Add this asset to the list of gradients:
[gradients addObject:asset];
}
if ([type isEqualToString:#"image"]) {
// do something similar for images
}
}
Then, after having done that, if you would like to read the "degrees" field for the 4th gradient, for example, you will find it at gradients[3][#"degrees"]

iOS adding object to an array from another array base on 3rd array value

EDIT: All array used in my project are NSMutableArray class
An overview of what I want to do is from my selectClueView, user can select a number from 3-10. Which represent the number of clue they will play. It will then generate list of random number between 0 and the objectArray.count and add the NSNumber into another array known as dataArray. Everything is working fine including prepareForSegue which transfer SelectClueViewController.dataArray to GamePageViewController.clueToSelect
However, I am stuck with loading data into the new array ds, from an array that hold all the object allDataObject. I am fairly new to iOS and because I had a working function in c#, I tried to replicate it in objective-C, unfortunately it seems that I can't replicate it fully.
In short, I'm trying to add data from allDataObject array into ds array with NSNumber values from cluesToSelect array.
Below are the coding which are used. Any help to fix the issue would be much appreciated. If there are any more information that I should give, please let me know.
SelectClueViewController.m
- (IBAction)onGoPress:(id)sender {
[self chooseNumber];
NSLog(#"Array got %d numbers",dataArray.count);
}
-(void)chooseNumber
{
[dataArray removeAllObjects];
maxCount = [numberOfClues.text intValue];
int count = 0;
do {
NSInteger rdmNumber = arc4random()%objectArray.count;
if (![dataArray containsObject:[NSNumber numberWithInt:rdmNumber]])
{
NSNumber* number = [NSNumber numberWithInt:rdmNumber];
[dataArray addObject:number];
count++;
NSLog(#"random no - %d",rdmNumber);
}
} while (count < maxCount);
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"sendNumber"]) {
GamePageViewController *gpViewController = [segue destinationViewController];
gpViewController.cluesToSelect = self.dataArray;
NSLog(#"Success");
}
}
GamePageViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
daoDS = [[ClueDataDAO alloc]init];
self.allDataObject = daoDS.PopulateDataSource;
NSLog(#"%d",cluesToSelect.count);
[self fillDataSample];
//for keyboard
self.answer.delegate = self;
}
-(void)fillDataSample
{
int count = 0;
do {
// [self.ds addObject:[allDataObject objectAtIndex:[[cluesToSelect objectAtIndex:count]intValue] ]];
ds = [[NSMutableArray alloc]init];
currentClueData = [[ClueData alloc]init];
int firstIndex = [[cluesToSelect objectAtIndex:count]intValue];
currentClueData = [allDataObject objectAtIndex:firstIndex];
[ds addObject:currentClueData];
count++;
} while (count < cluesToSelect.count);
NSLog(#"ds got %d object",ds.count);
}
EDIT:
I am now able to make it add in object into the ds array, unfortunately it only add once. Can someone look at my fillDataSample function?
In the line you point at at the beginning :
[self.ds addObject:[allDataObject objectAtIndex:[cluesToSelect objectAtIndex:count]]];
Just a wild guess, but [cluesToSelect objectAtIndex:count] returns an object. You're trying to pass that to another objectAtIndex:, which takes an int as argument. I'm guessing the error could come from that. You could try using .intValue if it's a number.
Edit :
Here's something more readable, considering cluesToSelect and allDataObject contain only NSNumbers :
int firstIndex = [[cluesToSelect objectAtIndex:count] intValue];
int secondIndex = [[allDataObject objectAtIndex: firstIndex] intValue];
[self.ds addObject:secondIndex];
As per our discussion,
Kindly check this one:
[self.ds addObject:[allDataObject objectAtIndex:[[cluesToSelect objectAtIndex:count] intValue]]];
will do your work.
The reason as to why it didn't work for my new method of loading object from allClueData to a class object currentClueData to ds was because I did not nil and alloc init the class object after adding it to ds. The object were unable to overwrite their own value like they do in other language. (which was probably why I'm wrecking my brain for doing it in objective C) But after adding in nil the object and casting alloc and init, its working great now. thanks all :)
-(void)fillDataSample
{
int count = 0;
currentClueData = [[ClueData alloc]init];
ds = [[NSMutableArray alloc]init];
do {
// [self.ds addObject:[allDataObject objectAtIndex:[[cluesToSelect objectAtIndex:count]intValue] ]];
int firstIndex = [[cluesToSelect objectAtIndex:count]intValue];
currentClueData = [allDataObject objectAtIndex:firstIndex];
[ds addObject:currentClueData];
currentClueData =nil;
currentClueData = [[ClueData alloc]init];
count++;
} while (count < cluesToSelect.count);
NSLog(#"ds got %d object",ds.count);
}

Array Issue in Objective C Xcode

I have a problem with my algorithm for calculating a sore. The user enters a word into the UITextField, and if the word matches a string in the array (#"The Word") the int 'score' will be added by 1.
Then the int score is set as a label as the user gets a word right. (DISPLAYING THE SCORE)
THE PROBLEM, a user can just keep on entering the same word over and over again and the score will keep going up by one. IS there a command for knowing if a word has already been entered, so you can only use the word once.
The Code
NSArray *scoreArray1 = [NSArray arrayWithObjects:
#"Word 1", #"Word 2", #"Word 3", nil];
NSString *inputtwo =_EnterNameText.text;
BOOL isItright = NO;
for(NSString *possible in scoreArray1) {
if([inputtwo isEqual:possible] ) {
isItright = YES;
break;
}
}
if(isItright) {
static int myInt = 0;
myInt++;
NSString *score = [NSString stringWithFormat:#"%d", myInt];
[_scorelabel setText:score];
}
UPDATE!!!!!!
NSArray *scoreArray1 = [NSArray arrayWithObjects:
#"Alan Shearer", #"Shearer", #"Andrew Cole", #"Andy Cole", #"Cole", #"Thierry Henry", #"Henry", #"Robbie Fowler", #"Fowler", #"Frank Lampard", #"Lampard", #"Michael Owen", #"Owen", nil];
NSSet *set2 = [NSSet setWithArray:scoreArray1];
NSString *inputtwo =_EnterNameText.text;
BOOL isItright = NO;
for(NSString *possible in set2) {
if([inputtwo isEqual:possible] ) {
isItright = YES;
break;
}
}
if(isItright) {
static int myInt = 0;
myInt++;
NSString *score = [NSString stringWithFormat:#"%d", myInt];
[_scorelabel setText:score];
}
HOWEVER NOW THE APP DOES NOT WORK, IT CRASHES, any suggestions?
Why don't you keep a second Array where you store the given (correct) answers.
Whit this you can just do a contains inside your if....problem solved.
a second option is not to put string in your array but "Answer" Objects, that have a field that you can flag as already used.
You could just create an NSMutableSet and put a copy of the word into there whenever one is entered. Then you just need to check if the word exists in the set before incrementing the score.
I'm suggesting a set because it uses hashed access, so lookups are fast. Also, if you add the same string more than once, the set will still only have one reference to the string.
Actually, if you have an array of "legal" words, the way to go is to simply remove each word as it's called out, until the array gets to be zero entries long.
NSMutableArray* scoreArrayCopy = [NSMutableArray arrayWithArray:scoreArray];
int originalCount = scoreArrayCopy.count;
...
while (scoreArrayCopy.count > 0) {
NSString* guess = <get next guess>;
[scoreArrayCopy removeObject:guess];
score = originalCount - scoreArrayCopy.count;
}
(If you have a lot of words things would be more efficient if you used an NSMutableSet instead of an NSMutableArray, but the logic would be the same.)

NSFormatter for BOOL

I have set up my simple Xcode project with a table that is binded to an array controller. It works fine if the array controller is full of entities with a string attribute. However I want to change the attribute to a BOOL and have the table show the string "true" or "false" based on the BOOL.
I have overrided the following two methods from NSFormatter:
-(NSString*) stringForObjectValue:(id)object {
//what is the object?
NSLog(#"object is: %#", object);
if(![object isKindOfClass: [ NSString class ] ] ) {
return nil;
}
//i'm tired....just output hello in the table!!
NSString *returnStr = [[NSString alloc] initWithFormat:#"hello"];
return returnStr;
}
-(BOOL)getObjectValue: (id*)object forString:string errorDescription:(NSString**)error {
if( object ) {
return YES;
}
return NO;
}
So the table gets populated with "hello" if the attribute is a string however if I switch it to a boolean, then the table gets populated with lots of blank spaces.
I don't know if this helps but on the line where I'm outputting the object, it outputs __NSCFString if the attribute is a string and "Text Cell" if I switch the attribute to a boolean. This is something else I don't understand.
Ok, it's not 100% clear what you're trying to do from the code, but first things first - BOOL is not an object, it's basically 0 or 1, so to place BOOL values into an array, you're probably best off using NSNumber:
NSNumber *boolValue = [NSNumber numberWithBool:YES];
and placing these into your array. Now you want to change your method:
-(NSString*) stringForObjectValue:(id)object {
NSNumber *number = (NSNumber *)object;
if ([number boolValue] == YES)
return #"true";
else
return #"false";
}
There's a few things here - for example, you want to avoid passing around id references if you can (if you know all your objects in the NSArray are NSNumber, you shouldn't need to).

Objective C : Writing into a Plist

Even though i know there are at least 2 or 3 topics with this name, i didnt find a proper answer so far to my problem :
I want to edit a Plist (which has been created by zwoptex (image/animations program)) in order to divide every number in it by 2.
So in my plist i do have some keys like "spriteOffset" with {{182, 160}, {58,75}} or {192, 165} as value. Those are NSStrings, and i just want to modify the numbers so i need to check if there's a "{" or a space or such, then casting the number.
The thing is i don't really know how to do it.....
Also, it seems that i'm missing something with my plist management. I've put some NSLogs for displaying every of those strings in my plist, but.... nothing gets displayed...
So here is my code :
-(void)DivideValues
{
for(NSString * plistName in plistSubpathsByName)
{
NSMutableDictionary* infoDict = [NSMutableDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:#"%#.plist",plistName]];
for(NSDictionary * sprite in [infoDict objectForKey:#"frames"])
{
for(NSString * string in [infoDict objectForKey:#"spriteColorRect"])
{
NSLog(#"%#",string);
}
for(NSString * string in [infoDict objectForKey:#"spriteOffset"])
{
NSLog(#"%#",string);
}
for(NSString * string in [infoDict objectForKey:#"spriteSize"])
{
NSLog(#"%#",string);
}
for(NSString * string in [infoDict objectForKey:#"spriteSourceSize"])
{
NSLog(#"%#",string);
}
for(NSString * string in [infoDict objectForKey:#"textureRect"])
{
NSLog(#"%#",string);
}
}
}
}
Thanks for any response, and i wish you all good luck for your career/passion
First of all, you should replace [infoDict objectForKey:#"spriteColorRect"] with [sprite objectForKey:#"spriteColorRect"], since the sprite is probably the dict containing further information.
You don't see any logs because -objectForKey: returns nil for a key that does not exist.
For changing the values, you might try to create a CGPoint or CGRect from the string, then changing it and finally converting it back to a string. (CGPointFromNSString() and NSStringFromCGPoint)
To save the modified version of your dictionary use NSDictionary's -writeToFile:atomically:.
The reason you example logs nothing is most likely because your inner for..in loops are probably looking in the wrong dictionary: the outer loop gets a dictionary sprite, so shouldn't the inner loops be looking at keys in that dictionary?
If you want to read a property list in, change some values in it, and write the same property list back out, you might find it useful to look at the NSPropertyListSerialization class -- it lets you quickly get a structure of mutable arrays/dictionaries from plist data, so you can iterate into them however you'd like to change values within, then serialize the whole thing back to data again. (If you use dictionaryWithContentsOfFile: you'll get a mutable dictionary, but all the containers within it will be immutable, so you'd have to do mutableCopy and swizzle contents all over the place during your iteration.)
No time to write up more detail at the moment, but I might edit the answer later if looking up the docs for NSPropertyListSerialization doesn't help you.
Ok I did succeed so if anyone is interested here is the code :
-(void)DivideValues
{
for(NSString * xflName in [xflSubpathsByName objectEnumerator]){
NSMutableDictionary* infoDict = [NSMutableDictionary dictionaryWithContentsOfFile:[sourceFolder stringByAppendingPathComponent:xflName]];
NSDictionary * dictionary = [infoDict objectForKey:#"frames"];
NSMutableDictionary * mutabledictionary = [[dictionary mutableCopy] autorelease];
for(NSString * pngFileName in dictionary) {
NSDictionary * sprite = [dictionary objectForKey:pngFileName];
NSLog(pngFileName);
NSMutableDictionary * mutablesprite = [[sprite mutableCopy] autorelease];
NSString * newstring = [self castSpriteRect:[sprite objectForKey:#"spriteColorRect"]];
[mutablesprite setObject:newstring forKey:#"spriteColorRect"];
newstring = [self castSprite:[sprite objectForKey:#"spriteOffset"]];
[mutablesprite setObject:newstring forKey:#"spriteOffset"];
newstring = [self castSprite:[sprite objectForKey:#"spriteSize"]];
[mutablesprite setObject:newstring forKey:#"spriteSize"];
newstring = [self castSprite:[sprite objectForKey:#"spriteSourceSize"]];
[mutablesprite setObject:newstring forKey:#"spriteSourceSize"];
newstring = [self castSpriteRect:[sprite objectForKey:#"textureRect"]];
[mutablesprite setObject:newstring forKey:#"textureRect"];
[mutabledictionary setObject:mutablesprite forKey:pngFileName];
}
[infoDict setObject:mutabledictionary forKey:#"frames"];
[infoDict writeToFile:[sourceFolder stringByAppendingPathComponent:xflName] atomically:NO];
}
if(!cancelling)
++digestStage;
else
digestStage = End;
}
-(NSString *)castSprite:(id)obj{
CGPoint point = NSPointFromString((NSString *)obj);
int i = (int)point.x%2 == 0 ?(int)point.x/2:1+(int)point.x/2;
int j = (int)point.y%2 == 0 ?(int)point.y/2:1+(int)point.y/2;
NSString * res = [NSString stringWithFormat:#"{%d, %d}",i,j];
return res;
}
-(NSString *)castSpriteRect:(id)obj{
CGRect point = NSRectFromString((NSString *)obj);
int i = (int)point.origin.x%2 == 0 ?(int)point.origin.x/2:1+(int)point.origin.x/2;
int j = (int)point.origin.y%2 == 0 ?(int)point.origin.y/2:1+(int)point.origin.y/2;
int y = (int)point.size.width%2 == 0 ?(int)point.size.width/2:1+(int)point.size.width/2;
int x = (int)point.size.height%2 == 0 ?(int)point.size.height/2:1+(int)point.size.height/2;
NSString * res = [NSString stringWithFormat:#"{{%d, %d}, {%d, %d}}",i,j,y,x];
return res;
}