I am trying to open a PDF via the QuickLook framework without using UIScrollView...
I believe I'm missing something...
Where I believe I'm going wrong is that I need to use a QLPreviewController and on the QLPreviewController is a dataSource that has to conform to QLPreviewItem. The documentation states that NSURL does conform to QLPriewItem so I'm setting the preview.dataSource to an NSURL which is throwing an error:
[NSURL numberOfPreviewItemsInPreviewController:]: unrecognized selector sent to instance
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSURL numberOfPreviewItemsInPreviewController:]: unrecognized selector sent to instance 0x5b5f200'
Which makes me think that NSURL does not conform.
all the code I think is necessary...
- (BOOL)previewController:(QLPreviewController *)controller shouldOpenURL:(NSURL *)url forPreviewItem:(id <QLPreviewItem>)item {
return YES;
}
- (NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller {
return [documents count];
}
- (id <QLPreviewItem>) previewController: (QLPreviewController *) controller previewItemAtIndex: (NSInteger) index {
return [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:[documents objectAtIndex:index] ofType:nil]];
}
- (void)pushPDF {
QLPreviewController *preview = [[QLPreviewController alloc] init];
preview.dataSource = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"MCIT_Quiz" ofType:#"pdf"]];
//preview.currentPreviewItemIndex = 0;
[self presentModalViewController:preview animated:YES];
[preview release];
}
I ended up just creating another class to hold my values and use as a datasource, a bit quick and dirty but it works.
//
// documentList.h
//
#import <Foundation/Foundation.h>
#import <QuickLook/QuickLook.h>
#interface DocumentList : NSObject <QLPreviewControllerDataSource, QLPreviewControllerDelegate> {
NSArray *documents;
}
#property (nonatomic, retain) NSArray *documents;
-(void)createList;
-(NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller;
- (id <QLPreviewItem>) previewController: (QLPreviewController *) controller previewItemAtIndex: (NSInteger) index;
#end
inserting text to break up the files
//
// documentList.m
//
#import "DocumentList.h"
#implementation DocumentList
#synthesize documents;
-(void) createList {
documents = [[NSArray arrayWithObjects:#"Quiz.pdf", nil] retain];
}
-(NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller {
return [documents count];
}
- (id <QLPreviewItem>) previewController: (QLPreviewController *) controller previewItemAtIndex: (NSInteger) index {
return [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:[documents objectAtIndex:index] ofType:nil]];
}
#end
Well, I don't see where an NSURL conforms to QLPreviewControllerDataSource. I think you want
preview.dataSource = self;
And then your already written routines (numberOfPreviewItemsInPreviewController and previewController) would return the appropriate NSURL (although it's not clear how "documents" gets filled.).
Related
I have done the following task of taking data from a csv file and publishing it in a table view. What i would like to do is use uisegmentented control to sort and filter the core data which i have.
I have a list of names somewhat like this and i have it stored in the data model as firstname, lastname and gender. I have designed the following screenshot. By clicking on the a-z button or z-a button. The data should sort and filter according to gender like male,female or both.
I am new to core data(sorting and filtering) and UISegmented controls. Could you please help me in doing the above tasks in detail and how you have done it.
Following is the code for the work i have done till now. And i am using xib's.
RootViewController.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface RootViewController : UITableViewController
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (retain) NSArray *people;
//! Filtering view from XIB file
#property (nonatomic, retain) IBOutlet UIView *filterSortView;
//! Filter control
#property (nonatomic, retain) IBOutlet UISegmentedControl *filterControl;
//! Sort control
#property (nonatomic, retain) IBOutlet UISegmentedControl *sortControl;
#end
Then we RootViewController.m
#import "RootViewController.h"
#import "Person.h"
#implementation RootViewController
#synthesize people;
#synthesize managedObjectContext=__managedObjectContext;
// Sorting & filtering UI components
#synthesize filterSortView, filterControl, sortControl;
#pragma mark -
#pragma mark View Lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.title = #"People";
// Get our people array - this next block of code could probably be extracted out to a private
// method and generalized for different fetch request types
NSEntityDescription *personEntity = [NSEntityDescription entityForName:#"Person"
inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:personEntity];
NSError *error = nil;
self.people = [self.managedObjectContext executeFetchRequest:request error:&error];
if (error)
{
[NSException raise:NSInternalInconsistencyException format:#"Could not fetch Core Data records: %#",error];
}
[request release];
// Now do a secondary load from another XIB file (other than the main table view)
[[NSBundle mainBundle] loadNibNamed:#"FilterSortView" owner:self options:nil];
// This disables the default selection
self.filterControl.selectedSegmentIndex = -1;
self.sortControl.selectedSegmentIndex = -1;
// Now register for events when the value changes
// TODO: write method implementations for each of these and then uncomment these lines.
// [self.filterControl addTarget:self action:#selector( -- TODO -- ) forControlEvents:UIControlEventValueChanged];
// [self.sortControl addTarget:self action:#selector( -- TODO -- ) forControlEvents:UIControlEventValueChanged];
}
- (void)viewDidUnload
{
[super viewDidUnload];
self.filterControl = nil;
self.sortControl = nil;
self.filterSortView = nil;
}
#pragma mark -
#pragma mark UITableViewDataSource
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.people count];
}
#pragma mark -
#pragma mark UITableViewDelegate
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
Person *person = [self.people objectAtIndex:[indexPath row]];
cell.textLabel.text = [NSString stringWithFormat:#"%#, %#",person.lastName,person.firstName];
return cell;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
return self.filterSortView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return self.filterSortView.frame.size.height;
}
#pragma mark -
#pragma mark Class Plumbing
- (void)dealloc
{
[filterControl release];
[sortControl release];
[filterSortView release];
[people release];
[__managedObjectContext release];
[super dealloc];
}
#end
Then we Have FilteredListAppDelegate.h
#import <UIKit/UIKit.h>
#interface FilteredListAppDelegate : NSObject <UIApplicationDelegate> {
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
#property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
#end
Then we have FilteredListAppDelegate.m
#import "FilteredListAppDelegate.h"
#import "RootViewController.h"
#import "Person.h"
// Private methods
#interface FilteredListAppDelegate ()
- (BOOL) _populateCoreData;
#end
#implementation FilteredListAppDelegate
#synthesize window=_window;
#synthesize managedObjectContext=__managedObjectContext;
#synthesize managedObjectModel=__managedObjectModel;
#synthesize persistentStoreCoordinator=__persistentStoreCoordinator;
#synthesize navigationController=_navigationController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Prepopulate Core Data with our data, if necessary.
NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
if ([settings objectForKey:#"core_data_populated"] == nil)
{
BOOL success = [self _populateCoreData];
if (success)
{
[settings setValue:[NSNumber numberWithBool:YES] forKey:#"core_data_populated"];
}
}
// Override point for customization after application launch.
// Add the navigation controller's view to the window and display.
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
/*
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
/*
Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
*/
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Saves changes in the application's managed object context before the application terminates.
[self saveContext];
}
- (void)dealloc
{
[_window release];
[__managedObjectContext release];
[__managedObjectModel release];
[__persistentStoreCoordinator release];
[_navigationController release];
[super dealloc];
}
- (void)awakeFromNib
{
RootViewController *rootViewController = (RootViewController *)[self.navigationController topViewController];
rootViewController.managedObjectContext = self.managedObjectContext;
}
- (void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil)
{
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error])
{
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
#pragma mark - Private Methods
/**
* Hacky method to just throw a bunch of data into Core Data.
*/
- (BOOL) _populateCoreData
{
NSManagedObjectContext *context = [self managedObjectContext];
// Get the names out of the text file
NSError *error = nil;
NSString *filename = [[NSBundle mainBundle] pathForResource:#"actors_by_gender" ofType:#"csv"];
NSString *fileContents = [NSString stringWithContentsOfFile:filename encoding:NSUTF8StringEncoding error:&error];
if (error)
{
[NSException raise:NSInternalInconsistencyException format:#"Unable to read text file for adding to Core Data"];
}
NSArray *namesChoppedByNewline = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for (NSString *singleNameRecord in namesChoppedByNewline)
{
NSArray *attributesChoppedByComma = [singleNameRecord componentsSeparatedByString:#","];
// Now make each record a new Core Data object
Person *newPerson = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
newPerson.firstName = [attributesChoppedByComma objectAtIndex:0];
newPerson.lastName = [attributesChoppedByComma objectAtIndex:1];
newPerson.gender = [attributesChoppedByComma objectAtIndex:2];
}
if (![context save:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
[NSException raise:NSInternalInconsistencyException
format:#"Couldn't save Core Data data for Person entity import. Reason: %#",error];
}
return YES;
}
#pragma mark - Core Data stack
/**
Returns the managed object context for the application.
If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
*/
- (NSManagedObjectContext *)managedObjectContext
{
if (__managedObjectContext != nil)
{
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
__managedObjectContext = [[NSManagedObjectContext alloc] init];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return __managedObjectContext;
}
/**
Returns the managed object model for the application.
If the model doesn't already exist, it is created from the application's model.
*/
- (NSManagedObjectModel *)managedObjectModel
{
if (__managedObjectModel != nil)
{
return __managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"FilteredList" withExtension:#"momd"];
__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return __managedObjectModel;
}
/**
Returns the persistent store coordinator for the application.
If the coordinator doesn't already exist, it is created and the application's store added to it.
*/
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"FilteredList.sqlite"];
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
{
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
Typical reasons for an error here include:
* The persistent store is not accessible;
* The schema for the persistent store is incompatible with current managed object model.
Check the error message to determine what the actual problem was.
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
* Simply deleting the existing store:
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __persistentStoreCoordinator;
}
#pragma mark - Application's Documents directory
/**
Returns the URL to the application's Documents directory.
*/
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
#end
Thank you for the help.
You need to create and set an instance of NSSortDescriptor on your fetch request before you execute it.
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"firstName" ascending:YES];
By switching the ascending parameter based on your UISegmentedControl state, you can easily return the results in a-z or z-a order.
Alternatively, you could apply the same sort descriptor to your array of people using sortedArrayUsingDescriptors like so:
NSArray *array = [[self people] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
Keep in mind however that as you toggle the segmented control, you will either need to perform your fetch request again, or sort your array into a new array (perhaps use NSMutableArray instead).
To implement your segmented control, you will need to add a target and selector:
[[self segmentedControl] addTarget:self action:#selector(segmentedControlEventHandler:) forControlEvents:UIControlEventValueChanged];
Then use the following method to amend and update your sort descriptor and table view.
- (void)segmentedControlEventHandler:(id)sender
{
UISegmentedControl *segmentedControl = (UISegmentedControl *)sender;
if([segmentedControl selectedSegmentIndex] == 0)
{
//segment 1 selected
//set BOOL value for ascending sort descriptor
//update table view -> [[self tableView] reloadData];
}
else
{
//segment 2 selected
}
}
to filter by gender: NSPredicate *predicatem =[NSPredicate predicateWithFormat:#"gender == %#", #"m" ];
//give the predicate to the fecth request
request.predicate=predicatem;
I am new to iOS development. I am trying to implement NSURLConnectionDataDelegate Protocol but it seems that none of the delegate methods ever get called. I had to type the delegate methods in myself, is it supposed to be automatically generated?
I have an NSLog command in each delegate method but nothing prints. I am using NSURLConnection to Asynchronously download and keep track of the progress so I can update a progressView later.
SearchFeed.h file (Notice I have tried to implement the protocol when I typed NSURLConnectionDataDelegate
#import <Foundation/Foundation.h>
#import "Doc.h"
#interface SearchFeed : NSObject <NSXMLParserDelegate, NSURLConnectionDataDelegate>
{
NSMutableString * currentElementValue;
Doc *currentDoc;
}
#property(strong,nonatomic) NSURL * searchUrl;
#property(strong,nonatomic) NSArray * searchResults;
//#property(retain, nonatomic) Doc * currentDoc;
#property(retain, nonatomic) NSMutableArray *docs;
//#property(retain, nonatomic) NSURLConnection *urlConnection;
#property(retain, nonatomic) UIProgressView * progressBar;
-(void)retrieveFromInternet;
-(double) getProgress;
+(NSString *)pathToDocuments;
+(void)downloadPDFToMyDocumentsFrom:(NSString*) PDFUrl filename:(NSString *) title;
+(NSArray *)listFilesAtPath:(NSString *)path;
#end
SearchFeed.m file:
#import "SearchFeed.h"
#implementation SearchFeed
#synthesize searchUrl = _searchUrl; //where to search from
#synthesize searchResults = _searchResults; // Not being used -- I think
//#synthesize currentDoc = _currentDoc; //current Doc
#synthesize docs = _docs; //array of Docs
#synthesize progressBar = _progressBar;
NSURLConnection *urlConnection;
double fileLength =0;
double lastProgress =0;
double currentLength =0;
NSOutputStream *fileStream;
+(void)downloadPDFToMyDocumentsFrom:(NSString*) PDFUrl filename:(NSString *) title {
NSURL *url = [[NSURL alloc] initWithString:PDFUrl];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
NSString *fileName = [title stringByAppendingPathExtension:#"pdf"];
NSString *filePath = [[self pathToDocuments] stringByAppendingPathComponent:fileName];
fileStream = [[NSOutputStream alloc] initToFileAtPath:filePath append:YES];
[fileStream open];
}
//handling incoming data
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
double length = [data length];
currentLength += length;
double progress = currentLength/fileLength;
NSLog(#"Receiving data");
if(lastProgress < progress)
{
//progressBar WRITE code to update the progress for the progress bar
lastProgress = progress;
self.progressBar.progress = lastProgress;
NSLog(#"%f -------------------------------------------------------", lastProgress);
}
NSUInteger left = [data length];
NSUInteger nwr = 0;
do {
nwr = [fileStream write:[data bytes] maxLength:left];
if(nwr == -1)
break;
left -= nwr;
}while(left>0);
if(left)
{
NSLog(#"Stream error: %#", [fileStream streamError]);
}
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
long length = [response expectedContentLength];
fileLength = length;
NSLog(#"%f ------------------------------------------------------- is the fileLength", fileLength);
}
//handling connection progress
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
//WRITE code to set the progress bar to 1.0
self.progressBar.progress = 1.0;
[fileStream close];
NSLog(#"%f -------------------------------------------------------", lastProgress);
}
I have set the delegate for NSURLConnection urlConnection to self which is SearchFeed.m class.
In SearchFeed.h, I tried to implement the NSURLConnectionDataDelegate protocol.
I had to create connectionDidFinishLoading, didReceiveResponse and didReceiveData methods but those methods don't get called.
I either have not implemented the protocol properly OR I have declared some methods as + and some as - (some methods are class methods while some are instance methods)
downloadPDFToMyDocumentsFrom is a class method which is invoked when the user clicks download.
This method sets the NSURLConnection, sets the URL etc and the delegate and opens the fileStream to receive data. However, none of the other methods get called.
Your downloadPDFToMyDocumentsFrom method is setup as a class method (+), and you setup your delegate to be self, meaning the Class in this case. You should make the downloadPDFToMyDocumentsFrom method a instance method (-) so that self is an instantiated object.
i am making game in which i am not using navigation controller but hiding and unhiding different controllers now my problem is as below
This Is going to be view long description you can understand what i am talking about
my flowConverViewController is like this
#import <UIKit/UIKit.h>
#import "FlowCoverView.h"
#import "NewGameController.h"
#import "MenuController.h"
#class NewGameController;
#interface FlowCoverViewController : UIViewController <FlowCoverViewDelegate>
{
}
#property (nonatomic, strong) NewGameController *game;
- (IBAction)done:(id)sender;
#end
flowcontroller.m is like below
when user click on image then image get selected and game should get start
for that delegate method get called which is below
- (void)flowCover:(FlowCoverView *)view didSelect:(int)image
{
NSLog(#"Selected Index %d",image);
UIImage *newimage = [self flowCover:view cover:image];
[
NSLog(#"%#",[self.game menu]); // this shows null
NSLog(#"%#",self.game); // this shows null thats why my below methods which are in another class is not getting called
[self.game menu] playMenuSound];
// game is object of newGameController that we created in .h file and in
same way in newgamecontroller there is object properly of menu class and
playMenuSound is method in menu class
[self.game imagePickedFromPuzzleLibrary:newimage];
[self.game startNewGame];
}
only problem is this methods are not getting called as it shows null i know that i can alloc init them but i cant do that previous data will get lost i have alloc init this properties in previous classes
.
newgamecontroller.h
#property (nonatomic, assign) MenuController *menu;
newgamecontroller.m
{
FlowCoverViewController *c = [[FlowCoverViewController alloc] init];
c.game = self;
NSArray *array = [[NSBundle mainBundle] loadNibNamed:#"TestFC" owner:self options:nil];
c = [array objectAtIndex:0];
[self presentModalViewController:c animated:YES];
}
I'm sorry, you do:
FlowCoverViewController *c = [[FlowCoverViewController alloc] init];
c.game = self;
and then you assign to c a different object with:
NSArray *array = [[NSBundle mainBundle] loadNibNamed:#"TestFC" owner:self options:nil];
c = [array objectAtIndex:0];
[self presentModalViewController:c animated:YES];
How could c still have the property c.game set properly??
EDIT: Try instead
NSArray *array = [[NSBundle mainBundle] loadNibNamed:#"TestFC" owner:self options:nil];
FlowCoverViewController *c = [array objectAtIndex:0];
[self presentViewController:c animated:YES completion:nil]
and let me know.
I'm trying to load a simple plist file (with an array at the root) into a UITableView (in the first view of an XCode 4.2 tabbed app). I've done this before in other (XCode 3) projects but for some reason it seems my lazy initializer for the array is never getting called.
.h file:
#import <UIKit/UIKit.h>
#interface NailPolishFirstViewController : UIViewController {
NSMutableArray *myCollection;
}
#property(nonatomic, retain) NSMutableArray *myCollection;
#end
.m file (relevant parts)
#import "NailPolishFirstViewController.h"
#implementation NailPolishFirstViewController
#synthesize myCollection;
// ...
- (NSMutableArray *) myCollection {
if (myCollection == nil) {
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"database.plist"];
self.myCollection = [NSMutableArray arrayWithContentsOfFile:finalPath];
NSLog(#"Collection size: %#", [self.myCollection count]);
}
return myCollection;
}
// ...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"Getting rows ... %#", [myCollection count]);
return [myCollection count];
}
// ...
The xib file for this controller has a UITableView attached and the dataSource and delegate are set to File's Owner.
When I build and run, the numberOfRowsInSection is logging "Getting rows ... (null)" but the log in my lazy initializer for myCollection never shows. Why isn't it ever getting called?
You aren't going through the accessor. Using [myCollection count] is accessing the ivar directly, which will be nil. If you're going to use lazy loading you have to always go via self.myCollection otherwise it will never call your accessor, and never populate the records.
Since myCollection is a declared property, it needs to reference its accessor. Try calling it as self.myCollection.
For instance
- (NSMutableArray *) myCollection {
if (myCollection == nil) {
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"database.plist"];
myCollection = [NSMutableArray arrayWithContentsOfFile:finalPath];
NSLog(#"Collection size: %#", [self.myCollection count]);
}
return myCollection;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"Getting rows ... %#", [self.myCollection count]);
return [self.myCollection count];
}
Ok, I'm trying to avoid global variables, so I read up on singleton classes.
This is a try to set and read a mutable array, but the result is null.
//Content.h
#interface Content : NSObject {
NSMutableArray *contentArray;
}
+ (Content *) sharedInstance;
- (NSMutableArray *) getArray;
- (void) addArray:(NSMutableArray *)mutableArray;
#end
.
//Content.m
#implementation Content
static Content *_sharedInstance;
+ (Content *) sharedInstance
{
if (!_sharedInstance)
{
_sharedInstance = [[Content alloc] init];
}
return _sharedInstance;
}
- (NSMutableArray *) getArray{
return contentArray;
}
- (void) addArray:(NSMutableArray *)mutableArray{
[contentArray addObject:mutableArray];
}
#end
And in a ViewController I added #import "Content.h", where I try to call this:
NSMutableArray *mArray = [NSMutableArray arrayWithObjects:#"test",#"foo",#"bar",nil];
Content *content = [Content sharedInstance];
[content addArray:mArray];
NSLog(#"contentArray: %#", [content getArray]);
You need to alloc and init the array first. Personally I'd do it in the init method of the content class like so:
-(id)init{
if(self = [super init]){
…the rest of your init code…
contentArray = [[NSMutableArray alloc] init];
}
return self;
}
You never actually alloc/initialise the contentArray array.