Set NSMutableArray to the new NSMutableArray - objective-c

#implementation RightViewController{
NSMutableArray * tableArray;
}
#synthesize tableView;
- (void)viewDidLoad {
[super viewDidLoad];
tableArray = [[NSMutableArray alloc] init];
}
- (void)refreshTable: (NSMutableArray*)resultsArray{
[tableArray setArray:resultsArray];
NSLog(#"resultsArray : %d : %d", [tableArray count] , [resultsArray count]);
[self.tableView reloadData];
}
It shows me : resultsArray : 0 : 78
Why can't I set information to this array?
I do call refreshTable from another controller like this:
[[[RightViewController alloc] init] refreshTable:resultsArray];
Updated:
tableArray = [NSMutableArray arrayWithArray:resultsArray];
worked for me.
After that i do
- (IBAction)reloadTableButton:(id)sender {
[self refreshTable:tableArray];
}
and it's shows me : resultsArray : 0 : 0
Why is tableArray array empty?

Try setting your tableArray variable like so :
tableArray = [NSMutableArray arrayWithArray:resultsArray];
You might not be retaining your mutable array I would suggest making a #property for your tableArray. Place this before your #synthesize
#property (retain, nonatomic) NSMutableArray *tableArray;

Option 1:
- (void)viewDidLoad {
[super viewDidLoad];
//don't do anything with the array here!
//refreshTable may well be called before the view is loaded
}
- (void)refreshTable: (NSMutableArray*)resultsArray{
if (!self.tableArray) // if it does not exist, then create it on the fly.
self.tableArray = [[NSMutableArray alloc] init];
[tableArray setArray:resultsArray];
[self.tableView reloadData];
}
Option 2:
- (MyClass*) init {
self = [super init];
if (self) {
self.tableArray = [[NSMutableArray alloc] init];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
//don't do anything with the array here!
}
- (void)refreshTable: (NSMutableArray*)resultsArray{
[tableArray setArray:resultsArray]; // you can be sure that init was invoked earlier.
[self.tableView reloadData];
}
Option 3:
- (void)viewDidLoad {
[super viewDidLoad];
//don't do anything with the array here!
}
- (void)refreshTable: (NSMutableArray*)resultsArray{
self.tableArray = [NSMutalbeArray arrayWithArray:resultsArray]; //This creates a new array as a copy of resultsArray and assigns it to tableArray. No need to initialize anything.
[self.tableView reloadData];
}

i believe it should work
RightViewController *controller = [[RightViewController alloc] init];
[controller refreshTable:resultsArray];

#implementation RightViewController{
NSMutableArray * tableArray;
}
#property (strong, nonatomic) NSMutableArray *tableArray; //use the keyword "retain" instead of "strong" in below iOS 5
#synthesize tableView;
#synthesize tableArray;
- (void)viewDidLoad {
[super viewDidLoad];
self.tableArray = [[NSMutableArray alloc] init];
}
- (void)refreshTable: (NSMutableArray*)resultsArray{
self.tableArray = resultsArray;
NSLog(#"resultsArray : %d : %d", [tableArray count] , [resultsArray count]);
[self.tableView reloadData];
}

Related

Object loses reference

I have a IKImageBrowserView which has its own controller - BrowserController.h + .m
#interface BrowserController : NSWindowController{
NSMutableArray *_images;
}
#property (strong,nonatomic) IBOutlet IKImageBrowserView *imageBrowser;
-(void)addMultipleImages;
When I run the app for the first time, the staring image loads, but when I click a button to add other images and call a method from another class I don't get any results. I have noticed that my _imageBrowser loses the reference and becomes nil.
How could I solve this issue?
AppDelegate.m
#import "AppDelegate.h"
#import "BrowserController.h"
#implementation AppDelegate{
BrowserController *imageBrowserController;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
imageBrowserController = [BrowserController sharedManager];
}
- (IBAction)doSomething:(id)sender {
[imageBrowserController addMultipleImages];
}
#end
BrowserController.m
#import "BrowserController.h"
#interface myImageObject : NSObject
{
NSString *_path;
}
#end
#implementation myImageObject
/* our datasource object is just a filepath representation */
- (void)setPath:(NSString *)path
{
if(_path != path)
{
_path = path;
}
}
/* required methods of the IKImageBrowserItem protocol */
#pragma mark -
#pragma mark item data source protocol
/* let the image browser knows we use a path representation */
- (NSString *)imageRepresentationType
{
return IKImageBrowserPathRepresentationType;
}
/* give our representation to the image browser */
- (id)imageRepresentation
{
return _path;
}
/* use the absolute filepath as identifier */
- (NSString *)imageUID
{
return _path;
}
#end
#interface BrowserController ()
#end
#implementation BrowserController
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
}
- (void)awakeFromNib{
myImageObject *p;
p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[self updateDataSource];
}
- (void)updateDataSource{
[_imageBrowser reloadData];
}
-(NSUInteger) numberOfItemsInImageBrowser:(IKImageBrowserView *)aBrowser{
return [_images count];
};
-(id)imageBrowser:(IKImageBrowserView *)aBrowser itemAtIndex:(NSUInteger)index{
return [_images objectAtIndex:index];
};
- (void)updateDatasource
{
[_imageBrowser reloadData];
}
- (void)addMultipleImages{
myImageObject *p;
p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[_images addObject:p];
[_images addObject:p];
[_imageBrowser reloadData];
}
+ (id)sharedManager {
static BrowserController *sharedMyManager = nil;
#synchronized(self) {
if (sharedMyManager == nil)
sharedMyManager = [[self alloc] init];
}
return sharedMyManager;
}
#end
There is some confusion as to the name of the class where you mention it's called ImageBrowserController at the start of your question and it looks like it's called BrowserController at the end of your question.
The issue is that you allocate _images in awakeFromNib which is never called given the class is created via the singleton pattern (sharedInstance) and not loaded from a .nib file.
Therefore move the code from awakeFromNib into init and dump awakeFromNib:
BrowserController.m:
- (id)init
{
self = [super init];
if (self) {
myImageObject *p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[self updateDataSource];
}
return self;
}
Further confusion: you have implemented an initWithFrame method. Why?

