I'm pretty new at this and having a go using Parse.com as the backend server. I'm building a database of vegetables, and want to perform a search on the list pulled from parse.com. I have it all working, except for one annoying thing...
Now, I'm using storyboards and have created a custom cell which includes a PFImage thumbnail view, a label showing the vegetable, and then another label showing the season for the vegetable.
When the viewcontroller is called, the list populates perfectly and lists the vegetables in alphabetical order. Then I drag the window down to reveal the search bar. I begin typing in a vegetable name, and as I do so the original table data rows begin disappearing (as they should), but the problem is the original table data sticks around. So, for instance, I'll type "carrot", and all the rows disappear except the top row which still holds a thumbnail of an artichoke (and the label "Artichoke" as well). But overlayed on that row is also the word "Carrots", which is another vegetable in the list. If I tap on it, it properly seques to my detail view controller showing carrots. So everything is working properly, but I can't figure out how to make it so the search results aren't being written over the top of the original data.
Here's the code portions:
- (void)viewDidLoad
{
self.tableView.backgroundColor = [UIColor clearColor];
self.tableView.backgroundView=[[UIImageView alloc] initWithImage:
[UIImage imageNamed:#"bg.jpg"]];
[super viewDidLoad];
//add the search bar
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
self.tableView.tableHeaderView = self.searchBar;
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
self.searchController.delegate = self;
CGPoint offset = CGPointMake(0, self.searchBar.frame.size.height);
self.tableView.contentOffset = offset;
self.searchResults = [NSMutableArray array];
//done adding search bar
}
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
// If no objects are loaded in memory, we look to the cache first to fill the table
// and then subsequently do a query against the network.
/* if ([self.objects count] == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}*/
[query orderByAscending:#"vegetable"];
return query;
}
// Override to customize the look of a cell representing an object. The default is to display
// a UITableViewCellStyleDefault style cell with the label being the first key in the object.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *simpleTableIdentifier = #"VegetableCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
// more search stuff
if (tableView == self.tableView) {
cell.backgroundColor = cell.backgroundColor;
}
else if(tableView == self.searchDisplayController.searchResultsTableView) {
PFObject *searchedVeggie = [self.searchResults objectAtIndex:indexPath.row];
cell.textLabel.text = [searchedVeggie objectForKey:#"vegetable"];
cell.backgroundColor = [UIColor whiteColor];
tableView.rowHeight = self.tableView.rowHeight;
}
PFFile *thumbnail = [object objectForKey:#"vegetableImageFile"];
PFImageView *thumbnailImageView = (PFImageView*)[cell viewWithTag:100];
thumbnailImageView.image = [UIImage imageNamed:#"placeholder.jpg"];
thumbnailImageView.file = thumbnail;
[thumbnailImageView loadInBackground];
UILabel *vegetableName = (UILabel*) [cell viewWithTag:101];
vegetableName.text = [object objectForKey:#"vegetable"];
UILabel *vegetableSeason = (UILabel*) [cell viewWithTag:102];
vegetableSeason.text = [object objectForKey:#"vegetableSeason"];
return cell;
}
Lower in the code is my prepareForSeque code and other search methods. I know it's a little ugly with repeated code, but I've been trying all sorts of things to fix my issue and wasn't going to get around to cleaning things up until I figured out the issue. Also, I created a new column on parse.com's data browser called lowerCaseVegetable since the search is case sensitive. So the search is actually performed on that column, but is displayed using the normal "vegetable" column, which has the vegetable name capitalized.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showVegetableDetail"]) {
if (self.searchDisplayController.active) {
NSLog(#"Search Display Controller");
VeggieDetailViewController *destViewController = segue.destinationViewController;
PFObject *object = [self.searchResults objectAtIndex: self.searchDisplayController.searchResultsTableView.indexPathForSelectedRow.row];
Vegetables *vegetables = [[Vegetables alloc] init];
vegetables.vegetable = [object objectForKey:#"vegetable"];
vegetables.vegetableInfo = [object objectForKey:#"vegetableInfo"];
vegetables.vegetableImageFile = [object objectForKey:#"vegetableImageFile"];
vegetables.vegetableSeason = [object objectForKey:#"vegetableSeason"];
destViewController.vegetables = vegetables;
} else {
NSLog(#"Default Display Controller");
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
VeggieDetailViewController *destViewController = segue.destinationViewController;
PFObject *object = [self.objects objectAtIndex:indexPath.row];
Vegetables *vegetables = [[Vegetables alloc] init];
vegetables.vegetable = [object objectForKey:#"vegetable"];
vegetables.vegetableInfo = [object objectForKey:#"vegetableInfo"];
vegetables.vegetableImageFile = [object objectForKey:#"vegetableImageFile"];
vegetables.vegetableSeason = [object objectForKey:#"vegetableSeason"];
destViewController.vegetables = vegetables;
}
}
}
// other search stuff
-(void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
PFQuery *query = [PFQuery queryWithClassName: #"Vegetables"];
[query whereKeyExists:#"lowerCaseVegetable"];
[query whereKey:#"lowerCaseVegetable" containsString:searchTerm];
NSArray *results = [query findObjects];
[self.searchResults addObjectsFromArray:results];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterResults:searchString];
return YES;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.tableView) {
return self.objects.count;
} else {
return self.searchResults.count;
}
}
-(void)callbackLoadObjectsFromParse:(NSArray *)result error:(NSError *)error {
if (!error) {
[self.searchResults removeAllObjects];
[self.searchResults addObjectsFromArray:result];
[self.searchDisplayController.searchResultsTableView reloadData];
} else {
// NSLog(#”Error: %# %#”, [error userInfo]);
}
}
I have a feeling I'm just making a stupid newbie mistake here, but I've only been at this since May, and specifically fighting this issue the last two weeks. I figured it was about time to ask for help.
I just implemented search in my parse app and I think I see the problem:
In your filterResults method try using findObjectsInBackgroundWithBlock instead of findObjects like this:
[query findObjectsInBackgroundWithBlock:^(NSArray *array, NSError *error){
[self.searchResults addObjectsFromArray:array];
[self.searchDisplayController.searchResultsTableView reloadData];
}];
prepareForSegue:
Vegetables *vegetables = [[Vegetables alloc] init];
vegetables.vegetable = [self.selectedObject objectForKey:#"vegetable"];
vegetables.vegetableInfo = [self.selectedObject objectForKey:#"vegetableInfo"];
vegetables.vegetableImageFile = [self.selectedObject objectForKey:#"vegetableImageFile"];
vegetables.vegetableSeason = [self.selectedObject objectForKey:#"vegetableSeason"];
destViewController.vegetables = vegetables;
Related
I use a PFQueryTableViewController, and am having difficulties adding a SearchBar to the table view. The table view shows all the items from the specific Parse class, but I'm having issues getting the search functionality to work. Clicking search bar causes app to crash with error NSArray containsString unrecognized selector. My code for the search bar is below...as of now, I'm not even trying to do anything but log if it detects the text, because I don't know how to get the table to properly update. The PFQTVC automatically provides the tableview with data from its query, and I don't know how to update this
-(void) updateSearchResultsForSearchController:(UISearchController *)searchController {
NSString *searchText = self.searchController.searchBar.text;
NSLog(#"searchText %#", searchText);
isFiltered = YES;
searchResults = [[NSMutableArray alloc] init];
if ( [[self.theObject valueForKey:#"NAME"] containsString:searchText]) {
NSLog(#"FINE");
}
[self.tableView reloadData];
}
The rest of the code for the table view is below. I have some other things added for the cellForIndexPath code to show a checkmark on a row after a user selects it. What am I doing wrong for being able to search?
- (id)initWithCoder:(NSCoder *)aDecoder
{
NSLog(#"initwithcoder");
self = [super initWithCoder:aDecoder];
if (self) {
NSLog(#"self");
// The className to query on
self.parseClassName = #"SongsList";
// Whether the built-in pull-to-refresh is enabled
self.pullToRefreshEnabled = YES;
// Whether the built-in pagination is enabled
self.paginationEnabled = NO;
// The number of objects to show per page
self.objectsPerPage = 0;
}
return self;
}
- (PFQuery *)queryForTable {
NSLog(#"QUERY");
PFQuery *query = [PFQuery queryWithClassName:#"SongsList"];
// If no objects are loaded in memory, we look to the cache first to fill the table
// and then subsequently do a query against the network.
if (self.objects.count == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
NSLog(#"Count%lu", self.objects.count);
[query orderByAscending:#"NAME"];
query.limit = 2000;
return query;
}
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
object:(PFObject *)object
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
self.theObject = object;
RSSEntryDirectory *entry = [_allEntries objectAtIndex:indexPath.row];
if ([self.chosen containsObject:object[#"NAME"]]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
cell.textLabel.text = object[#"NAME"];
if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad )
{
UIFont *cellFont = [UIFont fontWithName:#"ArialRoundedMTBold" size:38];
cell.textLabel.font = cellFont;
UIFont *cellFont2 = [UIFont fontWithName:#"ArialRoundedMTBold" size:24];
cell.detailTextLabel.font = cellFont2;
}
else {
UIFont *cellFont = [UIFont fontWithName:#"ArialRoundedMTBold" size:20];
cell.textLabel.font = cellFont;
UIFont *cellFont2 = [UIFont fontWithName:#"ArialRoundedMTBold" size:12];
cell.detailTextLabel.font = cellFont2;
}
return cell;
}
I'm using UITableViewController for displaying data from Parse. It runs perfectly on my Xcode Simulator as i think there's no latency in network. But whenever i'm uploading the code to AppStore for Testing. The very first time i run the app it has to load a couple of restaurant's from Parse and display in UITableViewController. Upon clicking a row the first rows data is being loaded into the 3rd row and 4th row data loading in 6th row data irregularly. Why is the data being loaded very unevenly ? Here's my
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *cellIdentifier = #"restaurantIdentifier";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
PFObject *tempObject = [self.objectArray objectAtIndex:indexPath.row];
PFFile *imageFile = [tempObject objectForKey:#"RestaurantIcon"];
PFImageView *imageView = [[PFImageView alloc] init];
imageView.file = imageFile;
[imageView loadInBackground:^(UIImage *img,NSError *error){
if(!error){
cell.imageCell.image = imageView.image;
}
}];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.imageView.contentMode = UIViewContentModeScaleAspectFit;
cell.imageView.layer.masksToBounds = YES;
cell.imageView.layer.cornerRadius = 4;
cell.imageView.frame = self.view.bounds;
cell.cellLabel.text = [tempObject objectForKey:#"RestaurantName"];
[self.hotelNamesArray addObject:[tempObject objectForKey:#"RestaurantName"]];
cell.cellLabel.lineBreakMode = NSLineBreakByWordWrapping;
return cell;
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
_restaurantName = [self.hotelNamesArray objectAtIndex:indexPath.row];
self.restaurantMenuNameArray = [[NSMutableArray alloc] init];
PFQuery *query = [PFQuery queryWithClassName:[self.hotelNamesArray objectAtIndex:indexPath.row]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *obj in objects) {
if (![_restaurantMenuNameArray containsObject:[obj objectForKey:#"RestaurantMenuName"]]) {
NSLog(#"restaurantmenunames are %#",[obj objectForKey:#"RestaurantMenuName"]);
if ([obj objectForKey:#"RestaurantMenuName"] ==nil) {
[self performSegueWithIdentifier:#"restaurantDetail" sender:self];
return;
}else {
[_restaurantMenuNameArray addObject: [obj objectForKey:#"RestaurantMenuName"]];
}
}
}
}else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
[self.tableView reloadData];
NSLog(#"restaurantMenuNames is %#",_restaurantMenuNameArray);
[self performSegueWithIdentifier:#"restaurantDetail" sender:self];
}];
}
Thanks in advance.
If you mean the images get in the wrong cell, you have to consider that cells are recycled when you scroll, and that if the image loading takes a bit too long, you may get the result after the cell has been reused.
You need to check that the cell is still for the item/row you want (you could store the row in the cell's tag and check it before setting the image in the completion handler, for instance).
If it's other data that is mixed up, then you'll need to show us the code that loads that data.
I'm trying to do a search function in my table view, to return objects from my Parse.com class.
I'm getting this error when I try to make a search in the UISearchBar:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier FeaturedTableViewCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
Here's how I'm doing it:
#interface DiscoverViewController () <UISearchBarDelegate, UISearchDisplayDelegate>
#property (nonatomic, retain) PFQuery *query;
#property (nonatomic, retain) PFObject *category;
#end
#implementation DailyDiscoverViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
self.searchController.delegate = self;
self.searchResults = [NSMutableArray array];
_categories = [[NSMutableArray alloc] initWithCapacity:100];
}
- (void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
PFQuery *query = [PFQuery queryWithClassName:#"Projects"];
[query whereKeyExists:#"name"];
[query whereKey:#"name" containsString:searchTerm];
NSArray *results = [query findObjects];
[self.searchResults addObjectsFromArray:results];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterResults:searchString];
return YES;
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
}
- (void)refresh {
PFQuery *query = [PFQuery queryWithClassName:#"Categories"];
[query orderByDescending:#"name"];
[query findObjectsInBackgroundWithBlock:^(NSArray *posts, NSError *error) {
if (!error) {
//NSLog(#"%#", posts);
} else {
}
[_categories setArray:posts];
[self.tableView reloadData];
[_loadingView stopAnimating];
[_refreshControl endRefreshing];
}];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.tableView) {
if (section == 0) {
return 1;
} else {
return self.categories.count;
}
} else {
return self.searchResults.count;
}
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
return 320;
} else {
return 52;
}
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
_featuredObject = [_featured objectAtIndex:indexPath.row];
DiscoverFeaturedTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"DiscoverFeaturedTableViewCell" forIndexPath:indexPath];
[(PFFile*)_featuredObject[#"picture"] getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
cell.image1.image = [UIImage imageWithData:data];
}];
return cell;
} else {
_category = [_categories objectAtIndex:indexPath.row];
DiscoverTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"DiscoverTableViewCell" forIndexPath:indexPath];
cell.name.text = _category[#"name"];
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]) {
PFUser *obj2 = [self.searchResults objectAtIndex:indexPath.row];
PFQuery *query = [PFQuery queryWithClassName:#"Projects"];
PFObject *searchedUser = [query getObjectWithId:obj2.objectId];
NSString *first = [searchedUser objectForKey:#"name"];
cell.name.text = [first substringToIndex:1];
cell.name.text = first;
return cell;
}
return cell;
}
}
#end
I think you can look this stack over flow post : POST
K.
Ok,
Try it on viewDidLoad:
YourCustomCell *yourCustomCell = [UINib
nibWithNibName:#"YourCustomCell" bundle:nil]; [self.tableView
registerNib:cellNib forCellReuseIdentifier:#"cell"];
Note: Your custom cell must have a .xib of course.
You could try this, I remember running into this exact issue and this was one of the solutions I tried:
[self.tableView registerClass:[DiscoverTableViewCell class] forCellReuseIdentifier:#"DiscoverTableViewCell"];
However, I distinctly remember that this didn't work. It turned out I hadn't correctly connected the cells in the storyboard. Furthermore, make sure that the class of your storyboard cell has been changed to represent your cell's custom class.
I am following this forum: https://parse.com/questions/using-pfquerytableviewcontroller-for-uitableview-sections
If you go to the link above and read the title you will see that I am trying to create sections in my PFQueryTableViewController. After copying the code form the link above I was able to section my TableView just fine.. it works great! Here is my problem. (The best way to do this is to give an example). Imagine I have 3 sections of cells with 2 cells in each section
A
B
C
D
E
F
When I click on A I get A result. When I click on B I get B result.
But when I click on C I get A, and when I click on D I get B.
Also When I click on E I get A and when I click on F I get B.
Basically it knows there are sections but it is making it as if there is 1 section that continuously repeats (3) times.
Here is the catch. The cells int he TableView display the correct information. It is after you click on the cells that the wrong information it transferred. Maybe I am missing something but I don't know where.
Here is my code:
#property (nonatomic, retain) NSMutableDictionary *sections;
#property (nonatomic, retain) NSMutableDictionary *sectionToSportTypeMap;
#implementation AllDataViewController
#synthesize sections = _sections;
#synthesize sectionToSportTypeMap = _sectionToSportTypeMap;
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if (self) {
// Custom the table
// The className to query on
self.parseClassName = #"schedule";
self.textKey = #"name";
self.pullToRefreshEnabled = YES;
self.paginationEnabled = YES;
self.objectsPerPage = 25;
self.sections = [NSMutableDictionary dictionary];
self.sectionToSportTypeMap = [NSMutableDictionary dictionary];
}
return self;
}
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
// If Pull To Refresh is enabled, query against the network by default.
if (self.pullToRefreshEnabled) {
query.cachePolicy = kPFCachePolicyNetworkOnly;
}
// If no objects are loaded in memory, we look to the cache first to fill the table
// and then subsequently do a query against the network.
if (self.objects.count == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
[query orderByAscending:#"order"];
return query;
}
- (void) objectsDidLoad:(NSError *)error
{
[super objectsDidLoad:error];
[self.sections removeAllObjects];
[self.sectionToSportTypeMap removeAllObjects];
NSInteger section = 0;
NSInteger rowIndex = 0;
for (PFObject *object in self.objects) {
NSString *sportType = [object objectForKey:#"order"];
NSMutableArray *objectsInSection = [self.sections objectForKey:sportType];
if (!objectsInSection) {
objectsInSection = [NSMutableArray array];
// this is the first time we see this sportType - increment the section index
[self.sectionToSportTypeMap setObject:sportType forKey:[NSNumber numberWithInt:section++]];
}
[objectsInSection addObject:[NSNumber numberWithInt:rowIndex++]];
[self.sections setObject:objectsInSection forKey:sportType];
}
}
- (PFObject *)objectAtIndexPath:(NSIndexPath *)indexPath {
NSString *sportType = [self sportTypeForSection:indexPath.section];
NSArray *rowIndecesInSection = [self.sections objectForKey:sportType];
NSNumber *rowIndex = [rowIndecesInSection objectAtIndex:indexPath.row];
return [self.objects objectAtIndex:[rowIndex intValue]];
}
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.sections.allKeys.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSString *sportType = [self sportTypeForSection:section];
NSArray *rowIndecesInSection = [self.sections objectForKey:sportType];
return rowIndecesInSection.count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *sportType = [self sportTypeForSection:section];
return sportType;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
PFObject *selectedObject = [self objectAtIndexPath:indexPath];
}
- (NSString *)sportTypeForSection:(NSInteger)section {
return [self.sectionToSportTypeMap objectForKey:[NSNumber numberWithInt:section]];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showDataDetail"]) { //showRecipeDetail
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
//PFObject *selectedObject = [self objectAtIndexPath:indexPath];
DataDetailViewController *destViewController = segue.destinationViewController;
PFObject *object = [self.objects objectAtIndex:indexPath.row];
AllData *data = [[AllData alloc] init];
data.title = [object objectForKey:#"title"];
data.imageFile = [object objectForKey:#"imageFile"];
data.date = [object objectForKey:#"date"];
data.information = [object objectForKey:#"information"];
destViewController.data = data;
}
}
If you have multiple sections in your table, but you're only segueing to the info in the first section regardless of the section you've selected, perhaps it is because of this line:
PFObject *object = [self.objects objectAtIndex:indexPath.row];
where the object grabbed from the self.objects array is dependent solely on the row without regard for the section.
So as your code stands now, if row 1 in section 2 is selected, assuming your data is in a standard order (ex. A, B, C, D, etc), you'll still segue to row 1 in section 1. To get your code to work as intended, you may have to change this line:
PFObject *object = [self.objects objectAtIndex:indexPath.row];
to
PFObject *object = [self.objects objectAtIndex:indexPath.row * (indexPath.section + 1)];
such that you multiply by the (section + 1) to access the proper index in the self.objects array.
Try nesting it an IF ELSE Statement like so
if (indexPath.section == 0) {
//A
if (indexPath.row == 0) {
PFObject *object = [self.messagesArray objectAtIndex:indexPath.row];
}
}
I have created a UITableView which contains cells that display Users. Each cell is added within this method -tableView:cellForRowAtIndexPath:. And each cell has content linked to the specific user, like an UIImageView and UILabel.
The UITableView works properly as long as there is no more than 9-10 cells displaying. But when the number of cells become higher, so the user has to scroll down to view them all, that's when the odd behavior begins. Content from the first, second, third and so on, is added to cell number eleven, twelve, thirteen and so on. And when the user then scroll up, the content that is supposed to be on number 11, 12, 13 is now in the first, second and third cell...
I hope someone understands my problem, and know what is wrong here..
Here is the code I user to add cells.. Ignore the parse stuff though, I dont think it is relevant
- (UITableViewCell *)tableView:(UITableView *)tableview cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleTableIdentifier = #"SimpleTableCell";
UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
if (tableview == commentViewTableView) {
//Ignore this
} else if (tableview == tableView) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 34, 34)];
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.clipsToBounds = YES;
[cell addSubview:imageView];
UILabel *usernameLabel = [[UILabel alloc] initWithFrame:CGRectMake(44, 0, 160, 44)];
usernameLabel.textAlignment = NSTextAlignmentLeft;
usernameLabel.font = [UIFont systemFontOfSize:17];
usernameLabel.backgroundColor = [UIColor clearColor];
[cell addSubview:usernameLabel];
UIImageView *hitImageView = [[UIImageView alloc] initWithFrame:CGRectMake(245, 9.5, 25, 25)];
hitImageView.contentMode = UIViewContentModeScaleAspectFill;
hitImageView.clipsToBounds = YES;
hitImageView.image = [UIImage imageNamed:#"hit.png"];
[cell addSubview:hitImageView];
NSString *key = //Code to retrieve userKey
PFQuery *query = [PFUser query];
[query whereKey:#"objectId" equalTo:key];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error) {
[[object objectForKey:#"image1"] getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
NSString *ageString = [[NSString alloc] initWithFormat:#"%li", (long)age];
imageView.image = [UIImage imageWithData:data];
usernameLabel.text = [NSString stringWithFormat:#"%#, %#", [object objectForKey:#"username"], ageString];
}
}];
}
}];
}
}
return cell;
}
I solved my problem by doing changing the cell identifier to be unique. I don't know if this actually is the way to do it, or if it is good practice, but when I did it solved my problem. So it would be good with some feedback to know if this will cause any other problems I'm might be missing?
NSString *identifier = [NSString stringWithFormat:#"Cell%li", indexPath.row];
UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
//My code..
}
Change your code like this:
- (UITableViewCell *)tableView:(UITableView *)tableview cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleTableIdentifier = #"SimpleTableCell";
UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
} // CLOSED PARANTHESES HERE!!!!
if (tableview == commentViewTableView) {
//Ignore this
} else if (tableview == tableView) {
// ... rest of your code here
}
}
There are a couple problems with the code. One is that special care must be taken with asynch calls inside the cellForRowAtIndex: datasource method. Another is that the cells are reused, so adding subviews to them each time they come into view will pile subviews upon subview.
Lets start with the asynch operation. #nburk correctly points out the issue, but its an overstatement to say you "can't do it". You could preload everything, but then user must wait for the whole table to be ready before they can see any of it. A good strategy here is lazy load.
Lazy load depends on a place to cache the loaded result. So lets make your datasource array an array of mutable dictionaries that look like this:
#{#"user": aPFUser, #"image": aUIImage };
It makes sense to prefetch the users, otherwise, you don't even know how many you have, so, in viewWillAppear:
// setup your model as #property(strong,nonatomic) NSMutableArray *users;
PFQuery *query = [PFUser query];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// build the datasource
self.users = [NSMutableArray array];
for (PFUser *user in objects) {
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary:
#{ #"user": user };
];
}
[self.tableView reloadData];
}
}];
Now, in cellForRowAtIndexPath you do this:
NSMutableDictionary *userDictionary = self.users[indexPath.row];
// in the lazy pattern, if the model is initialized, we're done
// start by assuming the best
imageView.image = userDictionary[#"image"];
// but the image might be nil (it will start out that way) so, load...
PFQuery *query = [PFUser query];
[query whereKey:#"objectId" equalTo:key];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error) {
[[object objectForKey:#"image1"] getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
UIImage *image = [UIImage imageWithData:data];
// this is the important part: this code doesn't run when the rest
// of the method runs. It happens later, when the request completes
// so don't assume anything about the state of the table. Instead
// treat the table like you would in other view controller methods
userDictionary[#"image"] = image;
// don't fool around with cell (it might be reused). Instead
// just tell the table to reload this row
[tableView reloadRowsAtIndexPaths:#[indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
}];
}
}];
The next time that row scrolls into view, the data from the asynch request will be cached in your user dictionary.
Problem two is simpler: the code builds subviews unconditionally, even if the (reused) cell already has that subview. The answer, again, is that laziness is your friend. Try to get the subview from the cell, and only build it if you must...
// change all of your subview-building code to do this:
UIImageView *imageView = (UIImageView *)[cell viewWithTag:32];
if (!imageView) {
imageView = [[UIImageView alloc] init....
// same code as you had here, adding...
imageView.tag = 32;
}
// and so on for the cell's other subviews. be sure to advance the tag (33, 34, etc)
In sum, the cellForRowAtIndexPath has a few sections.
dequeue the cell
lazy-build subviews as above
as above: access your model and optimistically init the subviews from the model
if part of the model is missing, do an asynch call, update the model,
and reload the cell when done