Parse iOS SDK: Query using UISearchDisplayController - ios

Scenario = I have an app that allows users to search for other users that use the service. In the search page there is a UISearchDisplayController that when a user begins typing in the search bar, a tableView will programmatically appear (just like any other UISearchDisplayController) and filter all of the users in the database depending on whats being typed ('begins-with'). So the user will begin typing, "B... r...." and users will begin to populate the tableView from "Brad" to "Brandon" and so on based on the text being inputted.
Question = How would one go about designing the parse query to achieve this effect?
Specific Questions =
1) When and Where to begin the initial query?...
PFQuery *searchQuery = [PFUser query];
[searchQuery whereKey:#"username" containsString:controller.searchBar.text];
[searchQuery orderByDescending:#"updatedAt"];
[searchQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
NSLog(#"%#", objects);
searchArray = objects;
}];
in "searchDisplayControllerDidBeginSearch"?
2) When and Where do I put the logic to fill in the tableView?
PFObject *searchObject = [searchArray objectAtIndexPath:indexPath.row];
cell.nameLabel.text = searchObject[#"name"];
in "cellForRowAtIndexPath"?
If there is anyone out there that knows this and can help me out Id appreciate it.

Here is a simple example:
#import <Parse/Parse.h>
#interface MySearchController : PFQueryTableViewController
#end
And implementation
#import "MySearchController.h"
#interface MySearchController() <UISearchBarDelegate, UISearchDisplayDelegate>
#property (strong, nonatomic) IBOutlet UISearchBar *searchBar;
#property (nonatomic, strong) NSMutableArray *searchResults;
#end
#implementation MySearchController
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if (self) {
// get users
self.parseClassName = [PFUser parseClassName];
self.pullToRefreshEnabled = YES;
self.paginationEnabled = YES;
self.objectsPerPage = 10;
self.searchResults = [NSMutableArray new];
}
return self;
}
- (void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
for (PFUser *user in self.objects)
{
NSString *username = user.username;
if ([[username lowercaseString] hasPrefix:[searchTerm lowercaseString]])
{
[self.searchResults addObject:user];
}
}
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString: (NSString *)searchString {
[self filterResults:searchString];
return YES;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return (tableView == self.tableView) ? self.objects.count : self.searchResults.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
PFUser *user = (tableView == self.tableView) ? self.objects[indexPath.row] : self.searchResults[indexPath.row];
static NSString *identifier = #"reuseIdentifier";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:identifier];
}
cell.textLabel.text = user.username;
return cell;
}
#end
The main thing to note about this that threw me off is that you have two table views, so you have to be careful.
One of the table views is from the original query, it will give you all users, that one is self.tableView. The other is from the search results, self.searchDisplayController.searchResultsTableView. The latter is active while searching. Therefore, you must return different values for each regular tableviewcontroller method. The number of rows is either self.objects.count or self.searchResults.count. The correct user is either self.objects[indexPath.row] or self.searchResults[indexPath.row]. It is easy to check which table view you're dealing with in a given protocol method, just use this condition:
(tableView == self.tableView)

Related

Display (All) Data from two classes on Parse.com in UITableView

I want to be able to display all of the data from two classes from Parse.com but it is not working. Every time I go to open the tableview the app crashes. Here is my code. I used the guidance from https://www.parse.com/questions/query-with-two-classes to aid me.
- (id)initWithStyle:(UITableViewStyle)style {
self = [super initWithStyle:style];
if (self) {
// This table displays items in the class
self.parseClassName = #"rep";
self.parseClassName = #"rep2";
self.pullToRefreshEnabled = YES;
self.paginationEnabled = NO;
self.objectsPerPage = 100;
}
return self;
}
- (PFQuery *)queryForTable {
PFQuery *1query = [PFQuery queryWithClassName:#"rep"];
PFQuery *2query = [PFQuery queryWithClassName:#"rep2"];
PFQuery *query = [PFQuery orQueryWithSubqueries:#[1query,2query]];
[query whereKey:#"user" equalTo:[PFUser currentUser]];
[query findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {
}];
return query;
}
- (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];
}
// Configure the cell to show todo item with a priority at the bottom
cell.textLabel.text = [object objectForKey:#"name"];
cell.detailTextLabel.text = [NSString stringWithFormat:#"Username: %#",
[object objectForKey:#"username"]];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
// Remove the row from data model
PFObject *objectToDel = [self.objects objectAtIndex:indexPath.row];
[objectToDel deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
UIAlertView *Alert = [[UIAlertView alloc] initWithTitle:#"Item Was Deleted Successfully. Pull Down to Refresh Tab" message:nil delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[Alert show];
}
];
}
#end
> Specwatch[668:192464] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'All sub queries of an
> `or` query should be on the same class.'
> *** First throw call stack:
> (0x182319900 0x181987f80 0x182319848 0x1000bd8b4 0x100025c10 0x100072514 0x100071e00 0x18700c0c0 0x1870cbda8 0x1870cbc80
> 0x1870caec8 0x1870caa6c 0x1870ca694 0x1870ca5fc 0x187007778
> 0x184a16b2c 0x184a11738 0x184a115f8 0x184a10c94 0x184a109dc
> 0x184a0a0cc 0x1822d0588 0x1822ce32c 0x1822ce75c 0x1821fd680
> 0x18370c088 0x187074d90 0x10006d054 0x181d9e8b8)
> libc++abi.dylib: terminating with uncaught exception of type NSException
> (lldb)
I'll try to summarize how to go about emancipating yourself from the PFQueryTableViewController (and UITableViewController for that matter. The world would be a slightly happier place if neither of these classes had been invented (imo)). Create a UIViewController subclass called ViewController.
In IB, add a UIViewController (setting its class to ViewController), give it a UITableView constrained to the edges of the view. Hook up the datasource, delegate and an outlet called tableView. Add a prototype cell. For now, just use a standard subtitle or left detail UITableViewCell. Give the cell an identifier of #"cell".
// viewcontroller.m
#interface ViewController () <UITableViewDataSource, UITableViewDelegate>
#property(weak,nonatomic) IBOutlet UITableView *tableView;
#property(strong,nonatomic) NSMutableArray *objects;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[PFCloud callFunctionInBackground:#"getTwoClasses" withParameters:nil block:^(NSArray *objects, NSError *error) {
self.objects = objects;
[self.tableView reloadData];
}];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.objects.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
PFObject *object = self.objects[indexPath.row];
cell.textLabel.text = [object objectId];
cell.detailTextLabel.text = [object parseClassName];
return cell;
}
That very simple implementation should be all that's needed on the client. On the server, we just need a cloud function called "getTwoClasses" to get objects from two classes (rep and rep2).
Deploy this function to do that...
Parse.Cloud.define("getTwoClasses", function(request, response) {
var reps;
var user = request.user;
var repQuery = new Parse.Query("rep");
repQuery.equalTo("user", user);
query.find().then(function(result) {
reps = result;
var rep2Query = new Parse.Query("rep2");
rep2Query.equalTo("user", user);
return rep2Query.find();
}).then(function(result) {
response.success(reps.concat(result));
}, function(error) {
response.error(error);
});
});

PFQueryTableViewController Multiple Sections

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];
}
}

