While (not) loop freezes app - objective-c

My while loop doesn't seem to work. When loading this view, the app freezes.
When I delete the part of code, containing the while loop, the app won't freeze.
What I'm searching for is a piece of code that will cause that the same array is not chosen twice.
#interface ThirdViewController ()
#end
#implementation ThirdViewController
...
NSString * Answer = #"";
NSArray * RAMArray;
...
- (void)NewQuestion
{
NSString * PlistString = [[NSBundle mainBundle] pathForResource:#"Questions" ofType:#"plist"];
NSMutableArray * PlistArray = [[NSMutableArray alloc]initWithContentsOfFile:PlistString];
NSArray *PlistRandom = [PlistArray objectAtIndex: random()%[PlistArray count]];
while (![PlistRandom isEqual: RAMArray])
{
NSArray *PlistRandom = [PlistArray objectAtIndex: random()%[PlistArray count]];
}
RAMArray = PlistRandom;
...
}
- (void)Check:(NSString*)Choise
{
...
if ([Choise isEqualToString: Answer])
{
...
[self NewQuestion];
}
}
- (IBAction)AnsButA:(id)sender
{
UIButton *ResultButton = (UIButton *)sender;
NSString *Click = ResultButton.currentTitle;
[self Check:Click];
}

My guess is that because you re-declare PlistRandom within the while loop, the inner-declared variable may be out of scope at the point the while conditionis evaluated. Your problem I think is a scoping issue, just change the loop to this and see if that works:
while (![PlistRandom isEqual: RAMArray])
{
PlistRandom = [PlistArray objectAtIndex: random()%[PlistArray count]];
}

Related

nsmutable array not saving values

My array is not saving the values I put in it...
I am defining my nsmutablearray *arrayClientList in .h file
#interface StartupTableViewController : UIViewController<UITableViewDataSource, UITableViewDelegate>
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#property NSMutableArray *arrayClientList;
#property BOOL boolAddToClient;
//#property (strong, nonatomic) NSMutableArray *arrayAddClient;
#end
in .m file I am initializing like so
- (void)viewDidLoad {
[super viewDidLoad];
//initialize variables
self.arrayClientList = [[NSMutableArray alloc] init];
arraySelectedInformation = [[NSMutableArray alloc] init];
self.boolAddToClient = NO;
NSString *tstring = #"hello";
[self.arrayClientList addObject:tstring];
but then once I get to another method in this same class... the array is nil again. I must be doing something stupid for the array not to hold the values
-(void)viewDidAppear:(BOOL)animated{
//NSLog(#"appeared");
if (self.boolAddToClient) {
NSLog(#"add client to list");
self.boolAddToClient = NO;
[self.tableView reloadData];
}
else{
NSLog(#"startup");
}
}
I am trying to use it in another class
- (IBAction)buttonSubmit:(id)sender {
NSString *userDescription = [[NSString alloc] init];
NSString *userUsername = [[NSString alloc] init];
NSString *userPassword = [[NSString alloc] init];
userDescription = self.textfieldDescription.text;
userUsername = self.textfieldUserID.text;
userPassword = self.textfieldPW.text;
//check to make sure user filled out all fields
if (![userDescription isEqual:#""] && ![userUsername isEqual:#""] && ![userPassword isEqual: #""]){
NSLog(#"correct");
NSArray *arrayVC = self.navigationController.viewControllers;
StartupTableViewController *parentViewController = [arrayVC objectAtIndex:0];
parentViewController.boolAddToClient = YES;
NSMutableArray *arrayNewObjects = [[NSMutableArray alloc] initWithObjects:userDescription, userUsername, userPassword, nil];
NSMutableArray *tarray = parentViewController.arrayClientList;
[tarray addObject:arrayNewObjects];
[parentViewController.arrayClientList addObject:arrayNewObjects];
[self.navigationController popViewControllerAnimated:YES];
}
else{
NSLog(#"something missing");
}
}
Since I can't comment without rep, I must try with answer.
Try this:
In ViewDidLoad do alloc init with Strings you create in implementation and also change if block to this:
#implementation
{
NSString *userDescription;
NSString *userUsername;
NSString *userPassword;
}
-(void)viewDidLoad {
[super viewDidLoad];
NSString *userDescription = [[NSString alloc] init];
NSString *userUsername = [[NSString alloc] init];
NSString *userPassword = [[NSString alloc] init];
}
- (IBAction)buttonSubmit:(id)sender {
if (self.textfieldDescription.text.lenght != 0 && self.textfieldUserID.text.lenght != 0 && self.textfieldPW.text.lenght != 0) {
userDescription = self.textfieldDescription.text;
userUsername = self.textfieldUserID.text;
userPassword = self.textfieldPW.text;
....... and the rest
}
Please comment if it's not working, and I also think that you're not passing the informations right. Try searching an answer on how to pass arrays between TableViewControllers. Good Luck!

Something is wrong with singleton...unable adding a child because it is nil

I use a singleton the first time and I don't really know how to implement it...
Ok I need to explain some things:
In Hexagon.h (which inherits from CCNode) I want to create multiple sprites (here referred to as "hexagons"). However, they are not added to the scene yet. They are being added in the HelloWorldLayer.m class by calling Hexagon *nHex = [[Hexagon alloc]init]; . Is that correct ? Is it then iterating through the for loop and creating all hexagons or only one ?
Well anyways, I have a singleton class which has to handle all the public game state information but retrieving is not possible yet.For instance I cannot retrieve the value of existingHexagons, because it returns (null) objects. Either I set the objects wrongly or I am falsely retrieving data from the singleton. Actually, I would even appreciate an answer for one of these questions. Please help me. If something is not clear, please add a comment and I'll try to clarify it.
What I have right now is the following:
GameStateSingleton.h
#import <Foundation/Foundation.h>
#interface GameStateSingleton : NSObject{
NSMutableDictionary *existingHexagons;
}
+(GameStateSingleton*)sharedMySingleton;
-(NSMutableDictionary*)getExistingHexagons;
#property (nonatomic,retain) NSMutableDictionary *existingHexagons;
#end
GameStateSingleton.m
#import "GameStateSingleton.h"
#implementation GameStateSingleton
#synthesize existingHexagons;
static GameStateSingleton* _sharedMySingleton = nil;
+(GameStateSingleton*)sharedMySingleton
{
#synchronized([GameStateSingleton class])
{
if (!_sharedMySingleton)
[[self alloc] init];
return _sharedMySingleton;
}
return nil;
}
+(id)alloc
{
#synchronized([GameStateSingleton class])
{
NSAssert(_sharedMySingleton == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedMySingleton = [super alloc];
return _sharedMySingleton;
}
return nil;
}
-(id)init {
self = [super init];
if (self != nil) {
}
return self;
}
#end
Hexagon.m
-(CCSprite *)init{
if( (self=[super init])) {
NSString *mainPath = [[NSBundle mainBundle] bundlePath];
NSString *levelConfigPlistLocation = [mainPath stringByAppendingPathComponent:#"levelconfig.plist"];
NSDictionary *levelConfig = [[NSDictionary alloc] initWithContentsOfFile:levelConfigPlistLocation];
NSString *currentLevelAsString = [NSString stringWithFormat:#"level%d", 1];
NSArray *hexPositions;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
hexPositions = [[levelConfig valueForKey:currentLevelAsString] valueForKey:#"hexpositionIpad"];
}
else{
hexPositions = [[levelConfig valueForKey:currentLevelAsString] valueForKey:#"hexpositionIphone"];
}
NSString *whichType = [NSString stringWithFormat:#"glass"];
CGSize screenSize = [CCDirector sharedDirector].winSize;
if ([whichType isEqualToString:#"stone"]){
hexagon = [CCSprite spriteWithFile:#"octagonstone.png"];
}else if([whichType isEqualToString: #"glass"]){
hexagon = [CCSprite spriteWithFile:#"octagoncolored1.png"];
}else if([whichType isEqualToString: #"metal"]){
hexagon = [CCSprite spriteWithFile:#"octagonmetal.png"];
}
NSMutableDictionary *eHexagons =[[GameStateSingleton sharedMySingleton] getExistingHexagons];
for (int i=0;i < [hexPositions count];i++){
CGPoint location = CGPointFromString([hexPositions objectAtIndex:i]);
CGPoint nLocation= ccp(screenSize.width/2 + 68 * location.x,screenSize.height/2 + 39 * location.y);
NSString *aKey = [NSString stringWithFormat:#"hexagon%d",i];
hexagon =[CCSprite spriteWithFile:#"octagoncolored1.png"];
hexagon.position = nLocation;
[eHexagons setObject:hexagon forKey:aKey];
[self addChild:[eHexagons valueForKey:aKey] z:3];
[[GameStateSingleton sharedMySingleton]setExistingHexagons:eHexagons];
}
NSLog(#"these are the existinghexagons %#", existingHexagons);
//This returns a dictionary with one (null) object
}
return hexagon;
}
HelloWorldLayer.m -> -(id)init method
Hexagon *nHex = [[Hexagon alloc]init];
First of all, it returns null because the existingHexagons array has never been initialized in the first place. Go to the init function of your singleton and add:
existingHexagons = [[NSMutableArray alloc]init];
As for your For Loop question, I did not get it. I recommend making one StackOverflow question per query instead of putting two in one.

NSArray "out of scope"

.h
#property (nonatomic, retain) NSArray *m_plistData;
.m
#synthesize m_plistData;
- (void)viewDidLoad
{
NSArray *array = [[NSArray alloc]initWithObjects:#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14", nil];
m_plistData = array;
NSArray *nn = m_plistData;
[super viewDidLoad];
}
I use breakpoint and found array is normal,but m_plistData has no values,shows "out of scope",I can't understand why nn can get normal values
array is a local reference variable. It just lasts until viewDidLoad method. Now,
m_plistData = array;
The above statement doesn't make a deep copy. It is just a shallow copy.
- (void)viewDidLoad
{
NSArray *array = [[NSArray alloc]initWithObjects:#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14", nil];
m_plistData = array;
NSArray *nn = m_plistData;
[super viewDidLoad];
} // Both the array, nn references cease to exist after this point.
// So having references to it leads to run-time exception if used else where.
If your objective is have elements in m_plistData, directly do -
m_plistData = [[NSArray alloc]initWithObjects:#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14", nil];
Try something like:
m_plistData = [array copy];
Do it like this as you have a NSAarray:
if(array)
{
m_plistData = [NSArray arrayWithArray:array];
}

iOS - Storing groups of UILabels into a NSMutableArray

I'm creating UILabels dynamically in a for each loop. Every loop that is run creates 1-4 UILabels.
What I want is that I put these UILabels into my NSMutableArray and being able later to easy retrieve the data.
My original thought was to put these UILabels into a NSDictionary and use [dictGroupLabels setValue:uiLabel1 forKey:#"uiLabel1"] and then [dictGroupLabels setValue:uiLabel2 forKey:#"uiLabel2"] and so on. And then put this dictionary into my NSMutableArray for each loop. Later on I could access the values like UILabel *label = [[myArray objectAtIndex:0] valueForKey:#"uiLabel1"] BUT that unfortunately doesn't work since UILabels don't conform to the NSCopying protocol.
So with this in mind how would you solve this?
this question provided more information on what you are trying to accomplish. Since you know for a fact, the possible set of labels you are trying to create in each case, I would highly recommend using mutable dictionaries instead of arrays.
To illustrate, given the following hypothetical class definition:
#interface MyClass: NSObject {
NSMutableDictionary * _labelDict;
}
#property (nonatomic, retain) NSMutableDictionary * labelDict;
- ( void )methodA;
- ( void )methodB;
- (NSMutableDictionary *) labelsForRunLoop: (NSUInteger) loopIdx;
#end
You would have the following, hypothetical, class implementation:
#implementation MyClass
#synthesize labelDict = _labelDict;
- ( id ) init {
if( ( self = [ super init ] ) ) {
[self setLabelDict: [NSMutableDictionary dictionaryWithCapacity: 8]];
}
}
- ( void ) dealloc {
[ self.labelDict release ];
[ super dealloc ];
}
- ( void ) methodA {
for(NSUInteger i = 0; i < some index; i++) {
[self.labelDict setObject: [self labelsForRunLoop: i] forKey: [NSString stringWithFormat: #"%d", i]];
}
}
- ( void ) methodB {
// Locate the label you need to work with. Example based on this crude pseudo code
NSMutableDictionary * subDict = (NSMutableDictionary *) [self.labelDict objectForKey: #"0"];
UILabel * theLabel = (UILabel * ) [subDict objectForKey: #"UILabel.Z"];
theLabel.text = #"Label 1";
}
- (NSMutableDictionary *) labelsForRunLoop: (NSUInteger) loopIdx {
NSMutableDictionary * dictionary = [NSMutableDictionary dictionaryWithCapacity: 4] ;
[dictionary setObject: create-w-label forKey: #"UILabel.W"];
[dictionary setObject: create-x-label forKey: #"UILabel.X"];
[dictionary setObject: create-y-label forKey: #"UILabel.Y"];
[dictionary setObject: create-z-label forKey: #"UILabel.Z"];
return [dictionary retain];
}
#end
This is basically pseudo code and will not successfully compile. However it will serve as a good starting point. You probably want to store each label dictionary under some key that makes sense, instead of just using the loop's index. Hope this helps.
They don’t need to adhere to NSCopying to be added to an array. It sounds like you just need to do something like this:
NSMutableArray *mainArray = [NSMutableArray array];
for(int i = 0; i < 5; i++)
{
NSMutableArray *subArray = [[NSMutableArray alloc] initWithCapacity:5];
for(int j = 0; j < 4; j++)
{
UILabel *label = [[UILabel alloc] init];
// etc.
[subArray addObject:label];
[label release];
}
[mainArray addObject:subArray];
[subArray release];
}
// then, to get one of the labels:
UILabel *someSpecificLabel = [[mainArray objectAtIndex:2] objectAtIndex:1];

Getting array elements with valueForKeyPath

Is there any way to access an NSArray element with valueForKeyPath? Google's reverse geocoder service, for example, returns a very complex data structure. If I want to get the city, right now I have to break it into two calls, like this:
NSDictionary *address = [NSString stringWithString:[[[dictionary objectForKey:#"Placemark"] objectAtIndex:0] objectForKey:#"address"]];
NSLog(#"%#", [address valueForKeyPath:#"AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.LocalityName"]);
Just wondering if there's a way to shoehorn the objectAtIndex: call into the valueForKeyPath string. I tried a javascript-esque formulation like #"Placemark[0].address" but no dice.
Unfortunately, no. The full documentation for what's allowed using Key-Value Coding is here. There are not, to my knowledge, any operators that allow you to grab a particular array or set object.
Here's a category I just wrote for NSObject that can handle array indexes so you can access a nested object like this: "person.friends[0].name"
#interface NSObject (ValueForKeyPathWithIndexes)
-(id)valueForKeyPathWithIndexes:(NSString*)fullPath;
#end
#import "NSObject+ValueForKeyPathWithIndexes.h"
#implementation NSObject (ValueForKeyPathWithIndexes)
-(id)valueForKeyPathWithIndexes:(NSString*)fullPath
{
NSRange testrange = [fullPath rangeOfString:#"["];
if (testrange.location == NSNotFound)
return [self valueForKeyPath:fullPath];
NSArray* parts = [fullPath componentsSeparatedByString:#"."];
id currentObj = self;
for (NSString* part in parts)
{
NSRange range1 = [part rangeOfString:#"["];
if (range1.location == NSNotFound)
{
currentObj = [currentObj valueForKey:part];
}
else
{
NSString* arrayKey = [part substringToIndex:range1.location];
int index = [[[part substringToIndex:part.length-1] substringFromIndex:range1.location+1] intValue];
currentObj = [[currentObj valueForKey:arrayKey] objectAtIndex:index];
}
}
return currentObj;
}
#end
Use it like so
NSString* personsFriendsName = [obj valueForKeyPathsWithIndexes:#"me.friends[0].name"];
There's no error checking, so it's prone to breaking but you get the idea.
You can intercept the keypath in the object holding the NSArray.
In your case the keypath would become Placemark0.address... Override valueForUndefinedKey; look for the index in the keypath; something like this:
-(id)valueForUndefinedKey:(NSString *)key
{
// Handle paths like Placemark0, Placemark1, ...
if ([key hasPrefix:#"Placemark"])
{
// Caller wants to access the Placemark array.
// Find the array index they're after.
NSString *indexString = [key stringByReplacingOccurrencesOfString:#"Placemark" withString:#""];
NSInteger index = [indexString integerValue];
// Return array element.
if (index < self.placemarks.count)
return self.placemarks[index];
}
return [super valueForUndefinedKey:key];
}
This works really well for model frameworks e.g. Mantle.
Subclass NSArrayController or NSDictionaryController
Use NSArrayController for this purpose, because NSObjectController does not include NSArrayController's provided handling of changes to bound array elements. If you use this same code with NSObjectController instead, then using Cocoa Bindings with your NSObjectController instance will only set the (bound interface element's) value at the time of binding but will not receive the messages from array elements in return. By using NSObjectController for this purpose, the user interface will not continue to update even though the contentObject is updated. Simply use the same code with NSArrayController to also include proper support for arrays -- which is the matter at hand.
#import <Cocoa/Cocoa.h>
#interface DelvingArrayController : NSArrayController
#end
#import "DelvingArrayController.h"
#implementation DelvingArrayController
-(id)valueForKeyPath:(NSString *)keyPath
{
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"^(.+?)\\[(\\d+?)\\]$" options:NSRegularExpressionCaseInsensitive error:&error];
NSArray<NSString*> *components = [keyPath componentsSeparatedByString:#"."];
id currentObject = self;
for (NSUInteger i = 0; i < components.count; i++)
{
if (![components[i] isEqualToString:#""])
{
NSTextCheckingResult *check_result = [regex firstMatchInString:components[i] options:0 range:NSMakeRange(0, components[i].length)];
if (!check_result)
currentObject = [currentObject valueForKey:components[i]];
else
{
NSRange array_name_capture_range = [check_result rangeAtIndex:1];
NSRange number_capture_range = [check_result rangeAtIndex:2];
if (number_capture_range.location == NSNotFound)
currentObject = [currentObject valueForKey:components[i]];
else if (array_name_capture_range.location != NSNotFound)
{
NSString *array_name = [components[i] substringWithRange:array_name_capture_range];
NSUInteger array_index = [[components[i] substringWithRange:number_capture_range] integerValue];
currentObject = [currentObject valueForKey:array_name];
if ([currentObject count] > array_index)
currentObject = [currentObject objectAtIndex:array_index];
}
}
}
}
return currentObject;
}
//at some point... also override setValueForKeyPath :-)
#end
This code uses NSRegularExpression, which is for macOS 10.7+.
I leave it as an exercise for you to use the same approach to also override setValueForKeyPath, if you want write functionality.
Cocoa Bindings Example Usage
Say we want a little trivia game, with a window that shows a question and uses four buttons to display multiple-choice options. We have the questions and multiple-choice options as NSStrings in a plist, and also an NSNumber or optionally BOOL entries to indicate the correct answers. We want to bind the option buttons to options in the array, for each question also stored in an array.
Here is the example plist containing some trivia questions related to the game Halo. Notice that the options are located within nested arrays.
In this example, I use NSObjectController *stringsController as the controller for the entire plist file, and DelvingArrayController *triviaController as the controller for the trivia-related plist entries. You might simply use one DelvingArrayController instead, but I provide this for your understanding.
The trivia window is really simple, so I merely design it using Interface Builder in MainMenu.xib:
A subclass of NSDocumentController is used for showing the trivia window via an NSMenuItem added in Interface Builder. The instance of this subclass is also in the .xib, so if we want to use the interface elements in the .xib, we have to wait for the Application Delegate instance's - (void)applicationDidFinishLaunching:(NSNotification *)aNotification method or otherwise wait until the .xib has finished loading...
#import <Cocoa/Cocoa.h>
#import "MenuInterfaceDocumentController.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property IBOutlet MenuInterfaceDocumentController *PrimaryInterfaceController;
#end
#import "AppDelegate.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
#synthesize PrimaryInterfaceController;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
if ([NSApp mainMenu])
{
[PrimaryInterfaceController configureTriviaWindow];
}
}
#import <Cocoa/Cocoa.h>
#interface MenuInterfaceDocumentController : NSDocumentController
{
IBOutlet NSMenuItem *MenuItemTrivia; // shows the Trivia window
IBOutlet NSWindow *TriviaWindow;
IBOutlet NSTextView *TriviaQuestionField;
IBOutlet NSButton *TriviaOption1, *TriviaOption2, *TriviaOption3, *TriviaOption4;
}
#property NSObjectController *stringsController;
-(void)configureTriviaWindow;
#end
#import "MenuInterfaceDocumentController.h"
#interface MenuInterfaceDocumentController ()
#property NSDictionary *languageDictionary;
#property DelvingArrayController *triviaController;
#property NSNumber *triviaAnswer;
#end
#implementation MenuInterfaceDocumentController
#synthesize stringsController, languageDictionary, triviaController, triviaAnswer;
// all this happens before the MainMenu is available, and before the AppDelegate is sent applicationDidFinishLaunching
-(instancetype)init
{
self = [super init];
if (self)
{
if (!stringsController)
stringsController = [NSObjectController new];
stringsController.editable = NO;
// check for the plist file, eventually applying the following
languageDictionary = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"en" ofType:#"plist"]];
if (languageDictionary)
[stringsController setContent:languageDictionary];
if (!triviaController)
{
triviaController = [DelvingArrayController new];
[triviaController bind:#"contentArray" toObject:stringsController withKeyPath:#"selection.trivia" options:nil];
}
triviaController.editable = NO;
if (!triviaAnswer)
{
triviaAnswer = #0;
[self bind:#"triviaAnswer" toObject:triviaController withKeyPath:#"selection.answer" options:nil];
}
}
return self;
}
// if we ever do something like change the plist file to a duplicate plist file that is in a different language, use this kind of approach to keep the same trivia entry active
-(IBAction)changeLanguage:(id)sender
{
NSUInteger triviaQIndex = triviaController.selectionIndex;
if (sender == MenuItemEnglishLanguage)
{
if ([self changeLanguageTo:#"en" Notify:YES])
{
[self updateSelectedLanguageMenuItemWithLanguageString:#"en"];
if ([triviaController.content count] > triviaQIndex) // in case the plist files don't match
[triviaController setSelectionIndex:triviaQIndex];
}
else
[self displayAlertFor:CUSTOM_ALERT_TYPE_LANGUAGE_CHANGE_FAILED];
}
else if (sender == MenuItemGermanLanguage)
{
if ([self changeLanguageTo:#"de" Notify:YES])
{
[self updateSelectedLanguageMenuItemWithLanguageString:#"de"];
if ([triviaController.content count] > triviaQIndex)
[triviaController setSelectionIndex:triviaQIndex];
}
else
[self displayAlertFor:CUSTOM_ALERT_TYPE_LANGUAGE_CHANGE_FAILED];
}
}
-(void)configureTriviaWindow
{
[TriviaQuestionField bind:#"string" toObject:triviaController withKeyPath:#"selection.question" options:nil];
[TriviaOption1 bind:#"title" toObject:triviaController withKeyPath:#"selection.options[0]" options:nil];
[TriviaOption2 bind:#"title" toObject:triviaController withKeyPath:#"selection.options[1]" options:nil];
[TriviaOption3 bind:#"title" toObject:triviaController withKeyPath:#"selection.options[2]" options:nil];
[TriviaOption4 bind:#"title" toObject:triviaController withKeyPath:#"selection.options[3]" options:nil];
}
// this method is how you would manually set the value if you did not use binding:
-(void)updateTriviaAnswer
{
triviaAnswer = [triviaController valueForKeyPath:#"selection.answer"];
}
-(IBAction)changeTriviaQuestion:(id)sender
{
if (triviaController.selectionIndex >= [(NSArray*)triviaController.content count] - 1)
[triviaController setSelectionIndex:0];
else
[triviaController setSelectionIndex:(triviaController.selectionIndex + 1)];
}
-(IBAction)showTriviaWindow:(id)sender
{
[TriviaWindow makeKeyAndOrderFront:sender];
}
- (IBAction)TriviaOptionChosen:(id)sender
{
// tag integers 0 through 3 are assigned to the option buttons in Interface Builder
if ([sender tag] == triviaAnswer.integerValue)
[self changeTriviaQuestion:sender];
else
NSBeep();
}
#end
Summary of Sequence
NSObjectController *stringsController = [[NSObjectController alloc] initWithContent:[NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"en" ofType:#"plist"]]];
DelvingArrayController *triviaController = [DelvingArrayController new];
[triviaController bind:#"contentArray" toObject:stringsController withKeyPath:#"selection.trivia" options:nil];
NSNumber *triviaAnswer = #0;
[self bind:#"triviaAnswer" toObject:triviaController withKeyPath:#"selection.answer" options:nil];
// bind to .xib's interface elements after the nib has finished loading, else the IBOutlets are null
[TriviaQuestionField bind:#"string" toObject:triviaController withKeyPath:#"selection.question" options:nil];
[TriviaOption1 bind:#"title" toObject:triviaController withKeyPath:#"selection.options[0]" options:nil];
[TriviaOption2 bind:#"title" toObject:triviaController withKeyPath:#"selection.options[1]" options:nil];
[TriviaOption3 bind:#"title" toObject:triviaController withKeyPath:#"selection.options[2]" options:nil];
[TriviaOption4 bind:#"title" toObject:triviaController withKeyPath:#"selection.options[3]" options:nil];
// when the user chooses the correct option, go to the next question
if ([sender tag] == triviaAnswer.integerValue)
{
if (triviaController.selectionIndex >= [(NSArray*)triviaController.content count] - 1)
[triviaController setSelectionIndex:0];
else
[triviaController setSelectionIndex:(triviaController.selectionIndex + 1)];
}
Create methods that supports array for NSObject:
#interface NSObject(ArraySupported)
-(id)valueForKeySupportedArray:(NSString*)path;
-(id)valueForKeyPathSupportedArray:(NSString*)fullPath;
#end
#implementation NSObject(ArraySupported)
-(id)valueForKeySupportedArray:(NSString*)path {
id value = nil;
if ([self isKindOfClass:[NSArray class]]) {
NSArray *array = (NSArray *)self;
NSUInteger index = path.integerValue;
if (index >= 0 && index < array.count) {
value = array[index];
}
} else {
value = [self valueForKey:path];
}
return value;
}
-(id)valueForKeyPathSupportedArray:(NSString*)fullPath {
NSArray* parts = [fullPath componentsSeparatedByString:#"."];
id value = self;
for (NSString* part in parts) {
value = [value valueForKeySupportedArray:part];
if (value == nil) {
break;
}
}
return value;
}
#end
How to use:
NSObject *object = #{#"Placemark":#[#{#"address":#"..."}]};
NSString *address = [object valueForKeyPathSupportedArray:#"Placemark.0.address"];
// address = "..."