filterContentForSearchText and shouldReloadTableForSearchString not called (UITableView & UISearchBar)

I'd like to implement an incremental search of words.
I implemented like below, but filterContentForSearchText and shouldReloadTableForSearchString methods are not called. Why?
WordsIndexViewController.h
#import <UIKit/UIKit.h>
#interface WordsIndexViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate, UISearchBarDelegate>
#end
WordsIndexViewController.m
#import "WordsIndexViewController.h"
#import "WordsShowViewController.h"
#import "Word.h"
#interface WordsIndexViewController ()
#property (strong, nonatomic) UITableView *tableView;
#property (strong, nonatomic) NSArray *words;
#property (strong, nonatomic) NSMutableArray *searchedWords;
#end
#implementation WordsIndexViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
_words = [[app models] objectForKey:#"words"];
_searchedWords = [NSMutableArray arrayWithArray:#[]];
UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 480) style:UITableViewStylePlain];
tableView.delegate = self;
tableView.dataSource = self;
_tableView = tableView;
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 44)];
searchBar.delegate = self; // needed?
[searchBar sizeToFit]; // needed?
UISearchDisplayController *searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
searchDisplayController.delegate = self;
searchDisplayController.searchResultsDelegate = self;
searchDisplayController.searchResultsDataSource = self;
_tableView.tableHeaderView = searchBar;
[self.view addSubview:_tableView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - related to UITableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"START numberOfRowsInSection");
NSArray *words = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
words = _searchedWords;
} else {
words = _words;
}
return [words count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"START cellForRowAtIndexPath");
NSArray *words = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
words = _searchedWords;
} else {
words = _words;
}
UITableViewCell *cell = [[UITableViewCell alloc] init];
Word *word = words[indexPath.row];
cell.textLabel.text = word.spelling;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray *words = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
words = _searchedWords;
} else {
words = _words;
}
WordsShowViewController *controller = [[WordsShowViewController alloc] initWithNibName:#"ViewController" bundle:nil];
controller.word = (Word *)words[indexPath.row];
[self.navigationController pushViewController:controller animated:YES];
}
#pragma mark - related to Search Bar
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
NSLog(#"Query: %#", searchString); // NOT CALLED...
[self filterContentForSearchText:searchString
scope:[
[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]
]
];
return YES;
}
- (void)filterContentForSearchText:(NSString*)searchString scope:(NSString*)scope {
NSLog(#"START filterContentForSearchText"); // NOT CALLED...
[_searchedWords removeAllObjects];
for(Word *word in _words) {
NSRange range = [word.spelling rangeOfString:searchString options:NSCaseInsensitiveSearch];
if(range.length > 0) {
[_searchedWords addObject:word];
}
}
}
#end
Word.h
#import <Foundation/Foundation.h>
#interface Word : NSObject
#property (strong, nonatomic) NSString *identifier;
#property (strong, nonatomic) NSString *spelling;
- (id)initWith:(NSString *)spelling;
#end
Word.m
#import "Word.h"
#implementation Word
- (id)initWith:(NSString *)spelling {
self = [super init];
if (!self) {
return nil;
}
CFUUIDRef uuid = CFUUIDCreate(NULL);
_identifier = (NSString *)CFBridgingRelease(CFUUIDCreateString(NULL, uuid));
CFRelease(uuid);
_spelling = spelling;
return self;
}
#end
Regards,
Finally, I solved the question myself.
To solve it, I called shouldReloadTableForSearchString in textDidChange then the incremental search worked. A diff belows:
WordsIndexViewController.m
## -16,6 +16,7 ##
#property (strong, nonatomic) NSArray *words;
#property (strong, nonatomic) NSMutableArray *searchedWords;
+#property (strong, nonatomic) UISearchDisplayController *displayController;
#end
## -52,6 +53,7 ##
searchDisplayController.searchResultsDelegate = self;
searchDisplayController.searchResultsDataSource = self;
_tableView.tableHeaderView = searchBar;
+ _displayController = searchDisplayController;
[self.view addSubview:_tableView];
}
## -132,5 +134,9 ##
}
}
}
+- (void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *) searchText {
+ [self searchDisplayController:_displayController shouldReloadTableForSearchString:searchText];
+}
#end
Thanks,
Make sure you create an iVar for the UISearchDisplayController in your header file.
If you create an UISearchDisplayController using:
UISearchDisplayController* searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchField contentsController:self];
it will get released by ARC, it will not call any delegate methods and when you'll call self.searchDisplayController (the UIViewController's property) it will be nil.
So, the fix is:
In your header (.h) file:
#interface MenuViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UISearchDisplayDelegate> {
UISearchDisplayController* searchDisplayController;
UISearchBar *searchField;
UITableView* tableView;
NSArray* searchResults;
}
and in the implementation (.m) file:
searchField = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 49)];
searchField.delegate = self;
searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchField contentsController:self];
searchDisplayController.delegate = self;
searchDisplayController.searchResultsDataSource = self;
searchDisplayController.searchResultsDelegate = self;
tableView.tableHeaderView = searchField;
tableView.contentOffset = CGPointMake(0, searchField.frame.size.height);
When implemented like that, you can call both self.searchDisplayController and searchDisplayController in the rest of your code.

