I am trying to fill my tableView with a points and distances to current location.
I've got a problem with initialize property.
In .h file:
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface SIBViewController : UIViewController
<CLLocationManagerDelegate>
{
NSArray *_data;
CLLocationManager *locationManager;
CLLocation *currentLocation;
}
#property (nonatomic, retain) CLLocation *currentLocation;
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#end
In .m file:
#import "SIBViewController.h"
#import "atmCell.h"
#import "sibAtmData.h"
#interface SIBViewController ()
#end
#implementation SIBViewController
#synthesize currentLocation;
- (void)viewDidLoad
{
[super viewDidLoad];
[self startSignificantChangeUpdates];
_data = [sibAtmData fetchData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_data count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"atmCell";
atmCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
sibAtmData *item = [_data objectAtIndex:indexPath.row];
cell.titleLabel.text = item.title;
cell.subtitleLabel.text = item.subtitle;
CLLocationDistance distance = [self.currentLocation distanceFromLocation:item.location];
cell.distanceLabel.text = [NSString stringWithFormat:#"%.1f km", distance/1000];
NSLog(#"distance: %f", distance);
return cell;
}
- (void)startSignificantChangeUpdates
{
// Create the location manager if this object does not
// already have one.
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
[locationManager startMonitoringSignificantLocationChanges];
}
// Delegate method from the CLLocationManagerDelegate protocol.
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
// If it's a relatively recent event, turn off updates to save power
self.currentLocation = [locations lastObject];
NSDate* eventDate = currentLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (abs(howRecent) < 15.0) {
[self.tableView reloadData];
// If the event is recent, do something with it.
NSLog(#"latitude %+.6f, longitude %+.6f\n",
currentLocation.coordinate.latitude,
currentLocation.coordinate.longitude);
}
}
#end
But currentLocation is empty:
currentLocation CLLocation * 0x00000000
I've tried to write in viewDidLoad:
currentLocation = [[CLLocation alloc] init];
but this didn't help me.
Memory allocates for object, but object creates without _latitude and _longitude properties
What am I doing wrong?
Related
I have two response from the server( longitude and latitude). This response is save and display to the Table cell. When clicking the cell, there is a map with the location given by the server. How can i update the location of the map (move the marker) depending on the server given response?
Here is my code in map:
#interface ChildDetailViewController ()
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#end
#implementation ChildDetailViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.mapView.delegate = self;
// Do any additional setup after loading the view.
MapAnnotation *mapPoint = [[MapAnnotation alloc] init];
mapPoint.coordinate = CLLocationCoordinate2DMake([self.appDelagate.latitude doubleValue], [self.appDelagate.longitude doubleValue]);
mapPoint.title = self.appDelagate.name;
mapPoint.mapimage = self.appDelagate.image;
// Add it to the map view
[self.mapView addAnnotation:mapPoint];
// Zoom to a region around the pin
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(mapPoint.coordinate, 500, 500);
[self.mapView setRegion:region];
}
#pragma mark - MKMapViewDelegate
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
MKPinAnnotationView *view = nil;
static NSString *reuseIdentifier = #"MapAnnotation";
// Return a MKPinAnnotationView with a simple accessory button
view = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:reuseIdentifier];
if(!view)
{
view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
view.canShowCallout = YES;
view.animatesDrop = YES;
}
return view;
}
Here is the code in Table Cell:
#import "ChildListViewController.h"
#import "AppDelegate.h"
#import "Child.h"
#import "ChildDetailViewController.h"
#interface ChildListViewController ()
{
NSArray *_child_info;
//NSString *locationName;
}
#end
#implementation ChildListViewController
- (IBAction)unwindchild:(UIStoryboardSegue *)segue
{
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self dismissViewControllerAnimated:YES completion:nil];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"Pumasok ba?");
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"TableIdentifier";
AppDelegate *appDelegate = [_child_info objectAtIndex:indexPath.row];
Child *cell = (Child *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"Child" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.childName.text = appDelegate.name;
cell.childbeaconID.text = appDelegate.beacon_id;
cell.childstatus.text = appDelegate.status;
cell.childLoclong.text = appDelegate.longitude;
cell.childLoclat.text = appDelegate.latitude;
cell.serverCreatedDate.text = appDelegate.server_created_date;
cell.childimageview.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:
[NSURL URLWithString:appDelegate.image]]];
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_child_info count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 119;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (appDelegate.name != nil)
{
[self performSegueWithIdentifier:(#"a") sender:(self)];
}
else
{
NSLog(#"No Data %#", appDelegate.name);
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier]isEqualToString:#"a"])
{
ChildDetailViewController *vc = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
vc.appDelagate = [_child_info objectAtIndex:indexPath.row];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
Parse the server response
AppDelegate.m
NSDictionary *fetchedDictionary =[userPushInfo objectForKey:#"aps"];
NSDictionary *fetchedDictionaryalert = [fetchedDictionary objectForKey:#"alert"];
NSDictionary *fetchedDictionarybody = [fetchedDictionaryalert objectForKey:#"body"];
NSDictionary *fetchedDictionaryresult = [fetchedDictionarybody objectForKey:#"student_event"];
for (NSDictionary *user in fetchedDictionaryresult)
{
beacon_device_name =[user objectForKey:#"beacon_device_name"];
image =[user objectForKey:#"image"];
latitude = [user objectForKey:#"latitude"];
longitude =[user objectForKey:#"longitude"];
server_created_date = [user objectForKey:#"server_created_date"];
status =[user objectForKey:#"status"];
}
you can update by doing this after you get the response
[self.mapView removeAnnotations:self.mapView.annotations];
MapAnnotation *mapPoint = [[MapAnnotation alloc] init];
mapPoint.coordinate = CLLocationCoordinate2DMake([newLatitude doubleValue], [newLongitude doubleValue]);
mapPoint.title = self.appDelagate.name;
mapPoint.mapimage = self.appDelagate.image;
// Add it to the map view
[self.mapView addAnnotation:mapPoint];
// Zoom to a region around the pin
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(mapPoint.coordinate, 500, 500);
[self.mapView setRegion:region];
you can communicate within the view controllers by using Notification Center,put the first in your child view controller,and second after parse your response.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateLocation) name:AppDelegateReceiveTheLocation object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:AppDelegateReceiveTheLocation object:nil userInfo:nil];
I have a problem with the bookstableview.m segue. if you look bellow there where it says PFObject *object = [self.objects objectatindex.indexPath.row] there is an error under "objects" saying "Property objects not found on object of type 'BooksTableViewController'."
here is the rest of the code: Bookstableview.m
#import "BooksTableViewController.h"
#import "BookDetailViewController.m"
#interface BooksTableViewController ()
#end
#implementation BooksTableViewController
#synthesize bookstableview;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self performSelector:#selector(RetrieveDatafromParse)];
}
-(void) RetrieveDatafromParse {
PFQuery * getbooks = [PFQuery queryWithClassName:#"BooksTableView"];
[getbooks findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if(!error) {
Booksarray =[[NSArray alloc] initWithArray: objects];
}
[bookstableview reloadData];
NSLog(#"%#",objects);
}];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section
{
return Booksarray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * CellIdentifier = #"Cell";
UITableViewCell * cell = [bookstableview dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell ==nil) {
cell = [[ UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Cell"];
}
PFObject * tempObject = [Booksarray objectAtIndex:indexPath.row];
cell.textLabel.text = [tempObject objectForKey:#"Books"];
cell.detailTextLabel.text= [tempObject objectForKey:#"Code"];
return cell;
}
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"booksseg" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"ShowDetails"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
PFObject * object = [self.objects objectAtIndex:indexPath.row];
PFFile *file = [object objectForKey:#"BooksTableView"];
[[segue destinationViewController] setFile:file];
}
}
#end
bookstableview.h
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#interface BooksTableViewController : UITableViewController <UITableViewDelegate >
{
NSArray * Booksarray;
}
#property (strong, nonatomic) IBOutlet UITableView *bookstableview;
#end
Booksdetailview.h
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#import "BooksTableViewController.h"
#interface BookDetailViewController : UIViewController {
}
#property (weak, nonatomic) IBOutlet UIImageView *BookImage;
#property (weak, nonatomic) IBOutlet UILabel *bookTitle;
#property (weak, nonatomic) IBOutlet UILabel *bookDesc;
#property (weak,nonatomic) PFObject *file;
#end
Replace self.objects with Booksarray:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"ShowDetails"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
PFObject * object = [Booksarray objectAtIndex:indexPath.row];
PFFile *file = [object objectForKey:#"BooksTableView"];
[[segue destinationViewController] setFile:file];
}
}
Also: normally, only class names are capitalized, while variables and methods begin with a lower-case letter, so _booksArray would be a more appropriate name for the variable. Helps avoid confusion and prevents overlap between class/variable names.
My app has a tabbarcontroller with a UIViewController (FirstViewController-calling it mapVC) with a mapview and a UITableViewController (SecondViewController-calling it tableVC). The app fetches data from web and puts it into CD-db and each VC executes a fetch to the db. Each entity is named Holiday (dont ask) and it has a lat and long property.
Here is the UITabBarController subclass which attempts to set the mapVC as datasource to tableVC:
- (void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view.
FirstViewController *mapVC;
SecondViewController *tableVC;
for(UIViewController *anyVC in self.viewControllers)
{
if([anyVC.class isKindOfClass:[SecondViewController class]]){
tableVC = (SecondViewController *)anyVC;
} else if ([anyVC.class isKindOfClass:[FirstViewController class]]){
mapVC = (FirstViewController *)anyVC;
}
}
tableVC.tableView.dataSource = mapVC;
tableVC.tableView.delegate = mapVC;
}
Here are the relevant parts of mapVC:
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#import "SecondViewController.h"
#define METERS_PER_MILE 2609.344
#interface FirstViewController : UIViewController <MKMapViewDelegate, UITableViewDataSource, UITableViewDelegate>{
BOOL _doneInitialZoom;
}
#property (strong, nonatomic) IBOutlet MKMapView *_mapView;
#property (strong, nonatomic) IBOutlet UIBarButtonItem *refreshButton;
#property (nonatomic, strong) NSString *entityName;
#property (strong, nonatomic) CLLocation *userLocation;
- (IBAction)refreshTapped:(id)sender;
-(void)showDetailView;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
#end
and its implementation:
#import "FirstViewController.h"
#import "Holiday.h"
#import "MyLocation.h"
#import "SDCoreDataController.h"
#import "MyTabBarController.h"
#import "TableViewCell.h"
- (void)loadRecordsFromCoreData {
[self.managedObjectContext performBlockAndWait:^{
[self.managedObjectContext reset];
NSError *error = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:self.entityName];
[request setSortDescriptors:[NSArray arrayWithObject:
[NSSortDescriptor sortDescriptorWithKey:#"date" ascending:YES]]];
self.farSiman = [self.managedObjectContext executeFetchRequest:request error:&error];
}];
NSLog(#"self.farSiman on launch = %#", self.farSiman);
}
- (void)plotStorePositions:(NSString *)responseString {
for (id<MKAnnotation> annotation in _mapView.annotations) {
[_mapView removeAnnotation:annotation];
}
NSLog(#"Dictionary is %#", self.farSiman);
for (Holiday * holidayObject in self.farSiman) {
NSString * latitude = holidayObject.latitude;
NSString * longitude = holidayObject.longitude;
NSString * storeDescription = holidayObject.name;
NSString * address = holidayObject.address;
CLLocationCoordinate2D coordinate;
coordinate.latitude = latitude.doubleValue;
coordinate.longitude = longitude.doubleValue;
MyLocation *annotation = [[MyLocation alloc] initWithName:storeDescription address:address coordinate:coordinate distance:0];
//
CLLocation *pinLocation = [[CLLocation alloc] initWithLatitude:annotation.coordinate.latitude longitude:annotation.coordinate.longitude];
//[(MyLocation*)[view annotation] coordinate].latitude longitude:[(MyLocation*)[view annotation] coordinate].longitude]];
self.userLocation = [[CLLocation alloc] initWithLatitude:self._mapView.userLocation.coordinate.latitude longitude:self._mapView.userLocation.coordinate.longitude];
NSLog(#"PLOT>>userLocation is %#", userLocation);
CLLocationDistance calculatedDistance = [pinLocation distanceFromLocation:self.userLocation];
annotation.distance = calculatedDistance/1000;
NSLog(#"PLOT>>Distance to pin %4.0f", annotation.distance);
//
[_mapView addAnnotation:annotation];
}
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [self.farSiman count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = nil;
// Check to see whether the normal table or search results table is being displayed and set the Candy object from the appropriate array
NSLog(#"Already in CFRAIP");
static NSString *CellIdentifier = #"HolidayCell";
if (cell == nil) {
cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
//cell.accessoryType=UITableViewCellAccessoryDetailDisclosureButton;
}
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Holiday *holiday = [self.farSiman objectAtIndex:indexPath.row];
cell.nameLabel.text = holiday.name;
//cell.dateLabel.text = holiday.latitude;
cell.dateLabel.text = [[self calculateDistanceForLat:[holiday.latitude doubleValue] Long:[holiday.longitude doubleValue]] stringValue];
if (holiday.image != nil) {
UIImage *image = [UIImage imageWithData:holiday.image];
cell.imageView.image = image;
} else {
cell.imageView.image = nil;
}
return cell;
}
// Add new method above refreshTapped
- (NSNumber*)calculateDistanceForLat:(double)lati Long:(double)longi {
double distancia;
CLLocation *pinLocation = [[CLLocation alloc] initWithLatitude:lati longitude:longi];
CLLocationDistance calculatedDistance = [pinLocation distanceFromLocation:self.userLocation];
//test locations
NSLog(#"pinLocations is %#, userLocation is %#", pinLocation, self.userLocation);
distancia = calculatedDistance/1000;
return [NSNumber numberWithDouble:distancia];
}
The plotStoreLocations method is the only method call from a UIButton in the mapVC toolbar.
As for tableVC (SecondViewController)
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface SecondViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UISearchDisplayDelegate>
#property (nonatomic, strong) NSArray *dates;
#property (nonatomic, strong) NSString *entityName;
#property (strong, nonatomic) IBOutlet UIBarButtonItem *refreshButton;
#property (strong,nonatomic) NSMutableArray *filteredResultsArray;
#property (strong,nonatomic) IBOutlet UISearchBar *resultsSearchBar;
#property (strong, nonatomic) CLLocation *userLocation;
- (IBAction)refreshButtonTouched:(id)sender;
And its implementation:
- (void)loadRecordsFromCoreData {
[self.managedObjectContext performBlockAndWait:^{
[self.managedObjectContext reset];
NSError *error = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:self.entityName];
[request setSortDescriptors:[NSArray arrayWithObject:
[NSSortDescriptor sortDescriptorWithKey:#"date" ascending:YES]]];
self.dates = [self.managedObjectContext executeFetchRequest:request error:&error];
NSLog(#"self.dates==%#",self.dates);
}];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [filteredResultsArray count];
} else {
return [self.dates count];
}
//return [self.dates count];
}
// Add new method above refreshTapped
- (NSNumber*)calculateDistanceForLat:(double)lati Long:(double)longi {
double distancia;
CLLocation *pinLocation = [[CLLocation alloc] initWithLatitude:lati longitude:longi];
CLLocationDistance calculatedDistance = [pinLocation distanceFromLocation:self.userLocation];
//test locations
NSLog(#"pinLocations is %#, userLocation is %#", pinLocation, self.userLocation);
distancia = calculatedDistance/1000;
return [NSNumber numberWithDouble:distancia];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
static NSString *CellIdentifier = #"HolidayCell";
if (cell == nil) {
cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Holiday *holiday = [filteredResultsArray objectAtIndex:indexPath.row];
NSLog(#"the holiday is %#", holiday.name);
cell.nameLabel.text = holiday.name;
} else {
static NSString *CellIdentifier = #"HolidayCell";
if (cell == nil) {
cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Holiday *holiday = [self.dates objectAtIndex:indexPath.row];
cell.nameLabel.text = holiday.name;
cell.dateLabel.text = [[self calculateDistanceForLat:[holiday.latitude doubleValue] Long:[holiday.longitude doubleValue]] stringValue];
if (holiday.image != nil) {
UIImage *image = [UIImage imageWithData:holiday.image];
cell.imageView.image = image;
} else {
cell.imageView.image = nil;
}
}
return cell;
}
Finally MyLocation:
import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MyLocation : NSObject <MKAnnotation> {
NSString *_name;
NSString *_address;
CLLocationCoordinate2D _coordinate;
}
#property (copy) NSString *name;
#property (copy) NSString *address;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (assign) float distance;
- (id)initWithName:(NSString*)name address:(NSString*)address coordinate:(CLLocationCoordinate2D)coordinate distance:(float)distance;
#end
#import "MyLocation.h"
#implementation MyLocation
#synthesize name = _name;
#synthesize address = _address;
#synthesize coordinate = _coordinate;
#synthesize distance = _distance;
- (id)initWithName:(NSString*)name address:(NSString*)address coordinate:(CLLocationCoordinate2D)coordinate distance:(float)distance{
if ((self = [super init])) {
_name = [name copy];
_address = [address copy];
_coordinate = coordinate;
_distance = distance;
}
return self;
}
- (NSString *)title {
if ([_name isKindOfClass:[NSNull class]])
return #"Unknown charge";
else
return _name;
}
- (NSString *)subtitle {
return [NSString stringWithFormat:#"A %0.2f Kms", _distance];
//return _address;
}
Specific Questions:
1) If I added the cFRAIP (noris & nosit) method in mapVC (the new datasource) do I need to remove it from tableVC?
2) If I remove cFRAIP and the other 2 (noris and nosit) methods from tableVC, it crashes because there is no datasource. So the tabbarcontroller assignment of datasource doesnt seem to be working.
3) Finally if I have to remove cFRAIP from tableVC, I will lose my ability to UISearchBar the tableview. Or am I wrong?
When I run the app, the mapVC is the selected vc. The UIButton in the toolbar that calls plotStoreLocations in mapVC is greyed out until the web fetch finishes. At this point the console logs the self.farsiman locations which are Holiday entities. And I can see all the entities log into the console.
When I click the plot button, the userLocation is logged correctly as specified in the plotStorePositions and the distance value for each annotation is correct. So the distance for each MKAnnotation is calculated correctly.
When I switch to the tableVC tab, the new self.dates array is logged in the console (because I currently have the tableVC do another CD-db fetch. And I get this for each pin location:
Already in CFRAIP pinLocations is <+15.50288611,-88.02716389> +/-
0.00m (speed -1.00 mps / course -1.00) # 1/24/13, 8:20:39 PM Central Standard Time, userLocation is (null)
which is called from the calculateDistanceForLat which is called by the CFRAIP. And every distance in the cell detail is -0.001.
You definitely need to make mapVC the datasource and delegate. If UITableViewController isn't letting you assign it to something other that itself you might want to consider using a regular UIViewController and drop a UITableView on to it. If you set it up as a property called tableview the code in tabbarcontroller that does tableVC.tableView.dataSource = mapVC; will still work.
Yes you can and should remove all tableview delegate and datasource code from your tableVC, if it is calling those then it isn't calling the ones in the mapVC which is what you want.
It's probably giving you the wrong distance because tableVC doesn't have the user's location to measure from. Another good reason for having the table datasource attached to the map.
I have an SQL DB that contains Lat and Long info. I have found my current location and been able to get the distance of each location from my current location. I can get my tableview to show this distance, but know I want to sort that tableview so the closest are listed first.
My thought is I will need to add the distance to my SQL DB data, sort that some how and then display that info back to the tableview.
My thought is I would do all of this within my TableView. Looking for guidance on how to do this and if I should be doing it in the tableview.
#import "TulsaMasterViewController.h"
#import "TulsaDetailViewController.h"
#import "Bars.h"
#import "BarDatabase.h"
#implementation TulsaMasterViewController
#synthesize barArray = _barArray;
#synthesize currentLat = _currentLat;
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
currentLat = newLocation;
if (newLocation.horizontalAccuracy <= 100.0f) {
[lm stopUpdatingLocation];
}
[self.tableView reloadData];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSString *msg = [[NSString alloc]initWithString:#"Error obtaining location"];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Error" message:msg delegate:nil cancelButtonTitle:#"Done" otherButtonTitles:nil];
[alert show];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.barArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
//Get the object from the array.
Bars *barObj = [self.barInfo objectAtIndex:indexPath.row];
//Set the name.
cell.textLabel.text = barObj.barName;
if (currentLat == nil) {
cell.detailTextLabel.text = [NSString stringWithFormat:#"?"];
}else
{
cell.detailTextLabel.text = [NSString stringWithFormat:#"%.02f", cachedDist];
}
// Set up the cell
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"ShowDetails"]) {
TulsaDetailViewController *detailViewController = [segue destinationViewController];
detailViewController.detailItem = [self.barArray objectAtIndex:[self.tableView indexPathForSelectedRow].row];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.barArray = [BarDatabase database].barInfo;
lm = [[CLLocationManager alloc] init];
lm.delegate = self;
[lm startUpdatingLocation];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
for (Bars *barObj in barArray) {
NSString *strLat = barObj.Lat;
NSString *strLong = barObj.Long;
CLLocation *barLocation = [[CLLocation alloc] initWithLatitude:[strLat doubleValue] longitude:[strLong doubleValue]];
CLLocationDistance distance = [currentLat distanceFromLocation:barLocation]/1000;
[barArray addObject:[NSNumber numberWithDouble:distance]];
NSSortDescriptor *sort=[NSSortDescriptor sortDescriptorWithKey:#"cachedDist" ascending:YES];
[barArray sortUsingDescriptors:[NSArray arrayWithObject:sort]];
}
For reference the rest of my code
Bars.h
#import <Foundation/Foundation.h>
#interface Bars : NSObject {
NSString *barName;
NSString *barAddress;
NSString *Lat;
NSString *Long;
NSString *cachedDist;
}
#property (nonatomic, copy) NSString *barName;
#property (nonatomic, copy) NSString *barAddress;
#property (nonatomic, copy) NSString *Lat;
#property (nonatomic, copy) NSString *Long;
#property (nonatomic, copy) NSString *cachedDist;
- (id)initWithName:(NSString *)name address:(NSString *)address latitude:(NSString *)latitude longitude:(NSString *)longitude distance:(NSString *)distance;
#end
Bars.m
#import "Bars.h"
#implementation Bars
#synthesize barName = _barName;
#synthesize barAddress = _barAddress;
#synthesize Lat = _Lat;
#synthesize Long = _Long;
#synthesize cachedDist = _cachedDist;
- (id)initWithName:(NSString *)name address:(NSString *)address latitude:(NSString *)latitude longitude:(NSString *)longitude distance:(NSString *)distance;
{
if ((self = [super init])) {
self.barName = name;
self.barAddress = address;
self.Lat = latitude;
self.Long = longitude;
self.cachedDist = distance;
}
return self;
}
#end
BarDatabase.h
#import <Foundation/Foundation.h>
#import <sqlite3.h>
#interface BarDatabase : NSObject
{
sqlite3 *_database;
}
+ (BarDatabase *)database;
- (NSMutableArray *)barInfo;
#end
BarDatabase.m
#import "BarDatabase.h"
#import "Bars.h"
#implementation BarDatabase
static BarDatabase *_database;
+ (BarDatabase *)database {
if (_database == nil) {
_database = [[BarDatabase alloc] init];
}
return _database;
}
- (id)init {
if ((self = [super init])) {
NSString *sqLiteDb = [[NSBundle mainBundle] pathForResource:#"TulsaBars"
ofType:#"sqlite"];
if (sqlite3_open([sqLiteDb UTF8String], &_database) != SQLITE_OK) {
NSLog(#"Failed to open database!");
}
}
return self;
}
- (void)dealloc {
sqlite3_close(_database);
}
- (NSMutableArray *)barInfo {
NSMutableArray *retval = [[NSMutableArray alloc] init];
NSString *query = #"SELECT * FROM TulsaBars";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, nil)
== SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
char *nameChars = (char *) sqlite3_column_text(statement, 1);
char *addressChars = (char *) sqlite3_column_text(statement, 2);
char *latChars = (char *) sqlite3_column_text(statement, 8);
char *longChars = (char *) sqlite3_column_text(statement, 9);
NSString *name = [[NSString alloc] initWithUTF8String:nameChars];
NSString *address = [[NSString alloc] initWithUTF8String:addressChars];
NSString *latitude = [[NSString alloc] initWithUTF8String:latChars];
NSString *longitude = [[NSString alloc] initWithUTF8String:longChars];
Bars *info = [[Bars alloc]
initWithName:name address:address latitude:latitude longitude:longitude];
[retval addObject:info];
}
sqlite3_finalize(statement);
}
return retval;
}
#end
Hrm. There should be no sorting happening inside cellForRowAtIndexPath. It's too late in the game. If each cell is 1 bar, and the bars should be sorted by distance, I recommend achieving the sort in a lazy getter for your self.barInfo (if that's your list of bars).
I'm not sure what your model looks like, but if each bar object has a property for distance from whatever your reference point is, your sorting would look similar to what you have there.
This sorting would only happen the when the first cell is loaded (that's the "lazy" part) and then cached. So your cellForRowAtIndexPath: would just ask for the model object at it's indexPath's row, trusting that it is in the right order.
I'm not sure how much of this terminology you are familiar with, so feel free to ask clarifying questions and I will iterate on the answer.
EDIT after sharing more code:
I think you should add a #property to the bar called something like cachedDistance, and whenever you come onscreen (viewWillAppear), iterate over the bars and set their cached distance (using similar code to what you have in cellForRow...). Then implement a getter for self.barArray: -(NSArray *)barArray which essentially returns the barArray sorted using a sortDescriptor with the name of your cached distance property as it's key. This will simplify your cellForRow... code a lot.
You can then extend the logic to recalculate the distances when you get location updates, perhaps only if it is a certain distance from the previous.
I'm trying to load my UITableView with distance from current location. I'm taking small steps in just trying to get the Latitude to load into the UITableView. My NSLog is reading the correct Lat/Long, but my Table is reading 0.000. At this point I'm not sure if it's my memory management or something else. Please help.
My ViewController.h
#import <UIKit/UIKit.h>
#import "CoreLocation/CoreLocation.h"
#interface TulsaMasterViewController : UITableViewController <CLLocationManagerDelegate>
{
NSArray *_barInfo;
CLLocationManager *lm;
NSString *currentLat;
}
#property (nonatomic, strong) NSArray *barInfo;
#property (nonatomic, strong) NSString *currentLat;
#end
My ViewController.m
#import "TulsaMasterViewController.h"
#import "TulsaDetailViewController.h"
#import "Bars.h"
#import "BarDatabase.h"
#implementation TulsaMasterViewController
#synthesize barInfo = _barInfo;
#synthesize currentLat = _currentLat;
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"%f", newLocation.coordinate.latitude);
NSLog(#"%f", newLocation.coordinate.longitude);
currentLat = [NSString stringWithFormat:#"%f", newLocation.coordinate.latitude];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSString *msg = [[NSString alloc]initWithString:#"Error obtaining location"];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Error" message:msg delegate:nil cancelButtonTitle:#"Done" otherButtonTitles:nil];
[alert show];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.barInfo count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
//Get the object from the array.
Bars *barObj = [self.barInfo objectAtIndex:indexPath.row];
//Set the coffename.
cell.textLabel.text = barObj.barName;
cell.detailTextLabel.text = [NSString stringWithFormat:#"%f", currentLat];
// Set up the cell
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"ShowDetails"]) {
TulsaDetailViewController *detailViewController = [segue destinationViewController];
detailViewController.detailItem = [self.barInfo objectAtIndex:[self.tableView indexPathForSelectedRow].row];
}
}
- (void)awakeFromNib
{
[super awakeFromNib];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.barInfo = [BarDatabase database].barInfo;
lm = [[CLLocationManager alloc] init];
lm.delegate = self;
[lm startUpdatingLocation];
}
#end
You need to update your tableview when the didUpdateToLocation method is called