I see example and tutorial after tutorial of how to load tableview from plist when root is dictionary but I have to use a plist that is an array.
plist setup:
root Array
Item 0 Dictionary
name String
Item 1 Dictionary
name String
...
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"food" ofType:#"plist"];
NSArray *tableData = [[NSArray alloc]initWithContentsOfFile:path];
NSArray *thumbnails = [[NSArray alloc] init];
for (NSDictionary *dict in tableData){
NSLog(#"%#",dict); // prints all key value pairs in dictionary
thumbnails = [dict objectForKey:#"name"];
}
NSLog(#"outside %#", thumbnails); // this prints last value added to thumbnails array
}
- (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];
}
NSLog(#"%#", thumbnails); // this loads last value added to array thumbnails
cell.textLabel.text = [thumbnails objectAtIndex:indexPath.row];
return cell;
}
I'm doing this wrong...it doesn't load into my tableview and crashes. I think my for loop is wrong and I think my objectAtIndex is wrong because it crashes at that line. I'm more than happy to share more information. I have datasource and delegate of tableview hooked up to file's owner. I've tested that the tableview works with loading an array directly into it. Please help, I appreciate it.
EDIT:
I placed declaration for the mutable array thumbnails at the top of my .m file as you see below:
#interface SimpleTableViewController ()
#property (nonatomic, strong) NSMutableArray *thumbnails;
#end
These changes still unfortunately leave me with empty thumbnails array in cellForRowAtIndexPath
Same when #property is placed into .h file
EDIT: .m file (latest code)
#import "SimpleTableViewController.h"
#interface SimpleTableViewController ()
#end
#implementation SimpleTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"food" ofType:#"plist"];
NSArray *tableData = [[NSArray alloc]initWithContentsOfFile:path];
NSMutableArray *thumbnails = [NSMutableArray array];
for (NSDictionary *dict in tableData){
NSLog(#"%#",dict); // prints all key value pairs in dictionary
[thumbnails addObject:dict[#"name"]];
}
NSLog(#"outside %#", thumbnails);
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 50;
}
- (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];
}
NSLog(#"other method%#", self.thumbnails); // this loads last value added to array thumbnails
cell.textLabel.text = self.thumbnails[indexPath.row];
return cell;
}
#end
.h file
#import <UIKit/UIKit.h>
#interface SimpleTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (strong, nonatomic) NSMutableArray *thumbnails;
#end
Try
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"food" ofType:#"plist"];
NSArray *tableData = [[NSArray alloc]initWithContentsOfFile:path];
NSMutableArray *thumbnails = [NSMutableArray array];
for (NSDictionary *dict in tableData){
NSLog(#"%#",dict); // prints all key value pairs in dictionary
[thumbnails addObject:dict[#"name"];
}
NSLog(#"outside %#", thumbnails); // this prints last value added to thumbnails array
}
EDIT :
#property (nonatomic, strong) NSMutableArray *thumbnails;
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"food" ofType:#"plist"];
NSArray *tableData = [[NSArray alloc]initWithContentsOfFile:path];
self.thumbnails = [NSMutableArray array];
for (NSDictionary *dict in tableData){
NSLog(#"%#",dict); // prints all key value pairs in dictionary
[self.thumbnails addObject:dict[#"name"];
}
NSLog(#"outside %#", self.thumbnails); // this prints last value added to thumbnails array
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Initialize Cell
cell.textLabel.text = self.thumbnails[indexPath.row];
return cell;
}
There are several mishaps with your code.
A. Looking at the provided plist data, you have an array of dictionaries.
B. This line, allocated thumbnails as a local NSArray.
NSArray *thumbnails = [[NSArray alloc] init];
But this line, what is self.thumbnails which is a property? It is not thumbnails above. And it looks lie key #"name" contains an array rather than a final object.
self.thumbnails = [dict objectForKey:#"name"];
Related
I am aware this question has been asked previously, but the answers provided have not solved my issue.
For instance, I have a very simple array of objects outlined in viewDidLoad:
#implementation MyViewController {
NSArray *tableData;
}
- (void)viewDidLoad
{
[super viewDidLoad];
tableData = [NSArray arrayWithObjects:#"Hello", #"My", #"Name", #"Is"];
NSLog(#"My Data: %#", tableData);
which is called in a tableView using cellForRowAtIndexPath
cell.nameLabel.text = [tableData objectAtIndex:indexPath.row];
This works fine and the NSLog shows my array. However, when i outline tableData outside of viewDidLoad, my array is (null).
My question is, how do I make my array available for the tableView when it is specified outside of ViewDidLoad?
edit: Here is my specific code:
#import <UIKit/UIKit.h>
#import "PhotoView.h"
#interface FrontViewController : UIViewController
#property (nonatomic, retain) UITableView *tableView;
#end
#import "FrontViewController.h"
#import "StreamScreen.h"
#import "API.h"
#import "PhotoView.h"
#import "StreamPhotoScreen.h"
#import "PrivateViewController.h"
#import "SWRevealViewController.h"
#import "PhotoScreen.h"
#import "RearViewController.h"
#import "SimpleTableCell.h"
#interface FrontViewController()
// Private Methods:
- (IBAction)pushExample:(id)sender;
#end
#implementation FrontViewController{
NSArray *tableData;
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = NSLocalizedString(#"Front View", nil);
SWRevealViewController *revealController = [self revealViewController];
[self.navigationController.navigationBar addGestureRecognizer:revealController.panGestureRecognizer];
UIBarButtonItem *revealButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"reveal-icon.png"]
style:UIBarButtonItemStyleBordered target:revealController action:#selector(revealToggle:)];
self.navigationItem.leftBarButtonItem = revealButtonItem;
// This works if I uncomment
//tableData = [NSArray arrayWithObjects:#"Hello", #"My", #"Name", #"Is", nil];
[self refreshStream];
}
-(void)refreshStream {
// call the "stream" command from the web API
[[API sharedInstance] commandWithParams:
[NSMutableDictionary dictionaryWithObjectsAndKeys:#"stream", #"command", nil]
onCompletion:^(NSDictionary *json) {
//got stream
[self showStream:[json objectForKey:#"result"]];
NSMutableArray *myData = [[NSMutableArray alloc] init];
myData = [json objectForKey:#"result"];
NSArray *userNameData = [myData valueForKey:#"username"];
[self loadData];
tableData = userNameData;
[self.tableView reloadData];
// I can see my json array in NSLog
NSLog(#"here's the results: %#", tableData);
}];
}
//This doesn't work either
//-(void)loadData {
// Add the data to your array.
//tableData = [NSArray arrayWithObjects:#"Hello", #"My", #"Name", #"Is", nil];
//NSLog(#"My Data: %#", tableData);
// Now load the table view.
// [self.tableView reloadData];
//}
-(void)showStream:(NSArray*)stream {
for (int i=0;i<[stream count];i++) {
NSDictionary* photo = [stream objectAtIndex:i];
}
NSArray *checkData = [stream valueForKey:#"username"];
//I can see my data in NSLog
NSLog(#"here's the results: %#", checkData);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [tableData count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 78;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableCell";
SimpleTableCell *cell = (SimpleTableCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"SimpleTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.nameLabel.text = [tableData objectAtIndex:indexPath.row];
cell.thumbnailImageView.image = [UIImage imageNamed:[thumbnails objectAtIndex:indexPath.row]];
cell.prepTimeLabel.text = [prepTime objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"didSelectRowAtIndexPath");
/*UIAlertView *messageAlert = [[UIAlertView alloc]
initWithTitle:#"Row Selected" message:#"You've selected a row" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];*/
UIAlertView *messageAlert = [[UIAlertView alloc]
initWithTitle:#"Row Selected" message:[tableData objectAtIndex:indexPath.row] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
// Display the Hello World Message
[messageAlert show];
// Checked the selected row
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"willSelectRowAtIndexPath");
if (indexPath.row == 0) {
return nil;
}
return indexPath;
}
#end
-(void)refreshStream {
// call the "stream" command from the web API
[[API sharedInstance] commandWithParams:
[NSMutableDictionary dictionaryWithObjectsAndKeys:#"stream", #"command", nil]
onCompletion:^(NSDictionary *json) {
//got stream
[self showStream:[json objectForKey:#"result"]];
NSMutableArray *myData = [json objectForKey:#"result"];
NSArray *userNameData = [myData valueForKey:#"username"];
}];
tableData = userNameData;
[self.tableView reloadData];
}
You're falling into a very common trap with asynchronous programming here.
commandWithParams takes a completion block, which is where you are getting the data out of the JSON. This block is not executed until the API call has returned. The sequence of events that happens when you run this code is:
commandWithParams is called
tableData is assigned to the contents of userNameData (which presumably you've also declared somewhere else otherwise this would not even compile)
reloadData is called
.... time passes
The completion block is executed and the JSON is read out into local variables, which are then instantly destroyed.
You need to move the two lines (points 2 and 3 in the list above) inside the completion block. There will be no data for your table until the block returns.
Ok my understanding of your question is that you want to assign variables to your NSArray in another method (not viewDidLoad) and then load the table view.
This is simple, just make a method which is in charge of adding the data to your array and then reload your table view like so:
-(void)viewDidLoad {
[super viewDidLoad];
// Call your method.
[self loadData];
}
-(void)loadData {
// Add the data to your array.
tableData = [NSArray arrayWithObjects:#"Hello", #"My", #"Name", #"Is"];
NSLog(#"My Data: %#", tableData);
// Now load the table view.
[myTableView reloadData];
}
Update 1
It would be much more helpful if you could share your code with us. How your and setting up your tableview, when its being called/etc....
Update 2
Ok well it seems obvious what yoru issue is. Your table view will never load like that. You need to call the tableview reloadData method outside the cellForRowAtIndexPath method.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier = #"SimpleTableCell";
SimpleTableCell *cell = (SimpleTableCell *)[self.tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"SimpleTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.nameLabel.text = [tableData objectAtIndex:indexPath.row];
cell.thumbnailImageView.image = [UIImage imageNamed:[thumbnails objectAtIndex:indexPath.row]];
cell.prepTimeLabel.text = [prepTime objectAtIndex:indexPath.row];
return cell;
}
So the method call [self.tableView reloadData]; should only be called in your refreshStream method.
Update 3
You need to initialize your NSMutableArray before you can add data to it. initialize it in your refreshStrem method like so:
-(void)refreshStream {
// call the "stream" command from the web API
[[API sharedInstance] commandWithParams:[NSMutableDictionary dictionaryWithObjectsAndKeys:#"stream", #"command", nil] onCompletion:^(NSDictionary *json) {
//got stream
[self showStream:[json objectForKey:#"result"]];
NSMutableArray *myData = [[NSMutableArray alloc] init];
myData = [json objectForKey:#"result"];
NSArray *userNameData = [myData valueForKey:#"username"];
}];
tableData = userNameData;
[self.tableView reloadData];
}
Update 4
Ok well after reading #jrturton answer, I think its safe to assume that my answer is rubbish. To anyone reading my answer, please view #jrturton post.
Well I feel pretty sheepish. The answer was simple. I was so hung up on Json and API that all I didn't check the basics. All I need was in my .h file:
#property (strong, nonatomic) IBOutlet UITableView *tableView;
I had originally had:
#property (nonatomic, retain) UITableView *tableView;
Hi everyone I would like to ask I want to show JSON data and customize the position for image and text in Table View.
how can I do like that? please advice me.
You can take a look at the Image Url.
for image 1 it is the result as I've parsing from JSON.
Image 1
for image 2 it is my goal.
Image 2
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>
#property (retain, nonatomic) IBOutlet UITableView *myTableView;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
{
NSMutableArray *myObject;
// A dictionary object
NSDictionary *dict;
// Define keys
NSString *galleryid;
NSString *name;
NSString *titlename;
NSString *thumbnail;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Define keys
galleryid = #"GalleryID";
name = #"Name";
titlename = #"TitleName";
thumbnail = #"Thumbnail";
// Create array to hold dictionaries
myObject = [[NSMutableArray alloc] init];
NSData *jsonData = [NSData dataWithContentsOfURL:
[NSURL URLWithString:#"MY_JSON_URL"]];
id jsonObjects = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
// values in foreach loop
for (NSDictionary *dataDict in jsonObjects) {
NSString *strGalleryID = [dataDict objectForKey:#"GalleryID"];
NSString *strName = [dataDict objectForKey:#"Name"];
NSString *strTitleName = [dataDict objectForKey:#"TitleName"];
NSString *strThumbnail = [dataDict objectForKey:#"Thumbnail"];
dict = [NSDictionary dictionaryWithObjectsAndKeys:
strGalleryID, galleryid,
strName, name,
strTitleName, titlename,
strThumbnail, thumbnail,
nil];
[myObject addObject:dict];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return myObject.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
// Use the default cell style.
cell = [[[UITableViewCell alloc] initWithStyle : UITableViewCellStyleSubtitle
reuseIdentifier : CellIdentifier] autorelease];
}
NSDictionary *tmpDict = [myObject objectAtIndex:indexPath.row];
NSURL *url = [NSURL URLWithString:[tmpDict objectForKey:thumbnail]];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [[UIImage alloc] initWithData:data];
cell.imageView.image = img;
cell.textLabel.text = [tmpDict objectForKey:name];
cell.detailTextLabel.text= [tmpDict objectForKey:titlename];
//[tmpDict objectForKey:memberid]
//[tmpDict objectForKey:name]
//[tmpDict objectForKey:titlename]
//[tmpDict objectForKey:thumbnail]
return cell;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dealloc {
[_myTableView release];
[super dealloc];
}
#end
It will be easy if creating custom TableviewCell (subclass to UITableViewCell) to display the array of objects you have, than using UITableViewCell to display as you desired.
Create custom UITableviewCell with xib or with storyboard and create properties as you needed and give the position and styling for the cell elements.
Bind data for the cells in your cellForRowAtIndexPath method.
For more information, refer: this and this for more info
I'm trying to replicate the contacts table view in my app. So I have a list of contacts displayed in a table view however I would like the table view to be sectioned into all the letters of the alphabet and the names of the contacts to be placed in the section related to the lister letter of their first name. Like this
How do I get that view? So far this is all I have done.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [displayNames count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"ContactsCell";
/*ContactCell *cell = (ContactCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ContactCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}*/
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
// cell.name.text = [displayNames
// objectAtIndex:indexPath.row];
UILabel *namer = (UILabel *)[cell viewWithTag:101];
namer.text=[displayNames
objectAtIndex:indexPath.row];
/*
Get the picture urls from the picture array and then
I loop through the array and initialize all the urls with
a NSUrl and place the loaded urls in anouther nsmutable array
*/
urls = [[NSMutableArray alloc] init];
for (id object in pictures) {
//NSDictionary *names = res[#"image"];
NSString *name = object;
NSURL *url=[[NSURL alloc] initWithString:name];
[urls addObject:url];
}
// cell.profile.image= [UIImage imageWithData:[NSData dataWithContentsOfURL: [urls objectAtIndex:indexPath.row]]];
UIImageView *profiler = (UIImageView *)[cell viewWithTag:100];
profiler.image= [UIImage imageWithData:[NSData dataWithContentsOfURL: [urls objectAtIndex:indexPath.row]]];
return cell;
}
Here is an easy solution using the 3rd party TLIndexPathTools data model class TLIndexPathDataModel. It is specifically designed for working with index paths and sections, so you can accomplish what you need with minimal complexity. And here is a full, working demo.
First define a class to represent a contact. This gives you a place to define firstName, lastName, displayName and sectionName:
#interface Contact : NSObject
#property (strong, nonatomic, readonly) NSString *firstName;
#property (strong, nonatomic, readonly) NSString *lastName;
#property (strong, nonatomic, readonly) NSString *displayName;
#property (strong, nonatomic, readonly) NSString *sectionName;
- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName;
#end
The sectionName property just returns the first character of the firstName. Then if your table view subclasses TLTableViewController, the implementation would look something like this:
#implementation ContactsTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *contacts = [NSMutableArray array];
//get actual list of contacts here...
[contacts addObject:[[Contact alloc] initWithFirstName:#"John" lastName:#"Doe"]];
[contacts addObject:[[Contact alloc] initWithFirstName:#"Sally" lastName:#"Smith"]];
[contacts addObject:[[Contact alloc] initWithFirstName:#"Bob" lastName:#"Marley"]];
[contacts addObject:[[Contact alloc] initWithFirstName:#"Tim" lastName:#"Cook"]];
[contacts addObject:[[Contact alloc] initWithFirstName:#"Jony" lastName:#"Ives"]];
[contacts addObject:[[Contact alloc] initWithFirstName:#"Henry" lastName:#"Ford"]];
//sort by section name
[contacts sortUsingDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"sectionName" ascending:YES]]];
//set the data model
self.indexPathController.dataModel = [[TLIndexPathDataModel alloc] initWithItems:contacts sectionNameKeyPath:#"sectionName" identifierKeyPath:nil];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
//get contact for index path from data model and configure cell
Contact *contact = [self.indexPathController.dataModel itemAtIndexPath:indexPath];
cell.textLabel.text = contact.displayName;
return cell;
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return self.indexPathController.dataModel.sectionNames;
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
return index;
}
#end
The key thing is that TLIndexPathDataModel automatically organizes your data into sections using the sectionNameKeyPath set to #"sectionName". Then in your view controller logic, you can easily access the contact for a given index path by calling:
Contact *contact = [self.indexPathController.dataModel itemAtIndexPath:indexPath];
update
You'd actually want to do a second-level sort on display name:
[contacts sortUsingDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"sectionName" ascending:YES], [NSSortDescriptor sortDescriptorWithKey:#"displayName" ascending:YES]]];
update #2
There is a new block-based initializer for TLIndexPathDataModel that makes this a lot easier if you don't want to define a custom data object just to add a sectionNameKeyPath property. For example, one can use the new initializer to organize a list of strings as illustrated in the "Blocks" sample project:
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *items = [#[
#"Fredricksburg",
#"Jelly Bean",
...
#"Metadata",
#"Fundamental",
#"Cellar Door"] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
//generate section names by taking the first letter of each item
self.indexPathController.dataModel = [[TLIndexPathDataModel alloc] initWithItems:items
sectionNameBlock:^NSString *(id item) {
return [((NSString *)item) substringToIndex:1];
} identifierBlock:nil];
}
I cant find results using my search bar. See my code attached:
// ViewController.m
#import "ViewController.h"
#implementation ViewController
#synthesize names;
#synthesize keys;
#pragma mark - View lifecycle
- (void)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:)];
self.keys = array;
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
#pragma mark -
#pragma mark Table View Data Source Methods
- (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 = [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;
}
http://i.stack.imgur.com/ZBzJZ.png
http://i.stack.imgur.com/ktevQ.png
here is my "h" file:
//
// ViewController.h
// Sections
//
// Created by t r on 3/17/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
<UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate>
{
NSDictionary *names;
NSArray *keys;
}
#property (nonatomic, retain) NSDictionary *names;
#property (nonatomic, retain) NSArray *keys;
#end
The UIsearchBar does not automatically do a search on the tabelview content. You have to implement the UISearchbarDelegate methods to detect the entered text, then reload the table view and return values in the table view delegate methods according to the new array filtered with the search criteria.
I have PropertyList.plist file in the "Supporting Files" folder. I have made a dictionary in it. The plist file is:
MY ViewController.m file code is
#implementation GroupedInexedViewController
{
NSDictionary *names;
NSArray *keys;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"PropertyList"
ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc]
initWithContentsOfFile:path];
names = dict;
[dict release];
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 = [names objectForKey:key];
static NSString *SectionsTableIdentifier = #"SectionsTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SectionsTableIdentifier];
if(cell == nil)
{
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SectionsTableIdentifier] autorelease];
}
cell.textLabel.text = [nameSection objectAtIndex:row];
return cell;
}
-(NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *key = [keys objectAtIndex:section];
return key;
}
Unfortunately the "keys" array doesnt contain any element. Because I made an alert with its count value which is "keys.count" and it was zero. and also the "names" count was also zero. The path variable on vieDidLoad method shows the correct path. but it cant read from the dictionary of the plist file's.
Edit: I used nslog and it shows that in "viewDidLoad" method "names" is able to load the dictionary . but "keys" array is unable to load it.
What #EugeneK said worked but got sigabrt in "cellForRowAtIndexPath" method on the line
cell.textLabel.text = [nameSection objectAtIndex:row];
the error message is
"Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFDictionary objectAtIndex:]: unrecognized selector sent to instance 0x6832440".
The problem was nameSection variable was appearing as a NSDictionary type object which doesn't support objectAtIndex method.
So what I did I know is not a very good way to do it. I am sure there is some other way. I changed the 'viewDidLoad' method to this,
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"PropertyList"
ofType:#"plist"];
names = [[NSDictionary alloc]
initWithContentsOfFile:path];
keys = [names allKeys];
NSString *key = [keys objectAtIndex:0];
names = [names objectForKey:key];
keys = [[[names allKeys] sortedArrayUsingSelector:#selector(compare:)] retain];
NSLog(#"keys = %# names = %#",keys,names);
}
It works! Any idea how to do it better will be appreciated though.
Martin R is right. Please see comments in your code:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"PropertyList"
ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc]
initWithContentsOfFile:path]; //retainCount of dict is 1
names = dict; // you made weak reference to dict
[dict release]; // retainCount is 0 - dict is being dealloced
NSArray *array = [[names allKeys] sortedArrayUsingSelector:
#selector(compare:)]; // you try to get data from dealloced object
keys = array;
}
Try to do the following:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"PropertyList"
ofType:#"plist"];
names = [[NSDictionary alloc]
initWithContentsOfFile:path];
keys = [[[names allKeys] sortedArrayUsingSelector:
#selector(compare:)] retain];
}
And do not forgot to release names and keys in your view controller dealloc.