PFQueryTableViewController loading for 5 minutes

I followed this tutorial here https://parse.com/questions/using-pfquerytableviewcontroller-for-uitableview-sections and was able to create a beautiful table with sections, but it takes forever to load! Well, not forever, 5 minutes to be exact. My table in parse has 587 rows in it and it takes 5 minutes to load all of the objects into sections. The first few minutes shows the "Loading..." on the blank view, then there is an empty tableview, and finally all of the objects load. Is there a reason something like this is taking so long? I can't have my users wait 5 minutes for something to load. This tableview is displayed during the register process. It is a list of schools and the new user must select which school they are from. The sections organize the schools based on location, and there are about 30 sections. Any suggestions for getting this to load faster?
Here is the code for the SchoolFinderViewController.m file
#import "SchoolFinderViewController.h"
#interface SchoolFinderViewController ()
#property (nonatomic, retain) NSMutableDictionary *sections;
#property (nonatomic, retain) NSMutableDictionary *sectionToRegionMap;
#end
#implementation SchoolFinderViewController
#synthesize sections = _sections;
#synthesize sectionToRegionMap = _sectionToRegionMap;
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
self.parseClassName = #"School";
self.textKey = #"Name";
self.pullToRefreshEnabled = NO;
self.paginationEnabled = YES;
self.objectsPerPage = 600;
self.sections = [NSMutableDictionary dictionary];
self.sectionToRegionMap = [NSMutableDictionary dictionary];
}
return self;
}
-(void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Schools";
}
#pragma mark - PFQueryTableViewController
- (void)objectsDidLoad:(NSError *)error {
[super objectsDidLoad:error];
// This method is called every time objects are loaded from Parse via the PFQuery
NSLog(#"Count in objectsDidLoad: %lu", (unsigned long)[self.objects count]);
[self.sections removeAllObjects];
[self.sectionToRegionMap removeAllObjects];
NSInteger section = 0;
NSInteger rowIndex = 0;
int i = 0;
for (PFObject *object in self.objects) {
PFObject *obj = [object objectForKey:#"region"];
[obj fetchIfNeeded];
NSLog(#"School %#", [object objectForKey:#"Name"]);
NSString *Region = [obj objectForKey:#"name"];
NSLog(#"Reg: %#", Region);
NSMutableArray *objectsInSection = [self.sections objectForKey:Region];
if (!objectsInSection) {
objectsInSection = [NSMutableArray array];
NSLog(#"Is this called? %d", i);
// this is the first time we see this Region - increment the section index
[self.sectionToRegionMap setObject:Region forKey:[NSNumber numberWithInt:section++]];
}
[objectsInSection addObject:[NSNumber numberWithInt:rowIndex++]];
[self.sections setObject:objectsInSection forKey:Region];
}
NSLog(#"Finally done...");
}
- (PFQuery *)queryForTable {
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
// 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;
}
// Order by name
[query orderByAscending:#"Name"];
return query;
}
- (PFObject *)objectAtIndexPath:(NSIndexPath *)indexPath {
NSString *Region = [self RegionForSection:indexPath.section];
NSArray *rowIndecesInSection = [self.sections objectForKey:Region];
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 *Region = [self RegionForSection:section];
NSArray *rowIndecesInSection = [self.sections objectForKey:Region];
return rowIndecesInSection.count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *Region = [self RegionForSection:section];
return Region;
}
#pragma mark - UITableViewDelegate
- (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];
}
NSLog(#"CellFor %ld", (long)indexPath.row);
cell.textLabel.text = [object objectForKey:#"Name"];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
PFObject *selectedObject = [self objectAtIndexPath:indexPath];
}
#pragma mark - ()
- (NSString *)RegionForSection:(NSInteger)section {
return [self.sectionToRegionMap objectForKey:[NSNumber numberWithInt:section]];
}
Yeah, you're not going to be able to make this fast enough as-is... The client should not have to download every object first, and scrolling lists with 500+ items are not a good user experience. Perhaps you should have an initial screen where they pick some subset, and then they can query a smaller set of data on the next screen. What you're currently using as a section might be a good candidate.

how to order cells by date created and alphabetically using Parse database in Xcode 5?

I am building a database style app using Parse in Xcode 5 and want to be able to order all the cells by date created. I also want to then be able to change this to alphabetical order when I click an option of a segmented control (by date order being the default). How do I do this?
My table view controller (.m):
#import "JKENotesListViewController.h"
#import "JKENotesViewController.h"
#import <Parse/Parse.h>
#interface JKENotesListViewController ()
#end
#implementation JKENotesListViewController
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithClassName:#"Post"];
self = [super initWithCoder:aDecoder];
if (self) {
// The className to query on
self.parseClassName = #"Post";
// Whether the built-in pull-to-refresh is enabled
self.pullToRefreshEnabled = YES;
// Whether the built-in pagination is enabled
self.paginationEnabled = YES;
// The number of objects to show per page
self.objectsPerPage = 15;
}
return self;
}
#pragma mark - UIViewController
- (void)viewDidLoad {
[super viewDidLoad];
PFUser *currentUser = [PFUser currentUser];
if (currentUser) {
NSLog(#"Current user: %#", currentUser.username);
}
else {
[self performSegueWithIdentifier:#"showLogin" sender:self];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self loadObjects];
}
#pragma mark - PFQueryTableViewController
// 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 textKey in the object,
// and the imageView being the imageKey in the object.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *CellIdentifier = #"Cell";
PFTableViewCell *cell = (PFTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[PFTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"EEEE, MMMM d yyyy"];
NSDate *date = [object createdAt];
// Configure the cell
cell.textLabel.text = [object objectForKey:#"title"];
cell.detailTextLabel.text = [dateFormatter stringFromDate:date];
cell.imageView.image = [UIImage imageNamed:#"bike-iconcell.png"];
return cell;
}
// Override to customize what kind of query to perform on the class. The default is to query for
// all objects ordered by createdAt descending.
- (PFQuery *)queryForTable {
// Create a query
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
// Follow relationship
if ([PFUser currentUser]) {
[query whereKey:#"author" equalTo:[PFUser currentUser]];
}
else {
// I added this so that when there is no currentUser, the query will not return any data
// Without this, when a user signs up and is logged in automatically, they briefly see a table with data
// before loadObjects is called and the table is refreshed.
// There are other ways to get an empty query, of course. With the below, I know that there
// is no such column with the value in the database.
[query whereKey:#"nonexistent" equalTo:#"doesn't exist"];
}
return query;
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showNote"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
PFObject *object = [self.objects objectAtIndex:indexPath.row];
JKENotesViewController *note = (JKENotesViewController *)segue.destinationViewController;
note.note = object;
}
}
- (IBAction)logout:(id)sender {
[PFUser logOut];
[self performSegueWithIdentifier:#"showLogin" sender:self];
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
[self.tableView setEditing:editing animated:animated];
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Remove the row from data model
PFObject *object = [self.objects objectAtIndex:indexPath.row];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
[self refreshControl];
[tableView reloadData];
}];
}
}
#end
Many thanks in advance!
As a guideline, you have to sort you datasource (probably NS(Mutable)Array or NS(Mutable)Dictionary) as per your criteria and once the datasource is sorted out you need to call:
[yourTableView reloadData];
Later when you have to change the sort criteria to alphabetic order (may be through segmented control) sort data source in alphabetic order instead of date created, and don;t forget to reload your table again.
Below are some examples:
Sort in descending order
Sort using custom objects

Warning: A long-running Parse operation is being executed on the main thread

my app uses a SearchGraphical to search for registered users in the app ... My problem is that when I go to do research shows me the duplicate results as:
Name Search: Francesca
Result: Francesca (1 cell) Francesca (2 Cell)
**09/14/2013 22:18:17.817 Unipot v.02 [63396: a0b] Successfully retrieved 1 scores.
09/14/2013 22:18:17.818 Unipot v.02 [63396: a0b] Content: Antonella Folino
09/14/2013 22:18:17.818 Unipot v.02 [63396: a0b] Content: francesca Folino
09/14/2013 22:18:17.818 Unipot v.02 [63396: a0b] Content: francesca Folino
09/14/2013 22:18:17.819 Unipot v.02 [63396: a0b] Content: francesca Folino**
I do not understand why. This problem arises when you type in the search bar is done quickly.
In addition, the app crashes after a short time by returning to this notice:
**Terminating apt two to uncaught exception 'NSRangeException', reason: '*** - [__NSArrayM objectAtIndex:]: index 10 beyond bounds [0 .. 9] '**
This is my file. M Can you help me??
#import "Ricerca.h"
#import "Custom.h"
#interface Ricerca () <UISearchDisplayDelegate, UISearchBarDelegate>
#property (nonatomic, strong) UISearchDisplayController *searchController;
#property (nonatomic, strong) NSMutableArray *searchResults;
#end
#implementation Ricerca
- (void)viewDidLoad {
[super viewDidLoad];
[self loadObjects];
self.searchResults = [NSMutableArray array];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
self.parseClassName = #"_User";
self.paginationEnabled = YES;
self.loadingViewEnabled = NO;
self.objectsPerPage = 10;
}
return self;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait || UIInterfaceOrientationIsLandscape(interfaceOrientation));
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Cell"];
}
// Configure the cell
UIColor *color = [[UIColor alloc] initWithRed:0.0 green:0.0 blue:0.0 alpha:0.0];
cell.detailTextLabel.backgroundColor = color;
cell.textLabel.backgroundColor = color;
if (tableView == self.tableView) {
cell.textLabel.text = [object objectForKey:#"username"];
}
else {
PFObject *searchedUser = [self.searchResults objectAtIndex:indexPath.row];
NSString *content = [searchedUser objectForKey:#"username"];
cell.textLabel.text = content;
NSLog(#"Content: %#", content);
}
return cell;
}
-(void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"username" containsString:searchTerm];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", objects.count);
[self.searchResults addObjectsFromArray:objects];
[self.searchDisplayController.searchResultsTableView reloadData];
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
-(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)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.tableView) {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
} else {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
}
}
In your filterResults: method you are calling [self.searchResults addObjectsFromArray:objects];, this will add those results to what is already there. In the case where this method is getting hit multiple times before the first query finishes you could end up with the following scenario:
filter 1: "Fran"
clear results
start query 1
filter 2: "Franc"
clear results
start query 2
filter 2 finishes in background: add single result to array (contents 1 item)
filter 1 finishes in background: add single result to array (contents 2 items)
As you can see, there's no way to be sure when a query will finish, they might come back in a different order, and might not come back before you call the method again.
In this case it is hard to know what to do, you could empty self.searchResults in the success block, but in a case like above the final contents will be for the first query instead of the second query.
You could implement some kind of cancel/ignore-results option, or add a delay to the start of the query and hope for the best. Either way I would suggest reading up on threading issues and async operations.

Resources