I have a very simple app to learn how to work with sections in UITableView but there is an exception -
2013-09-17 08:46:19.956 Sections[4497:c07] * -[__NSArrayI
objectAtIndex:]: message sent to deallocated instance 0x9566d40
The whole methods are below - need help.
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"sortednames" ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
self.names = dict;
NSArray *array = [[_names allKeys] sortedArrayUsingSelector:#selector(compare:)];
_keys = array;
}
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(#"%lu", (unsigned long)[_keys count]);
return [_keys count];
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSString *key = [_keys objectAtIndex:section];
NSArray *nameSection = [_names objectForKey:key];
return [nameSection count];
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *key = [_keys objectAtIndex:section];
NSArray *nameSection = [_names objectForKey:key];
static NSString *SectionsTableIdentifier = #"SectionsTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SectionsTableIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SectionsTableIdentifier];
}
cell.textLabel.text = [nameSection objectAtIndex:row];
return cell;
}
- (NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *key = [_keys objectAtIndex:section];
return key;
}
You must retain _keys array like that:
_keys = [[[_names allKeys] sortedArrayUsingSelector:#selector(compare:)] retain];
Here you are first taking values in other array and then passing it to _keys..thats not proper way of doing it..
just directly pass the values to _keys like below
_keys = [[_names allKeys] sortedArrayUsingSelector:#selector(compare:)];
also check for self.names, you are doing the same thing there.
Hope this will help you.
Related
I have the following code. It results in a sectioned uitableview. However, the words in each section are in reverse order (r before a, x before t). I have tried to use nsdescriptor and selectors in various parts of the code but it results in Xcode throwing nsexception ncsf dictionary invalid selector sent localisedcaseinsensitivecompare. This only occurs when I fiddle with the above - even if I don't add localisedCaseInsensitiveCompare (it works with those instances that are already in place).
Any fresh eyes able to solve this.
Thanks
#implementation RCViewController
static NSString *CellIdentifier = #"Cell Identifier";
#synthesize words;
#synthesize alphabetizedWords;
#synthesize wordDictionary;
#synthesize keys;
-(NSDictionary *)alphabetizedWords:(NSArray *)wordsArray {
NSMutableDictionary *buffer = [[NSMutableDictionary alloc]init];
for (int i=0; i <wordsArray.count; i++) {
NSDictionary *keyValue = [wordsArray objectAtIndex:i];
NSString *word = [[wordsArray objectAtIndex:i]objectForKey:#"Word"];
NSString *firstLetter = [[word substringToIndex:1]uppercaseString];
if ([buffer objectForKey:firstLetter]) {
[(NSMutableArray *)[buffer objectForKey:firstLetter]addObject:keyValue];
}
else {
NSMutableArray *mutableArray = [[NSMutableArray alloc]initWithObjects:keyValue, nil];
[buffer setObject:mutableArray forKey:firstLetter];
}
}
NSArray *bufferKeys = [buffer allKeys];
for (int j; j<bufferKeys.count; j++) {
NSString *bufferkey = [bufferKeys objectAtIndex:j];
[(NSMutableArray *)[buffer objectForKey:bufferkey]sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
}
NSDictionary *result = [NSDictionary dictionaryWithDictionary:buffer];
return result;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [keys count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSString *key = [_sortedKeys objectAtIndex:section];
NSArray *wordsForSection = [self.alphabetizedWords objectForKey:key];
return [wordsForSection count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSString *key = [_sortedKeys objectAtIndex:[indexPath section]];
NSArray *wordsForSection = [self.alphabetizedWords objectForKey:key];
NSString *word = [[wordsForSection objectAtIndex:[indexPath row]]objectForKey:#"Word"];
[cell.textLabel setText:word];
return cell;
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *key = [_sortedKeys objectAtIndex:section];
return key;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[self performSegueWithIdentifier:#"showDetail" sender:cell];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
detailViewController *destViewController = segue.destinationViewController;
NSString *key = [_sortedKeys objectAtIndex:[indexPath section]];
NSArray *wordsForSection = [self.alphabetizedWords objectForKey:key];
NSString *word = [[wordsForSection objectAtIndex:[indexPath row]]objectForKey:#"Word"];
destViewController.word = word;
destViewController.definition = [[wordsForSection objectAtIndex:[indexPath row] ]objectForKey:#"Definition"];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *path = [[NSBundle mainBundle]pathForResource:#"words" ofType:#"plist"];
NSArray *wordsDictionary = [NSArray arrayWithContentsOfFile:path];
self.words = wordsDictionary;
self.alphabetizedWords = [self alphabetizedWords:self.words];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifier];
self.keys = [self.alphabetizedWords allKeys];
self.sortedKeys = [keys sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
You are creating an array of dictionaries. Dictionaries don't support the compare method. You therefore can't use a selector based sort. You need to use a different sort method, like sortUsingComparator, or a predicate. (There are lots of different ways to sort arrays.)
If you just have to reverse the position of element of array you could use following
NSArray *wordArray = [[NSArray alloc] initWithObjects:#"Brian",#"Job",#"Bob",#"Ben",#"Robert", nil];
wordArray = [[wordArray reverseObjectEnumerator] allObjects];
Your output will be : Robert, Ben, Bob, Job, Brian.
Evening,
I have managed to section my table alphabetically (:D) but now the cells do not select.
The idea is that the first view presents a list of words. A word is then selected and that leads to a detail view featuring a glossary. But, the cell doesn't select (turns grey not blue?) and the segue in storyboard running from prototype cell to new view doesn't do anything (clicking a cell in simulator doesn't cause the segue).
Does anyone have a suggestion?
I have included my code below, with the prepareforsegue code ready for when needed.
Thanks guys :)
#interface RCViewController ()
#end
#implementation RCViewController
static NSString *CellIdentifier = #"Cell Identifier";
#synthesize fruits;
-(NSDictionary *)alphabetizedFruits:(NSArray *)fruitsArray {
NSMutableDictionary *buffer = [[NSMutableDictionary alloc]init];
for (int i=0; i <fruits.count; i++) {
NSString *fruit = [fruits objectAtIndex:i];
NSString *firstLetter = [[fruit substringToIndex:1]uppercaseString];
if ([buffer objectForKey:firstLetter]) {
[(NSMutableArray *)[buffer objectForKey:firstLetter]addObject:fruit];
}
else {
NSMutableArray *mutableArray = [[NSMutableArray alloc]initWithObjects:fruit, nil];
[buffer setObject:mutableArray forKey:firstLetter];
}
}
NSArray *keys = [buffer allKeys];
for (int j; j<keys.count; j++) {
NSString *key = [keys objectAtIndex:j];
[(NSMutableArray *)[buffer objectForKey:key]sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
}
NSDictionary *result = [NSDictionary dictionaryWithDictionary:buffer];
return result;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSArray *keys = [self.alphabetizedFruits allKeys];
return [keys count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSArray *unsortedKeys = [self.alphabetizedFruits allKeys];
NSArray *sortedKeys = [unsortedKeys sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSString *key = [sortedKeys objectAtIndex:section];
NSArray *fruitsForSection = [self.alphabetizedFruits objectForKey:key];
return [fruitsForSection count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSArray *unsortedKeys = [self.alphabetizedFruits allKeys];
NSArray *sortedKeys = [unsortedKeys sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSString *key = [sortedKeys objectAtIndex:[indexPath section]];
NSArray *fruitsForSection = [self.alphabetizedFruits objectForKey:key];
NSString *fruit = [fruitsForSection objectAtIndex:[indexPath row]];
[cell.textLabel setText:fruit];
return cell;
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSArray *keys = [[self.alphabetizedFruits allKeys]sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSString *key = [keys objectAtIndex:section];
return key;
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
detailViewController *destViewController = segue.destinationViewController;
destViewController.word = [fruits objectAtIndex:indexPath.row];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *path = [[NSBundle mainBundle]pathForResource:#"words" ofType:#"plist"];
NSArray *wordsDictionary = [NSArray arrayWithContentsOfFile:path];
self.fruits = [wordsDictionary valueForKey:#"Word"];
self.alphabetizedFruits = [self alphabetizedFruits:self.fruits];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifier];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
you need to add the didSelectRowAtIndexPath method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// confirm cell is being selected
NSLog(#"didSelectRowAtIndexPath");
// perform the segue by getting the cell selected and passing it to the prepareForSegue method
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[self performSegueWithIdentifier:#"showDetail" sender:cell];
}
I'm making a note taking app for the iPad, and it lets the user draw lines, and right now, it can save the pages of the notebook by saving each page as a PNG in the documents directory. Here's the code I have to save the images:
- (IBAction)saveImage:(id)sender {
UIImage *saveImage = drawImage.image;
if (saveImage != nil)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:
[NSString stringWithFormat: #"%#-%d.png", #"image", numberOfPages] ];
NSData* data = UIImagePNGRepresentation(saveImage);
[data writeToFile:path atomically:YES];
}
}
Just as a side note, numberOfPages is an int that is set to add 1 each time a "new page" button is pressed, this way, each image is named "image1", "image2", etc.
So my question is: How would I set up a UITable so the user can see a list of all the pages they've made.
Thanks so much for your time,
Karl
Each time you make a new page, add a string with the image's name to an array. Then, iterate through the array to populate a UITableView. When the user selects a cell, open the file with that name.
#import "RootViewController.h"
#implementation RootViewController
#synthesize keys, names;
AppDelegate *appD;
- (void)viewDidLoad
{
[super viewDidLoad];
appD = (AppDelegate *)[[UIApplication sharedApplication] delegate];
names = appD.data;
NSArray *array = [[names allKeys] sortedArrayUsingSelector:#selector(compare:)];
keys = array;
}
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return [keys count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [names objectForKey:key];
return [nameSection count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger section = indexPath.section;
NSUInteger row = indexPath.row;
NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [appD.data objectForKey:key];
static NSString *SectionsTableIdentifier = #"SectionsTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SectionsTableIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SectionsTableIdentifier];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.text = [nameSection objectAtIndex:row];
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *key = [keys objectAtIndex:section];
key = [key substringFromIndex:2];
return key;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger section = indexPath.section;
NSUInteger row = indexPath.row;
NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [appD.data objectForKey:key];
appD.theInfo.primary = [nameSection objectAtIndex:row];
[self performSegueWithIdentifier:#"secondarySegue" sender:self];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
This is for a table in a navigation controller that populates itself from a .plist and moves to another table when a cell is clicked. The tableview is connected to the view controller with delegate and datasource outlets.
I load data from plist to uitableview like this:
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *path = [[documentPaths lastObject] stringByAppendingPathComponent:#"data.plist"];
NSMutableDictionary *resultDic = [[NSMutableDictionary alloc] init];
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
NSDictionary *myDict = [NSDictionary dictionaryWithContentsOfFile:path];
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"purpleKey"])
{
NSArray *purple = [myDict objectForKey:#"Purple"];
[resultArray addObject:#"Purple"];
[resultDic setValue:purple forKey:#"Purple"];
}
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"orangeKey"])
{
NSArray *orange = [myDict objectForKey:#"Orange"];
[resultArray addObject:#"Orange"];
[resultDic setValue:orange forKey:#"Orange"];
}
self.tableData = resultDic;
self.sectionsTitle = resultArray;
}
titleForHeaderInSection
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [sectionsTitle objectAtIndex:section];
}
my plist structure
My question is:
How can I manually set title for Purple header and Orange header without to changing names in plist file?
like this: Purple = Category 1, Orange = Category 2
EDIT
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return sectionsTitle.count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [sectionsTitle objectAtIndex:section];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
int num = [[tableData objectForKey:[sectionsTitle objectAtIndex:section]] count];
if (num > 3) {
num = 3;
}
return num;
}
// 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];
}
NSDictionary *dict = [[tableData objectForKey:[sectionsTitle objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
cell.textLabel.numberOfLines = 1;
cell.textLabel.font = [UIFont systemFontOfSize:11];
cell.textLabel.text = [NSString stringWithFormat:#"%# - %#", [dict objectForKey:#"Name"], [dict objectForKey:#"Address"]];
if ([dict objectForKey:#"Address"] == (NULL)) {
//do nothing
}
return cell;
}
From the look of your code, the section headers are coming from resultsArray, which you are populating with the constant strings Orange and Purple.
You can just put different constant strings into that array, unless I'm missing something.
So, instead of
[resultArray addObject:#"Orange"];
Use
[resultArray addObject:#"Category 1"];
I have a plist with an alphabetical indexed list of entries. I now want to add a further level so that each entry has a definition text (like a small dictionary.) I need some advice as to how to modify my plist and code to accommodate this. The existing code is below - it's from the Apress book, but I can't seem to modify it for another level.
NSString *path = [[NSBundle mainBundle] pathForResource:#"sortedglossary" ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc]initWithContentsOfFile:path];
self.names = dict;
[dict release];
NSArray *array = [[names allKeys] sortedArrayUsingSelector:#selector(compare:)];
self.keys = array;
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSLog(#"numberOfSectionInTable start");
return [keys count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"numberOfRowsInSection start");
NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [names objectForKey:key];
return [nameSection count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [names objectForKey:key];
static NSString *SectionsTableIdentifier = #"SectionsTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SectionsTableIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:SectionsTableIdentifier] autorelease];
}
cell.text = [nameSection objectAtIndex:row];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSLog(#"titleForHeader");
NSString *key = [keys objectAtIndex:section];
return key;
}
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return keys;
}
There are a number of ways to accomplish this. Normally I will just create another child of the root node in the plist for definitions.
So your plist would have the array of your entries and it would have another dictionary. The dictionary contains the definition text, keyed by the values from the first array. That way, you can just look up the definition when you need it.
So the structure would look roughly like this:
Root
Entries(NSArray)
0 => entryA(NSString)
1 => entryB(NSString)
...
x => entryX(NSString)
Definitions(NSDictionary)
entryA(NSString) => definitionA(NSString)
entryB(NSString) => definitionB(NSString)
...
entryX(NSString) => definitionX(NSString)