Calling reloadData in UITableViewController not working - ios

When my app loads up it builds a menu using a UITableViewController, this menu is split in two parts. The first part has 4 items from a 'hard-coded' array, the second part is made dynamically from a list of all text files in the documents folder.
When my app runs it builds the menu and then gets files if they're needed (which the menu needs to be complete). So the second part of my menu is blank. If I run the app again the menu is fine as the files now exist.
What I want is, when the file has finished downloading, for the UITableViewController to be reloaded and so the menu rebuilt.
The number of files can and will change and will be updated often with a timestamp check carried out just before the file is downloaded.
Where the file is downloaded:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
MenuViewController *refreshedMenu = [[MenuViewController alloc]init];
[refreshedMenu reloadTableView];
// Have also tried:
// [refreshedMenu performSelectorOnMainThread:#selector(reloadTableView)withObject:nil waitUntilDone:NO ];
[file closeFile];
}
The UITableViewController's code:
-(void)reloadTableView{
NSLog(#"reloadTableView has been run");
[self buildMenuArrays];
[self.tableView reloadData];
}
-(void)buildMenuArrays{
self.mainMenu = [NSMutableArray arrayWithObjects:#"Home", #"Exhibitor", #"Speaker", #"Workshop", nil];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDirectory = [paths objectAtIndex:0];
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSArray *files = [fileManager contentsOfDirectoryAtPath:docDirectory error:nil];
self.infoMenu = [[NSMutableArray alloc] init];
for (int count = 0; count < files.count; count++) {
NSString *currentFile = [files objectAtIndex:count];
if ([currentFile rangeOfString:#".txt"].location == NSNotFound) {
NSLog(#"File %# does not contain .txt", currentFile);
} else {
NSLog(#"File %# DOES contain .txt", currentFile);
[self.infoMenu addObject:[files objectAtIndex:count]];
}
}
NSLog(#"File array = %#", self.infoMenu);
}
At the moment, the array is updated and the correct file names are there but [self.tableView reloadData]; seems to do nothing.
Any ideas?
EDIT
This seems like such a simple process but I see many other people have the same problem. IS the approach I'm taking fundamentally wrong? It seems like calling a UITableViewController's reloadData method should be very easy to do, but isn't?

You seem to be making a new object of MenuViewController in connectionDidFinishLoading method. This will give you a new object allright but where have you added the view of this new TableViewController object into the existing ViewController?
You need to add view of this newly created TableViewController object onto your view hierarchy.

Related

iOS - custom initialization method for view controllers when using storyboard

I've read several posts similar to this one but I found them too specific. What I really want is a more general answer. According to Apple's view controller programming guide, viewDidLoad: should be used to "Allocating or loading data to be displayed in your view". If I have some data that has nothing to do with display, where should I initialize them?
Some posts suggests that initialization could be done in initWithCoder: when the view controller is initialized via a storyboard. I've tried to initialize an array in initWithCoder:, but after that it turned out that the array is still empty. So can we write a designated initializer to initialize this kind of data?
Here is the code:
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
// load notes
NSString * path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
_notes = [[PWCNotes alloc] initNotesWithFilename:self.docTitle path:path numberOfPages:self.numberOfPages];
_index = 0;
}
return self;
}
Here is the designated initializer method for PWCNotes
- (id)initNotesWithFilename:(NSString *)fileName path:(NSString *)path numberOfPages:(int)numberOfPages
{
if (!(self = [super init])) {
return nil;
}
_filePath = [path stringByAppendingString:[NSString stringWithFormat:#"/%#/notes.txt", fileName]];
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:self.filePath];
if (!exists)
{
// if file does not exist, create one and initialize the content
_notes = [[NSMutableArray alloc] init];
[[NSFileManager defaultManager] createFileAtPath:self.filePath contents:nil attributes:nil];
NSString * emptyString = #"Add Notes Here!";
for (int i = 0; i < numberOfPages; ++i)
{
[self.notes addObject:emptyString];
}
// write content of the array to the file
[self.notes writeToFile:self.filePath atomically:YES];
}
else
{
// otherwise, load it from the text file
_notes = [[NSMutableArray alloc] initWithContentsOfFile:self.filePath];
}
return self;
}
PWCNotes class has as a property a mutable array of NSString *s. When I call [self.notes.notes getObjectAtIndex:self.index], an NSRangeException is thrown, saying that I'm trying to access the object at index 0 in an empty array. Am I missing something?
viewDidLoad is the best place if you need to use the data only once the view appears, which is usually the case for View Controllers, as they only exists for managing views.
It's the best place because it is the one that's called in any case, no matter how the class got initialized.
If you really wanted to have the data ready earlier, you could implement an +initialize or +load method (NSObject +load and +initialize - What do they do?) instead, but that's not really what you should do in a View Controller.

Append New Input to plist in Documents Folder

After much searching of SO I've been able to come up with code to correct previous failures. What I am trying to accomplish is accepting user input from a UITextField and add that input to my TableView, which is populated from a plist. The root value of my plist is dictionary and I would like to keep it as such. My problem is when writeToFile is called, I'm completely over writing the existing values instead of inserting, adding or appending to the existing data. It doesn't appear that my attempt to combine the two dictionaries is working, since only the new value is being stored to the plist. Any insight as to where I'm going wrong? The following code is what I have
//////////********** Add New Cell When OK is Chosen
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
UITextField *newDevice = [alertView textFieldAtIndex:0];
NSLog(#"newDevice - %#",newDevice.text);
if (alertView.tag == 1 && buttonIndex == 1){
NSMutableDictionary *input = [[NSMutableDictionary alloc]init];
[input setObject:[NSArray arrayWithObject:newDevice.text] forKey:#"Room"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Test.plist"];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
// Combine the two Dictionaries to create one
[dictionary addEntriesFromDictionary:input];
// Write Combined Dictionary to plist
[dictionary writeToFile:path atomically:YES];
// Add Newly Created text to Table because reload table doesn't do it
[myTableData addObject:newDevice.text];
// Reload Table Data even though it seems useless
[myTable reloadData];
}
}
I guess you overwrote the values of the key Room.
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Test.plist"];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
// Get exist input from plist
NSMutableArray *inputFromPlist = dictionary[#"Room"];
// add new object to input
[inputFromPlist addObject:newDevice.text];
// set Room with modified object
[dictionary setObject:inputFromPlist forKey:#"Room"];
// Write Combined Dictionary to plist
[dictionary writeToFile:path atomically:YES];
// do something after saving plist ...

uiswitch in uitableview persistence when close and reopen the app? [duplicate]

This question already has answers here:
NSUserDefault and Switches
(2 answers)
Closed 9 years ago.
I would like to ask about uiswitch in uitableview custom cell.
if I have array that keeps state of each uiswitch in the table and update it on Change Value Event.However this changes are not persistence each time i open the app it reset to it's initial state. My question is how to make the changes persistent on my app whenever i close it and reopen it again ?
Here is my change value code:
-(void)switchChanged:(UISwitch *)sender
{
UITableViewCell *cell = (UITableViewCell *)[sender superview];
NSIndexPath *x=[mainTableView indexPathForCell:cell];
NSMutableArray *repl = [[NSMutableArray alloc] init];
if (sender.on)
{
repl= [SwitchArray objectAtIndex:x.section] ;
[repl replaceObjectAtIndex:x.row withObject:#"ON"];
}
else
{
//call the first array by section
repl= [SwitchArray objectAtIndex:x.section] ;
[repl replaceObjectAtIndex:x.row withObject:#"OFF"];
}
}
Here is the initial values of the array in viewDidLoad:
for(int j=0 ; j < 30 ; j++)
[switchArray addObject:#"ON"];
Thanks in advance. I appreciate your collaboration This will make me happy
One easy way to persist the array between uses of the app is to write out the array to a pList.
In order to do so you'll need a place to store the file, take the following example:
- (NSURL *)switchArrayFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSURL *filePath = [NSURL fileURLWithPath:[documentsDirectory stringByAppendingPathComponent:#"SwitchArray.plist"]];
return filePath;
}
Then in order to load up your array, you can read the pList back in in viewDidLoad: for instance:
self.switchArray = [[NSMutableArray alloc] initWithContentsOfURL:[self switchArrayFilePath]];
Then in order to write out the data, you could so something like this in viewWillDisappear:
[self.switchArray writeToURL:[self switchArrayFilePath] atomically:YES];
Other ways to persist this type of data on iOS would be use NSUserDefaults or use Core Data but that would be complex for something simple like this.
Hope that helps!

Unable to create ReaderDocument Vfr-Reader

I searched a lot but can't open PDF with vfr-reader from documents folder.
NSString *filePath = #"/Users/***/Library/Application Support/iPhone Simulator/5.0/Applications/F2B7E9DE-9996-4F05-BC81-2A2889B4F504/Documents/Number1.pdf";
ReaderDocument *document = [ReaderDocument withDocumentFilePath:filePath password:password];
if (document != nil)
{// document comes nil here
ReaderViewController *readerViewController = [[ReaderViewController alloc] initWithReaderDocument:document];
readerViewController.delegate = self; // Set the ReaderViewController delegate to self
[self.navigationController pushViewController:readerViewController animated:YES];
}
I am sure that filepath is exact the pdf file.
In the example code of reader it opens the pdf from main bundle. But I need to open from resources folder.
Thanks
i facing same issue, may be you also have same one.
if you are not using ARC than just write -fobjc-arc to every pdf reader file in build face. that will solve your problem.
You should use [[NSBundle mainBundle] bundlePath] and stringByAppendingPathComponent: instead of hard-coding the string. This is quite horrible and will only ever work on the iOS Simulator.
You should give file name like below code,don't give directly
NSArray *pathss = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPaths = [pathss objectAtIndex:0];
NSString *filePaths = [documentsPaths stringByAppendingPathComponent:[NSString stringWithFormat:#"%#",fileName]];
I understand this is an old post but none the less I ran into a similar problem as #user1392687 and wanted to share how I resolved the issue (I was loading files from various directories not just the Documents folder).
Problem: Load a series of PDF files out of a directory, populate a table view with filenames and supporting meta data, then upon selecting a cell, open the PDF file using VFR Reader.
Solution: The folder within X-Code is a Folder Reference to enable content updates without having to perform the remove/add cycle of a Group Reference. The function below was used to read all contents - URLs - of a specific folder path then remove all/any simlinks contained within the returned file paths. Prior passing the URL into VRF to load the PDF file [url path] was used for a RFC 1808 (unescaped) path.
+ (NSArray *)enumerateContentsOfFolderWithPath:(NSURL *)aFolderPath
{
NSError *error = nil;
NSArray *contentProperties = #[NSURLIsDirectoryKey,
NSURLIsReadableKey,
NSURLCreationDateKey,
NSURLContentAccessDateKey,
NSURLContentModificationDateKey];
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:aFolderPath
includingPropertiesForKeys:contentProperties
options:NSDirectoryEnumerationSkipsHiddenFiles
error:&error];
if (error != nil)
DLog(#"Content enumeration error: %#", error);
NSMutableArray *pdfURLs = [NSMutableArray array];
for (NSURL *item in contents)
{
NSURL *fileURL = [NSURL fileURLWithPath: [item path]];
NSURL *noSimlink = [fileURL URLByResolvingSymlinksInPath];
[pdfURLs addObject: noSimlink];
}
return pdfURLs;
}
After populating the table view with the contents of the folder and all supporting metadata, and upon a user touching a row to view the PDF file, the VRF Reader was setup as follows:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Other setup code...
NSURL *item = [pdfURLs objectAtIndex:(NSUInteger) indexPath.row];
[self presentPdfViewerForItem: item];
}
- (void)presentPdfViewerForItem:(NSURL *)aItem
{
NSString *phrase = nil; // Document password (for unlocking most encrypted PDF files)
NSString *filePath = [aItem path];
ReaderDocument *document = [ReaderDocument withDocumentFilePath: filePath password:phrase];
if (document != nil) // Must have a valid ReaderDocument object in order to proceed
{
ReaderViewController *readerViewController = [[ReaderViewController alloc] initWithReaderDocument:document];
readerViewController.delegate = self;
readerViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
readerViewController.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:readerViewController animated:YES completion:nil];
}
}

What is the correct way to save user data using NSFileManager?

I am having trouble initializing dictionaries I use throughout my program to store user achievements and scores.
I have almost identical code for the two dictionaries and only the gameCenterData dictionary seems to be working properly. I have tried altering the plist file name and contents yet nothing seems to make the playerData dictionary properly load info from the file as it should
In the Root View Controller I have the following code (playerData and gameCenterData are both NSMutableDictionaries and the plist files are in the proper place)
-(NSString *)scoreFilePath
{
NSArray *scorePath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [scorePath objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"PlayerScoreData.plist"];
}
-(NSString *)gameCenterFilePath
{
NSArray *gameCenterPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [gameCenterPath objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"GameCenterData.plist"];
}
then the view did load
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *playerDataPath = [self scoreFilePath];
if (! [[NSFileManager defaultManager] fileExistsAtPath:playerDataPath])
{
playerData = [NSMutableDictionary dictionaryWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"scoreData.plist"]];
[playerData writeToFile:[self scoreFilePath] atomically:YES];
NSLog(#"Player data file does not exist");
}
else
{
playerData = [[NSMutableDictionary alloc] initWithContentsOfFile:[self scoreFilePath]];
NSLog(#"player data file exists");
}
NSLog(#"scoreData is %#",playerData);
NSString *gameCenterPath = [self gameCenterFilePath];
if (! [[NSFileManager defaultManager] fileExistsAtPath:gameCenterPath])
{
gameCenterData = [NSMutableDictionary dictionaryWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"gameCenterData.plist"]];
[gameCenterData writeToFile:[self gameCenterFilePath] atomically:YES];
NSLog(#"game center data file does not exist");
}
else
{
gameCenterData = [[NSMutableDictionary alloc] initWithContentsOfFile:[self gameCenterFilePath]];
NSLog(#"game center data file exists");
}
NSLog(#"gameCenterData is %#",gameCenterData);
the output is as follows
2012-08-05 11:46:49.991 GlobeRoller[6410:1be03] Player data file does not exist
2012-08-05 11:46:49.992 GlobeRoller[6410:1be03] playerData is (null)
2012-08-05 11:46:50.061 GlobeRoller[6410:1be03] game center data file does not exist
2012-08-05 11:46:50.062 GlobeRoller[6410:1be03] gameCenterData is {
"Career Odometer" = 0;
"Career Score" = 0;
"Cities Found" = 0;
"Offline Games Played" = 0;
"Online Games Played" = 0;
"Online Games Won" = 0;
}
I have searched all of the questions and answers to see if I can find out why this isn't working for both methods. Any help you could offer, or resources you could point me to I would greatly appreciate.
Thank you,
CF
The plist file you are trying to load from the bundle is either not there, or has been created improperly. Directly from the documentation of dictionaryWithContentsOfFile:.
Return Value
A new dictionary that contains the dictionary at path, or
nil if there is a file error or if the contents of the file are an
invalid representation of a dictionary.
You should make sure you are using the proper file name, and then open your plist in Xcode to see if it is properly formatted.
iOS is case sensitive. Are you sure that your file in the bundle is lower case, i.e. "#"scoreData.plist", and not upper case like the name your code uses? Also, verify that these two files are in your bundle - check the build phase or select the files (one at a time) and look in the 3rd Xcode pane in the file attribute section (to verify they are included in your target). If all that looks good then when you try to retrieve the files from your bundle:
Also, don't try to find the file at the root level of the bundle - you should be using:
NSString *path = [[NSBundle mainBundle] pathForResource:#"GameCenterData" ofType:#"plist"];
NSLog(#"PATH is %#", path);
...then use path instead of the code you are using now

Resources