Question

I have seen many posts on here regarding this issue but none seem to solve the problem i am having.

I have an app thats using core data. I am trying to sort a tableView thats hooked up to a NSFetchedResultsController, what makes the issue more complex than some of the other posts on here is that I need to update the sort based on the users location.

My scenario is as follows:

I have an entity called Store. Store has many branches, each branch has its own lat/long. I have a tableview that displays all the branches for a selected store. What i want is that when a user updates their location while the tableview is visible, the cells update their position so that the tableview is sorted with the closest branch at the top.

I manage to get all this working correctly with the cell animations moving up and down when ever a user moves closer to a branch etc. However my solution is not only inefficient but also has a major bug in it when the tableview has more cells than can fit on the screen.

I know that the mostly likely route to go down is using NSOrderedSet to sort the to-many relationship on my Store entity. But not sure how to implement this as the value that i am sorting on is not stored in core data but is calculated dynamically if that make sense. The lat and long is stored, but the distance is calculated on the fly and its the distance value that determines the sort.

Currently i use the CLLocationManger protocol locactionManager:didUpdateLocations to call updateCellSortingOrder which is my sort method. The location manager is set to:

self.manager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;

My sort method works, but like i said its not ideal. I want to be able to leverage as much of the core data tools such as NSFetchedViewController,predicates, aggregates etc rather than creating arrays, sorting them then pushing them back into the FRC etc which is kinda of what i am doing now.... all too messy.

Here is my code, note: branchCell is a subclass of UITableViewCell which has a property init that holds a branch. Branch is an instance of my entity Branch

//Creating the branch cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"MyTableCellView";

BranchCell *cell  = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[BranchCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];


    Branch *currentBranch =  [[self.list.listsToDelivery allObjects] objectAtIndex:indexPath.row];

    cell.branch = currentBranch;
    cell.textLabel.text = currentBranch.title;
}

return cell;

}

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    //update the cells
    [self updateCellSortingOrder];
}

- (void) updateCellSortingOrder
{
    // get the number of cells
    static int numberOfCells;

    numberOfCells = [[self.currentStore.branches allObjects]  count];

//loop though all the cells
    for (int cellAIndex = 0; cellAIndex < numberOfCells; cellAIndex++)
{
    //set up the cells
    static BranchCell * cellA;
    static BranchCell * cellB;

    //branch instances
    static Branch *branchA;
    static Branch *branchB;

    //distances
    static CLLocationDistance distanceA;
    static CLLocationDistance distanceB;
    static CLLocation *locationA;
    static CLLocation *locationB;

    //second loop to get the next cell 
    for (int cellBIndex = cellAIndex+1; cellBIndex < numberOfCells; cellBIndex++)
    {
        //assign the cells
        cellA = (BranchCell *)[self.myTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:cellAIndex inSection:0]];
        cellB = (BranchCell *)[self.myTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:cellBIndex inSection:0]];

        //get the two branches which we need to compate
        branchA = cellA.branch;
        branchB = cellB.branch;

        //calculate the distance
        locationA = [[CLLocation alloc] initWithLatitude:branchA.address.region.center.latitude longitude:branchA.address.region.center.longitude];

        locationB = [[CLLocation alloc] initWithLatitude:branchB.address.region.center.latitude longitude:branchB.address.region.center.longitude];

        distanceA = [self.manager.location distanceFromLocation:locationA];
        distanceB = [self.manager.location distanceFromLocation:locationB];

        //move the cell a to cell b's location if a's distances is greater than b's
        if (distanceA > distanceB)
        {
            [self.myTableView moveRowAtIndexPath:[NSIndexPath indexPathForRow:cellAIndex inSection:0] toIndexPath:[NSIndexPath indexPathForRow:cellBIndex inSection:0]];

        } 
    }
}
}
Was it helpful?

Solution

In case any one is facing the same issue I had, here is my fix. Fixed it a while ago but never updated. It was used in my app Delivered which is on the app store now :)

the updateCellSortingOrder is called on the map delegate method mapView:didUpdateUserLocation:

  • (void) updateCellSortingOrder {

    [self.myTableView reloadData];

    [self.sortedCellArray sortUsingComparator: ^(Delivery *deliveryA, Delivery *deliveryB) {

     CLLocationDistance distanceA;
     CLLocationDistance distanceB;
    
    
     distanceA = [[self.mapView userLocation] distanceFromLocation: deliveryA.address];
     distanceB = [[self.mapView userLocation] distanceFromLocation: deliveryB.address];
    
     if ( distanceA > distanceB)
     {
    
         return (NSComparisonResult)NSOrderedDescending;
     }
    
     return (NSComparisonResult)NSOrderedSame;
    

    }];

}

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top