nsmutabledictionary returning null when accessed outside of class - objective-c

I have searched around a bit and not found an answer to my question. I am a beginner to objective-c and I am currently experimenting around with dictionaries. I have a class called "SETestBank" that creates 3 dictionaries of images and then adds them to a large dictionary. I am doing it this way because down the line I may add more smaller dictionaries. I've created a specific "accessBank" method to pull objects out via other classes.
SETestBank.m
#import "SETestBank.h"
#implementation SETestBank
#synthesize mathBankA, mathBankB, mathBankC, mathTest;
- (id)init
{
[self createBankA];
[self createBankB];
[self createBankC];
[self createTest];
return 0;
}
- (NSMutableDictionary *)createBankA
{
mathBankA = [[NSMutableDictionary alloc] init];
for (int i=0; i < 11; i++) {
NSString *aKey = [NSString stringWithFormat:#"%da", i];
NSString* imageName = [NSString stringWithFormat:#"%da.png",i];
[mathBankA setObject:imageName forKey:aKey];
NSLog(#"%#", aKey);
}
return mathBankA;
}
//same occurs to generate bankB and bankC
- (NSMutableDictionary *)createTest
{
mathTest = [[NSMutableDictionary alloc] init];
[mathTest addEntriesFromDictionary:mathBankA];
[mathTest addEntriesFromDictionary:mathBankB];
[mathTest addEntriesFromDictionary:mathBankC];
return mathTest;
}
- (NSString *)accessBank:(NSString *)accessor
{
NSString *img = [mathTest objectForKey:accessor];
return img;
}
#end
now this code seems to work fine. The dictionaries are created and console logs display the correct keys and object filenames (the images are all in the project and properly named)
However, when I access it in my view controller and try to apply an image to a UIImageView I get nothing.
SEViewController:
- (void)viewDidLoad
{
SETestBank *mathTest = [[SETestBank alloc] init];
UIImage *img = [UIImage imageNamed:[mathTest accessBank:#"1a"]];
NSString *test = [mathTest accessBank:#"1a"];
NSLog(#"%#", test);
[imageView setImage:img];
[self.view addSubview:imageView];
[super viewDidLoad];
}
the log here simply returns null along with a "CUICatalog: Invalid asset name supplied: (null), or invalid scale factor: 1.000000" error which I gather is from trying to assign null to an image. I'm stumped on this one. If I hardcode the image to display "1a.png" rather than trying to access it by key it works fine but that is not what I'm trying to accomplish. imageView is connected to a UIImageView in storyboard and the view controller is set to use SEViewController as it's class.
Thanks for any help!

In the init method, you return a 0, which means nothing (nil)
- (id)init
{
[self createBankA];
[self createBankB];
[self createBankC];
[self createTest];
return 0;
}
Change this to
- (id)init
{
if (self = [super init]) {
[self createBankA];
[self createBankB];
[self createBankC];
[self createTest];
}
return self;
}
may solve your problem.

Your init in SETestBank is returning 0, so your mathTest object is nil when you do
SETestBank *mathTest = [[SETestBank alloc] init];

Related

Singleton not updating variable immediately

I have a singleton here is the header file:
#import <Foundation/Foundation.h>
#interface Shared : NSObject
{
NSString *messages;
}
#property (nonatomic, retain) NSString *messages;
+ (Shared*)sharedInstance;
#end
Here is the implementation:
#import "Shared.h"
static Shared* sharedInstance;
#implementation Shared
#synthesize messages;
+ (Shared*)sharedInstance
{
if ( !sharedInstance)
{
sharedInstance = [[Shared alloc] init];
}
return sharedInstance;
}
- (id)init
{
self = [super init];
if ( self )
{
messages = [[NSString alloc] init];
}
return self;
}
#end
The problem is when the I use
[Shared sharedInstance].messages = someVariable;
I can use
NSLog([Shared sharedInstance].messages);
and it shows the right output, but when i check from another class, NSLog doesn't show any output. I have the NSLog in the viewDidLoad method of another class, so when I click a button to go to the next view, it should output the value of the string, but it only works the second time. If the variable is set to dog, first it outputs nothing, then when I close the view and try again, it outputs dog. however, if I then change the variable to cat, it will output dog, and on the next attempt, output cat. I want it to update immediately, rather than remain one behind all the time.
EDIT: Here's the code from the other classes
This particular section is from a view controller class in the method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Omitted, just preparing the DB, and emptying the array.
if ([db open])
{
FMResultSet *s = [db executeQueryWithFormat:#"SELECT ShabadID FROM Shabad WHERE Gurmukhi LIKE %#", currentLine];
while ([s next])
{
lineID = [s intForColumn:#"ShabadID"];
}
s = [db executeQueryWithFormat:#"SELECT Gurmukhi, ShabadID FROM Shabad WHERE ShabadID LIKE %i", lineID];
while ([s next])
{
//NSLog([s stringForColumn:#"Gurmukhi"]);
[paragraphArray addObject:[s stringForColumn:#"Gurmukhi"]];
}
Text = #"";
for (int i = 0; i<[paragraphArray count]; i++)
{
Text = [Text stringByAppendingFormat:#"%#\n", [paragraphArray objectAtIndex:i]];
}
[Shared sharedInstance].messages = Text;
}
Then in the another class, where I want the text to appear, in the viewDidLoad method,
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog([Shared sharedInstance].messages);
UITextView *myUITextView = [[UITextView alloc] initWithFrame:CGRectMake(0,30,310,450)];
myUITextView.text = [Shared sharedInstance].messages;
myUITextView.textAlignment = NSTextAlignmentCenter;
myUITextView.textColor = [UIColor blackColor];
myUITextView.font = [UIFont fontWithName:#"GurbaniLipiLight" size:24];
[myUITextView setBackgroundColor:[UIColor clearColor]];
myUITextView.editable = NO;
myUITextView.scrollEnabled = YES;
[ScrollerView addSubview:myUITextView];
}
Sure the NSLog doesn't show up right, but neither does the text in the textview, it does the same thing the NSLog does.
There is an assumption here about what order things happen in that's not quite right. Assuming there's a segue involved in this, didSelectRowAtIndexPath: is called after the new view controller is prepared but before it's displayed. Moving code to viewWillAppear: or viewDidAppear: delays execution until after the calling controller has set new data.
The other approach for communication between controllers that use a segue, is to use prepareForSegue: in the first controller to set data that the second controller needs. That way it should be available when the view is loaded.

Populating an IKImageBrowserView

I am currently working on building up a view which shows icons, and text labels to the right of the icons. After some searching, I've decided that an IKImageBrowserView is most suitable. As such, I've gone ahead and created my IKImageBrowserView and set it's data source as follows:
// Setup image browser view
IKImageBrowserView *browser = [IKImageBrowserView new];
// Build our datasource delegate
MyDataStore *ds = [[MyDataStore alloc] init];
[browser setDelegate:ds];
[browser setDataSource:ds];
NSImage *image = // . . . File path
NSString *imageID = // . . . Filename
IKBBrowserItem *item = [[IKBBrowserItem alloc] initWithImage:image imageID:imageID];
[[ds images] addObject:item];
I've also created my data source, implementing the two required methods in the protocol:
import "MyDataStore.h"
#implementation MyDataStore
- (id) init {
if ([super init] != nil) {
images = [NSMutableArray new];
}
return self;
}
- (NSUInteger) numberOfItemsInImageBrowser:(IKImageBrowserView *) aBrowser {
return [images count];
}
- (id) imageBrowser:(IKImageBrowserView *) aBrowser itemAtIndex:(NSUInteger)index {
return [images objectAtIndex:index];
}
- (NSMutableArray *) images {
return images;
}
#end
However, when I try and add an item (as shown in the first block of code), nothing shows up in my IKImageBrowserView. I suspect I'm doing something glaringly incorrect with this view. Anyone know what that may be?

Improve Load Time of Sectioned UITableView

I am displaying a UITableView modally, but it takes about two seconds for it to appear, below is the code that is holding up the transition.
ModalViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
// get all songs from iTunes library
MPMediaQuery *songQuery = [MPMediaQuery songsQuery];
// put the songs into an array
self.songsArray = [songQuery items];
// create a sectioned array where songs are sectioned by title
self.sectionedSongsArray = [self partitionObjects:self.songsArray collationStringSelector:#selector(title)];
}
- (NSArray *)partitionObjects:(NSArray *)array collationStringSelector:(SEL)selector
{
UILocalizedIndexedCollation *collation = [UILocalizedIndexedCollation currentCollation];
NSInteger sectionCount = [[collation sectionTitles] count];
NSMutableArray *unsortedSections = [NSMutableArray arrayWithCapacity:sectionCount];
for(int i = 0; i < sectionCount; i++)
{
[unsortedSections addObject:[NSMutableArray array]];
}
for (id object in array)
{
NSInteger index = [collation sectionForObject:object collationStringSelector:selector];
[[unsortedSections objectAtIndex:index] addObject:object];
}
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
for (NSMutableArray *section in unsortedSections)
{
[sections addObject:[collation sortedArrayFromArray:section collationStringSelector:selector]];
}
return sections;
}
The above code works fine, but its slow to load the modal view first time, is there a better way to do this? Thanks.
Yeah: don’t do it in -viewDidLoad. A better place would be in the view controller’s -init or -initWithNibNamed:bundle: or whatever, and in the background. Example:
- (id)init
{
self = [super init];
if(self)
{
// ...
dispatch_async(dispatch_get_global_queue(DISPATCH_PRIORITY_DEFAULT, 0), ^{
// since it's not on the main thread, you need to create your own autorelease pool to prevent leaks
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
MPMediaQuery *songQuery = [MPMediaQuery songsQuery];
self.songsArray = [songQuery items];
self.sectionedSongsArray = [self partitionObjects:self.songsArray collationStringSelector:#selector(title)];
// UI calls have to be on the main thread, so we go back to that here
dispatch_async(dispatch_get_main_queue(), ^{
if([self isViewLoaded])
{
[self.tableView reloadData];
}
});
// this releases any objects that got autoreleased earlier in the block
[pool release];
});
}
return self;
}
Your -tableView:numberOfRowsInSection: method should of course now check whether sectionedSongsArray is non-nil and in that case return 0 (or 1 if you want to display a “loading” cell, which you probably should).

NSTableView don't display data

I have data in NSMutableArray and I want to display it in NSTableView, but only the number of cols has changed.
This use of NSTableView is based on tutorial here.
FinalImageBrowser is IBOutlet to NSTableView.
#implementation AppController
NSMutableArray *listData;
- (void)awakeFromNib {
[FinalImageBrowser setDataSource:self];
}
- (IBAction)StartReconstruction:(id)sender
{
NSMutableArray *ArrayOfFinals = [[NSMutableArray alloc] init]; //Array of list with final images
NSString *FinalPicture;
NSString *PicNum;
int FromLine = [TextFieldFrom intValue]; //read number of start line
int ToLine = [TextFieldTo intValue]; //read number of finish line
int RecLine;
for (RecLine = FromLine; RecLine < ToLine; RecLine++) //reconstruct from line to line
{
Start(RecLine); //start reconstruction
//Create path of final image
FinalPicture = #"FIN/final";
PicNum = [NSString stringWithFormat: #"%d", RecLine];
FinalPicture = [FinalPicture stringByAppendingString:PicNum];
FinalPicture = [FinalPicture stringByAppendingString:#".bmp"];
[ArrayOfFinals addObject:FinalPicture]; // add path to array
}
listData = [[NSMutableArray alloc] init];
[listData autorelease];
[listData addObjectsFromArray:ArrayOfFinals];
[FinalImageBrowser reloadData];
NSBeep(); //make some noise
NSImage *fin = [[NSImage alloc] initWithContentsOfFile:FinalPicture];
[FinalImage setImage:fin];
}
- (int)numberOfRowsInTableView:(NSTableView *)tv {
return [listData count];
}
- (id)tableView:(NSTableView *)tv objectValueFromTableColumn:(NSTableColumn *)tableColumn row:(int)row {
return (NSString *)[listData objectAtIndex:row];
}
#end
When the StartReconstruction end the number of cols have changed right, but they're empty. When I debug app, items in listData is rigth.
I'm guessing you forgot to connect the FinalImageBrowser outlet to the table view. That would mean your setDataSource: message is to nil, which would leave the table view without a data source.
You don't need to send that message anyway—you can set the data source in the nib. Remove your awakeFromNib implementation and connect the table view's dataSource outlet to your data source object in IB, as well as the FinalImageBrowser outlet to the table view (also in IB).

Objective-C: Assigning the value of a static variable to an instance variable

I essentially want to give each instance of a class a unique id.
So, I created a static integer. I increment it each time a new object is created and then assign the value of the static variable to an ivar. But clearly I don't understand something because, let's say I create three objects, "thisPageNumber" (which is the instance variable) is always 3 no matter which object I reference.
More information:
This class creates a number of "Page" objects. I'd like each page to know it's page number so that it can display the correct page art as well as perform a number of other various actions.
.h partial code:
#interface Page : UIViewController
{
NSNumber *thisPageNumber;
UIImageView *thisPageView;
UIImageView *nextPageView;
UIImageView *prevPageView;
UIImageView *pageArt;
}
.m partial code:
#implementation Page
static int pageCount = 0;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
pageCount++;
thisPageNumber = pageCount;
}
return self;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
CGRect defaultFrame = CGRectMake(0.0, 0.0, 1024.0, 768.0);
if (thisPageView == nil) {
thisPageView = [[UIImageView alloc]
initWithImage:[UIImage
imageNamed:[NSString stringWithFormat:#"Page%i.png", [thisPageNumber intValue]]]];
thisPageView.frame = defaultFrame;
[self.view addSubview:thisPageView];
}
if (nextPageView == nil && [thisPageNumber intValue] < BOOK_PAGE_COUNT) {
nextPageView = [[UIImageView alloc]
initWithImage:[UIImage
imageNamed:[NSString stringWithFormat:#"Page%i.png", [thisPageNumber intValue]+1]]];
nextPageView.frame = defaultFrame;
[self.view addSubview:nextPageView];
}
if (prevPageView == nil && [thisPageNumber intValue] > 1) {
prevPageView = [[UIImageView alloc]
initWithImage:[UIImage
imageNamed:[NSString stringWithFormat:#"Page%i.png", [thisPageNumber intValue]-1]]];
prevPageView.frame = defaultFrame;
[self.view addSubview:prevPageView];
}
}
I'm not sure why the compiler didn't complain, but part of your problem is here:
thisPageNumber = pageCount;
NSNumber is an object. To set it to the current pageCount value, use
thisPageNumber = [[NSNumber alloc] initWithInt:pageCount];
Why don't you just use self as the unique ID? It's unique.