Create a UIPageViewController with a UITableView in each page

I have been reading things on stack overflow for quite a while now but this is my first post, only because it is the first time I have a problem that no one else seems to have fixed yet!
Ok, down to business. It should be a simple matter to put UITableViews inside a UIPageView but I am having difficulties. I have a ViewController and contentViewController. I am using .xibs instead of storyboarding. The contentViewController.xib is a Table View and the ViewController.xib is a View. I am only focusing on iPhone. The UITableView is connected to dataSource, delegate, and Referencing Outlet named theTableView.
The project builds but when I run it I get the following error message:
2013-03-17 16:14:23.026 pageApp[775:c07] *** Assertion failure in -[UITableView layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2380.17/UIView.m:5776
2013-03-17 16:14:23.028 pageApp[775:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super.'
*** First throw call stack:
(0x1c93012 0x10d0e7e 0x1c92e78 0xb66665 0x6539f 0x10e46b0 0x228ffc0 0x228433c 0x228feaf 0x1042bd 0x4cb56 0x4b66f 0x4b589 0x4a7e4 0x4a61e 0x4b3d9 0x4e2d2 0xf899c 0x45574 0x4576f 0x45905 0x4e917 0x20eb 0x12157 0x12747 0x1394b 0x24cb5 0x25beb 0x17698 0x1beedf9 0x1beead0 0x1c08bf5 0x1c08962 0x1c39bb6 0x1c38f44 0x1c38e1b 0x1317a 0x14ffc 0x1d2d 0x1c55)
libc++abi.dylib: terminate called throwing an exception
This crashes after -(void)viewDidLoad{} in ViewController.m and I have not yet learned how to fix auto layout/ layoutSubview errors. Does anyone else know how?
I have limited experience with ios development so I am sure that I just don't have the right pieces in the right spots. I used http://www.techotopia.com/index.php/An_Example_iOS_5_iPhone_UIPageViewController_Application to get this far.
My code is as follows:
ViewController.h
#import <UIKit/UIKit.h>
#import "contentViewController.h"
#interface ViewController : UIViewController
<UIPageViewControllerDataSource>
{
UIPageViewController *pageController;
NSArray *pageContent;
}
#property (strong, nonatomic) UIPageViewController *pageController;
#property (strong, nonatomic) NSArray *pageContent;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize pageController, pageContent;
- (contentViewController *)viewControllerAtIndex:(NSUInteger)index
{
// Return the data view controller for the given index.
if (([self.pageContent count] == 0) || (index >= [self.pageContent count])) {
return nil;
}
// Create a new view controller and pass suitable data.
contentViewController *dataViewController =[[contentViewController alloc]initWithNibName:#"contentViewController"bundle:nil];
dataViewController.dataObject =[self.pageContent objectAtIndex:index];
return dataViewController;
}
- (NSUInteger)indexOfViewController:(contentViewController *)viewController
{
return [self.pageContent indexOfObject:viewController.dataObject];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = [self indexOfViewController:(contentViewController *)viewController];
if ((index == 0) || (index == NSNotFound)) {
return nil;
}
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = [self indexOfViewController:(contentViewController *)viewController];
if (index == NSNotFound) {
return nil;
}
index++;
if (index == [self.pageContent count]) {
return nil;
}
return [self viewControllerAtIndex:index];
}
- (void) createContentPages
{
NSMutableArray *pageStrings = [[NSMutableArray alloc] init];
for (int i = 1; i < 4; i++)
{
NSString *contentString = [[NSString alloc]initWithFormat:#"Chapter %d \nThis is the page %d of content displayed using UIPageViewController in iOS 5.", i, i];
[pageStrings addObject:contentString];
}
pageContent = [[NSArray alloc] initWithArray:pageStrings];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self createContentPages];
self.pageController = [[UIPageViewController alloc]initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
pageController.dataSource = self;
[[pageController view] setFrame:[[self view] bounds]];
contentViewController *initialViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:pageController];
[[self view] addSubview:[pageController view]];
[pageController didMoveToParentViewController:self];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
contentViewController.h
#import <UIKit/UIKit.h>
#interface contentViewController : UIViewController
#property (strong, nonatomic) IBOutlet UITableView *theTableView;
#property (strong, nonatomic) id dataObject;
#property (strong, nonatomic) NSArray *pageContent;
#end
contentViewController.m
#import "contentViewController.h"
#interface contentViewController ()
#end
#implementation contentViewController
#synthesize theTableView, dataObject, pageContent;
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void) createContentPages
{
NSMutableArray *pageStrings = [[NSMutableArray alloc] init];
for (int i = 1; i < 4; i++)
{
NSString *contentString = [[NSString alloc]initWithFormat:#"Chapter %d \nThis is the page %d of content displayed using UIPageViewController in iOS 5.", i, i];
[pageStrings addObject:contentString];
}
pageContent = [[NSArray alloc] initWithArray:pageStrings];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self createContentPages];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection (NSInteger)section
{
return 4;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath (NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [pageContent objectAtIndex:indexPath.row];
return cell;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
So if anyone could straighten me out I would appreciate it.
I was able to solve my own problem, and it seems I was just a bit mixed up.
The comment from rdelmar set me on the right track but I hooked the delegate and data source to the wrong object. I had to connect them to File's Owner in order for it to work.
In addition it seems theTableView was not necessary and when I removed that my code suddenly worked as expected.
If this isn't clear enough for an answer please tell me how I can be more specific. Thank you!

Init a NSMutableArray in a singelton that saves as a state

Im am using a pattern from the book Learning Cocos2D. I have a GameState class that is a singleton that can be saved as a state. The BOOL soundOn gets init as false the first time the class gest created, but the array levelProgress dosent. I made comments on all the lines on which I have tried to init the array.
The class uses a helper that loads and saves the data.
When i try 1 or 2 i get "instance variable 'levelProgress' accessed in class method" error.
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface GameState : NSObject <NSCoding> {
BOOL soundOn;
NSMutableArray *levelProgress;
}
+ (GameState *) sharedInstance;
- (void)save;
#property (assign) BOOL soundOn;
#property (nonatomic, retain) NSMutableArray *levelProgress;
#end
#import "GameState.h"
#import "GCDatabase.h"
#implementation GameState
#synthesize soundOn;
#synthesize levelProgress;
static GameState *sharedInstance = nil;
+(GameState*)sharedInstance {
#synchronized([GameState class])
{
if(!sharedInstance) {
sharedInstance = [loadData(#"GameState") retain];
if (!sharedInstance) {
[[self alloc] init];
// 1. levelProgress = [[NSMutableArray alloc] init];
}
}
return sharedInstance;
}
return nil;
}
+(id)alloc
{
#synchronized ([GameState class])
{
NSAssert(sharedInstance == nil, #"Attempted to allocate a \
second instance of the GameState singleton");
sharedInstance = [super alloc];
// 2. levelProgress = [[NSMutableArray alloc] init];
return sharedInstance;
}
return nil;
}
- (void)save {
saveData(self, #"GameState");
}
- (void)encodeWithCoder:(NSCoder *)encoder {
// 3. levelProgress = [[NSMutableArray alloc] init];
[encoder encodeBool:currentChoosenCountry forKey:#"soundOn"];
[encoder encodeObject:levelProgress forKey:#"levelProgress"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super init])) {
// 4. levelProgress = [[NSMutableArray alloc] init];
soundOn = [decoder decodeBoolForKey:#"soundOn"];
levelProgress = [decoder decodeObjectForKey:#"levelProgress"];
}
return self;
}
#end
Solution:
*I just added av init method...*
-(id)init {
self = [super init];
if (self != nil) {
levelProgress = [[NSMutableArray alloc] init];
}
return self;
}
Try this (this code may contain syntax or logic errors - I wrote it in notepad):
#interface GameState : NSObject <NSCoding>
#property (nonatomic, readwrite) BOOL soundOn;
#property (nonatomic, retain) NSMutableArray *levelProgress;
+ (GameState *)sharedState;
- (void)writeDataToCache;
#end
//
#implementation GameState
#synthesize soundOn, levelProgress;
#pragma mark - Singleton
static GameState *sharedState = nil;
+ (void)initialize {
static BOOL initialized = NO;
if (!initialized) {
initialized = YES;
if ([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:#"%#/gameState", pathCache]]) {
NSData *encodedObject = [[NSData alloc] initWithContentsOfFile:[NSString stringWithFormat:#"%#/gameState", pathCache]];
data = [NSKeyedUnarchiver unarchiveObjectWithData:encodedObject];
}
else
data = [[GameState alloc] init];
}
}
+ (GameState *)sharedState {
return sharedState;
}
#pragma mark - Initialization
- (id)init {
if (self = [super init]) { // will be inited while application first run
soundOn = NO;
levelProgress = [[NSMutableArray alloc] init];
return self;
}
return nil;
}
#pragma mark - Coding Implementation
- (void)writeDataToCache { // use this method to save current state to cache
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:self];
if([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:#"%#/GameState", pathCache]])
[[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:#"%#/GameState", pathCache] error:nil];
[[NSFileManager defaultManager] createFileAtPath:[NSString stringWithFormat:#"%#/GameState", pathCache] contents:encodedObject attributes:nil];
NSLog(#"GameState was saved successfully.");
}
- (void)encodeWithCoder:(NSCoder*)encoder {
[encoder encodeBool:self.soundOn forKey:#"soundOn"];
[encoder encodeObject:self.levelProgress forKey:#"levelProgress"];
}
- (id)initWithCoder:(NSCoder*)decoder {
if ((self = [super init])) {
self.soundOn = [decoder decodeBoolForKey:#"soundOn"];
self.levelProgress = [decoder decodeObjectForKey:#"levelProgress"];
NSLog(#"GameState was inited successfully");
return self;
}
return nil;
}
#end
1 & 2 are the same (and should not work, since levelProgress is an instance variable), 3 and 4 should be encoding/decoding the array, not initializing it anew.
1 and 2 are a mistake - you can't access instance variable without specifying an instance.
You can fix 1 by changing the line to sharedInstance.levelProgress = ....
2 is just a bad idea, you're breaking common convention of initializing using init... methods, it may surprise and cause problems for an other programmer who'll be working on the same code later.
3 and 4 are ok but if loadData fails they will not be executed and the object will be initialized with ordinary init and the array will be nil pointer:
[[self alloc] init];
you should also override init and initialize the properties there.

UIMenuController: how to tell which menuItem is pressed?

I have a UILongPressGestureRecognizer on a UITableViewCell that displays a UIMenuController with some categories the user can pick from. These categories are stored in a NSMutableArray and can be customized by the user. I want to use one method to handle all category-presses in the UIMenuController. How can I pass the index of the selected UIMenuItem? Thanks in advance.
#pragma mark -
#pragma mark Custom Quick Menu Item
#interface QuickMenuItem : UIMenuItem
{
}
#property (nonatomic, retain) NSIndexPath *indexPath;
#property (nonatomic, retain) NSMutableString *category;
#end
#implementation QuickMenuItem
#synthesize indexPath, category;
- (void)dealloc
{
[indexPath release];
[category release];
[super dealloc];
}
#end
#pragma mark -
#pragma mark Handle UILongPressGesture
- (void)handleLongItemPress:(UILongPressGestureRecognizer *)longPressRecognizer
{
if (longPressRecognizer.state == UIGestureRecognizerStateBegan)
{
NSIndexPath *pressedIndexPath = [queueTableView indexPathForRowAtPoint:[longPressRecognizer locationInView:queueTableView]];
if (pressedIndexPath && (pressedIndexPath.row != NSNotFound) && (pressedIndexPath.section != NSNotFound))
{
[self becomeFirstResponder];
UIMenuController *menuController = [UIMenuController sharedMenuController];
NSMutableArray *categoryMenuItems = [NSMutableArray array];
NSEnumerator *e = [self.stats.categories objectEnumerator]; // array with categories
NSMutableString *cat;
while (cat = [e nextObject])
{
QuickMenuItem *categoryMenuItem = [[QuickMenuItem alloc] initWithTitle:cat action:#selector(quickMenuCategoryPressed:)];
categoryMenuItem.indexPath = pressedIndexPath;
categoryMenuItem.category = cat;
[categoryMenuItems addObject:categoryMenuItem];
[categoryMenuItem release];
}
menuController.menuItems = [NSArray arrayWithArray:categoryMenuItems];
[menuController setTargetRect:[queueTableView rectForRowAtIndexPath:pressedIndexPath] inView:queueTableView];
[menuController setMenuVisible:YES animated:YES];
}
}
}
- (void)quickMenuCategoryPressed:(UIMenuController *)menuController
{
QuickMenuItem *menuItem = [[[UIMenuController sharedMenuController] menuItems] objectAtIndex: ??]; // How to tell which category is selected?
if (menuItem.indexPath)
{
[self resignFirstResponder];
// Perform action
}
}
You'll probably need to create some dynamic selectors, as described at Dynamic UIMenuItems with #selector and dynamic methods