Skip to content

Commit 9a114e7

Browse files
authored
Merge pull request #319 from jwalgran/feature/jcw/update-location-handling
Update location handling
2 parents 8f4fd36 + ec844c8 commit 9a114e7

14 files changed

Lines changed: 267 additions & 282 deletions

OpenTreeMap/OpenTreeMap.xcodeproj/project.pbxproj

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
6251603A199BA05500138CFA /* OTMUdfCollectionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 62516039199BA05500138CFA /* OTMUdfCollectionCell.m */; };
1414
62F06164196366B800F8B84D /* profile.png in Resources */ = {isa = PBXBuildFile; fileRef = 62F06162196366B800F8B84D /* profile.png */; };
1515
62F06165196366B800F8B84D /* profile@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 62F06163196366B800F8B84D /* profile@2x.png */; };
16-
641210A91935363400118A33 /* OTMLocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 641210A81935363400118A33 /* OTMLocationManager.m */; };
1716
641210AC1938181E00118A33 /* OTMPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 641210AB1938181E00118A33 /* OTMPreferences.m */; };
1817
641210AF19382DF100118A33 /* OTMSetRootViewControllerSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 641210AE19382DF100118A33 /* OTMSetRootViewControllerSegue.m */; };
1918
64318CD31551E27D001C827F /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7C990221518C87E0094770C /* CFNetwork.framework */; };
@@ -99,6 +98,7 @@
9998
6470BEED18E0A8C700C56A8F /* OTMViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6470BEEC18E0A8C700C56A8F /* OTMViewController.m */; };
10099
647D3E831934E75D00077ADE /* OTMInstanceSelectTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 647D3E821934E75D00077ADE /* OTMInstanceSelectTableViewController.m */; };
101100
647E445716417BDF000CF872 /* about.html in Resources */ = {isa = PBXBuildFile; fileRef = 647E445616417BDF000CF872 /* about.html */; };
101+
648B1A9E1DA8573E0059BEA6 /* CLLocation+AgeInSecondsLessThan.m in Sources */ = {isa = PBXBuildFile; fileRef = 648B1A9D1DA8573E0059BEA6 /* CLLocation+AgeInSecondsLessThan.m */; };
102102
649426E619008F3300923470 /* SettingsIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 649426E219008F3300923470 /* SettingsIcon.png */; };
103103
649426E719008F3300923470 /* SettingsIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 649426E319008F3300923470 /* SettingsIcon@2x.png */; };
104104
649426E819008F3300923470 /* SpotlightIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 649426E419008F3300923470 /* SpotlightIcon.png */; };
@@ -192,8 +192,6 @@
192192
62516039199BA05500138CFA /* OTMUdfCollectionCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTMUdfCollectionCell.m; sourceTree = "<group>"; };
193193
62F06162196366B800F8B84D /* profile.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = profile.png; sourceTree = "<group>"; };
194194
62F06163196366B800F8B84D /* profile@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "profile@2x.png"; sourceTree = "<group>"; };
195-
641210A71935363400118A33 /* OTMLocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTMLocationManager.h; sourceTree = "<group>"; };
196-
641210A81935363400118A33 /* OTMLocationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTMLocationManager.m; sourceTree = "<group>"; };
197195
641210AA1938181E00118A33 /* OTMPreferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTMPreferences.h; sourceTree = "<group>"; };
198196
641210AB1938181E00118A33 /* OTMPreferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTMPreferences.m; sourceTree = "<group>"; };
199197
641210AD19382DF100118A33 /* OTMSetRootViewControllerSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTMSetRootViewControllerSegue.h; sourceTree = "<group>"; };
@@ -345,6 +343,8 @@
345343
647D3E811934E75D00077ADE /* OTMInstanceSelectTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTMInstanceSelectTableViewController.h; sourceTree = "<group>"; };
346344
647D3E821934E75D00077ADE /* OTMInstanceSelectTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTMInstanceSelectTableViewController.m; sourceTree = "<group>"; };
347345
647E445616417BDF000CF872 /* about.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = about.html; sourceTree = "<group>"; };
346+
648B1A9C1DA8573E0059BEA6 /* CLLocation+AgeInSecondsLessThan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CLLocation+AgeInSecondsLessThan.h"; sourceTree = "<group>"; };
347+
648B1A9D1DA8573E0059BEA6 /* CLLocation+AgeInSecondsLessThan.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CLLocation+AgeInSecondsLessThan.m"; sourceTree = "<group>"; };
348348
649426E219008F3300923470 /* SettingsIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = SettingsIcon.png; sourceTree = "<group>"; };
349349
649426E319008F3300923470 /* SettingsIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "SettingsIcon@2x.png"; sourceTree = "<group>"; };
350350
649426E419008F3300923470 /* SpotlightIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = SpotlightIcon.png; sourceTree = "<group>"; };
@@ -556,6 +556,8 @@
556556
64608683160CFB6700A1458B /* Categories */ = {
557557
isa = PBXGroup;
558558
children = (
559+
648B1A9C1DA8573E0059BEA6 /* CLLocation+AgeInSecondsLessThan.h */,
560+
648B1A9D1DA8573E0059BEA6 /* CLLocation+AgeInSecondsLessThan.m */,
559561
64608684160CFB6700A1458B /* NSArray+MutableDeepCopy.h */,
560562
64608685160CFB6700A1458B /* NSArray+MutableDeepCopy.m */,
561563
64608686160CFB6700A1458B /* NSDictionary+DecodeKey.h */,
@@ -589,8 +591,6 @@
589591
646086C6160CFB6700A1458B /* OTMEnvironment.m */,
590592
D74B1FC918B78EA5006BABE2 /* OTMFormatter.h */,
591593
D74B1FCA18B78EA5006BABE2 /* OTMFormatter.m */,
592-
641210A71935363400118A33 /* OTMLocationManager.h */,
593-
641210A81935363400118A33 /* OTMLocationManager.m */,
594594
646086C9160CFB6700A1458B /* OTMLoginManager.h */,
595595
646086CA160CFB6700A1458B /* OTMLoginManager.m */,
596596
646086CB160CFB6700A1458B /* OTMPictureTaker.h */,
@@ -1028,7 +1028,6 @@
10281028
64608707160CFB6700A1458B /* OTMDetailCellRenderer.m in Sources */,
10291029
64608708160CFB6700A1458B /* OTMDetailTableViewCell.m in Sources */,
10301030
6470BEED18E0A8C700C56A8F /* OTMViewController.m in Sources */,
1031-
641210A91935363400118A33 /* OTMLocationManager.m in Sources */,
10321031
6470BEEA18DCAC5000C56A8F /* UIView+Borders.m in Sources */,
10331032
64608709160CFB6700A1458B /* OTMDistanceTableViewCell.m in Sources */,
10341033
6460870A160CFB6700A1458B /* OTMMapDetailCellRenderer.m in Sources */,
@@ -1045,6 +1044,7 @@
10451044
64608715160CFB6700A1458B /* OTMRegistrationViewController.m in Sources */,
10461045
64608716160CFB6700A1458B /* OTMScrollAwareViewController.m in Sources */,
10471046
64608717160CFB6700A1458B /* OTMSpeciesTableViewController.m in Sources */,
1047+
648B1A9E1DA8573E0059BEA6 /* CLLocation+AgeInSecondsLessThan.m in Sources */,
10481048
64608718160CFB6700A1458B /* OTMSplashViewController.m in Sources */,
10491049
64608719160CFB6700A1458B /* OTMTreeDetailViewController.m in Sources */,
10501050
6460871A160CFB6700A1458B /* OTMAddTreeAnnotationView.m in Sources */,

OpenTreeMap/src/OTM/OTMLocationManager.h renamed to OpenTreeMap/src/Categories/CLLocation+AgeInSecondsLessThan.h

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,10 @@
1313
// You should have received a copy of the GNU General Public License
1414
// along with OpenTreeMap. If not, see <http://www.gnu.org/licenses/>.
1515

16-
#import <Foundation/Foundation.h>
1716
#import <CoreLocation/CoreLocation.h>
1817

19-
@interface OTMLocationManager : NSObject <CLLocationManagerDelegate>
18+
@interface CLLocation (AgeInSecondsLessThan)
2019

21-
@property (nonatomic,strong) CLLocationManager *locationManager;
22-
@property (nonatomic,strong) CLLocation *mostAccurateLocationResponse;
23-
@property (nonatomic,assign) BOOL restrictDistance;
20+
-(BOOL)ageInSecondsLessThan:(float)age;
2421

25-
@property (nonatomic,copy) void (^locationFoundCallback)(CLLocation*, NSError*);
26-
27-
- (id)initWithDistanceRestriction:(BOOL)rd;
28-
- (void)findLocation:(void(^)(CLLocation *location, NSError *error))callback;
29-
- (void)findLocationWithAccuracy:(CLLocationAccuracy)accuracy callback:(void(^)(CLLocation*, NSError*))callback;
30-
- (void)stopFindingLocation;
31-
- (BOOL)locationServicesAvailable;
32-
33-
@end
22+
@end
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// This file is part of the OpenTreeMap code.
2+
//
3+
// OpenTreeMap is free software: you can redistribute it and/or modify
4+
// it under the terms of the GNU General Public License as published by
5+
// the Free Software Foundation, either version 3 of the License, or
6+
// (at your option) any later version.
7+
//
8+
// OpenTreeMap is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU General Public License
14+
// along with OpenTreeMap. If not, see <http://www.gnu.org/licenses/>.
15+
16+
#import "CLLocation+AgeInSecondsLessThan.h"
17+
18+
@implementation CLLocation (AgeInSecondsLessThan)
19+
20+
-(BOOL)ageInSecondsLessThan:(float)age
21+
{
22+
NSDate* eventDate = self.timestamp;
23+
NSTimeInterval locationAgeInSeconds = [eventDate timeIntervalSinceNow];
24+
return fabs(locationAgeInSeconds) < age;
25+
}
26+
27+
@end

OpenTreeMap/src/OTM/Controllers/OTMAboutViewController.m

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ - (void)viewDidUnload
7070
// Release any retained subviews of the main view.
7171
}
7272

73+
- (void)viewWillAppear:(BOOL)animated {
74+
// The about view does not need the user's location. Save the battery by turning off location updates.
75+
// We can not depend on order of viewWillAppear: and viewWillDisappear: calls when transitioning
76+
// between views. We would prefer to put these stopUpdatingLocation calls in the viewWillDisappear: method
77+
// of the view controllers that need location data, but we can't.
78+
[[SharedAppDelegate locationManager] stopUpdatingLocation];
79+
}
80+
7381
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
7482
{
7583
return (interfaceOrientation == UIInterfaceOrientationPortrait);

OpenTreeMap/src/OTM/Controllers/OTMInstanceSelectTableViewController.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,10 @@
1414
// along with OpenTreeMap. If not, see <http://www.gnu.org/licenses/>.
1515

1616
#import <UIKit/UIKit.h>
17-
#import "OTMLocationManager.h"
1817
#import "OTMAllInstancesTableViewController.h"
1918

20-
@interface OTMInstanceSelectTableViewController : UITableViewController<OTMAllInstancesViewControllerDelegate>
19+
@interface OTMInstanceSelectTableViewController : UITableViewController<CLLocationManagerDelegate, OTMAllInstancesViewControllerDelegate>
2120

22-
@property (nonatomic, strong) OTMLocationManager *locationManager;
2321
@property (nonatomic, strong) NSDictionary *instances;
2422
@property (nonatomic, strong) UIActivityIndicatorView *activityIndicatorView;
2523

OpenTreeMap/src/OTM/Controllers/OTMInstanceSelectTableViewController.m

Lines changed: 69 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// You should have received a copy of the GNU General Public License
1414
// along with OpenTreeMap. If not, see <http://www.gnu.org/licenses/>.
1515

16+
#import "CLLocation+AgeInSecondsLessThan.h"
1617
#import "OTMInstanceSelectTableViewController.h"
1718
#import "OTMPreferences.h"
1819
#import "OTMAllInstancesTableViewController.h"
@@ -70,10 +71,6 @@ - (void)viewWillAppear:(BOOL)animated
7071
[[self logInOutButton] setTitle:@"Login to OpenTreeMap" forState:UIControlStateNormal];
7172
}
7273

73-
if (![self locationManager]) {
74-
[self setLocationManager:[[OTMLocationManager alloc] initWithDistanceRestriction:NO]];
75-
}
76-
7774
[self loadInstances];
7875
}
7976

@@ -89,15 +86,15 @@ - (void)loadInstances
8986
if ([self oneOrMorePersonalInstancesInDictionary:json]) {
9087
[self updateInstancesFromDictionary:json];
9188
} else {
92-
[self loadNearbyInstances];
89+
[self startLoadingNearbyInstances];
9390
}
9491
} else {
9592
NSLog(@"Error getting instances for %@: %@", [[SharedAppDelegate loginManager] loggedInUser], error);
96-
[self loadNearbyInstances];
93+
[self startLoadingNearbyInstances];
9794
}
9895
}];
9996
} else {
100-
[self loadNearbyInstances];
97+
[self startLoadingNearbyInstances];
10198
}
10299
}
103100

@@ -110,33 +107,59 @@ - (BOOL)oneOrMorePersonalInstancesInDictionary:(NSDictionary *)json
110107
return NO;
111108
}
112109

113-
- (void)loadNearbyInstances
110+
111+
- (void)startUpdatingLocation
114112
{
115-
[self.activityIndicatorView startAnimating];
116-
[[self locationManager] findLocationWithAccuracy:kCLLocationAccuracyThreeKilometers
117-
callback:^(CLLocation *location, NSError *error) {
118-
[self.activityIndicatorView stopAnimating];
119-
// TODO: if there is an error, we need to show some list of instances
120-
if (!error) {
121-
[self.activityIndicatorView startAnimating];
122-
[[[OTMEnvironment sharedEnvironment] api]
123-
getInstancesNearLatitude:location.coordinate.latitude
124-
longitude:location.coordinate.longitude
125-
user:nil
126-
maxResults:5
127-
distance:320000 callback:^(id json, NSError *error) {
128-
[self.activityIndicatorView stopAnimating];
129-
if (!error) {
130-
NSLog(@"Retrieved instances: %@", json);
131-
[self updateInstancesFromDictionary:json];
132-
} else {
133-
NSLog(@"Error getting nearby instances: %@", error);
134-
}
135-
}];
136-
} else {
137-
NSLog(@"Error finding location: %@", error);
113+
NSLog(@"Starting location updates for the instance table view.");
114+
[[SharedAppDelegate locationManager] setDelegate:self];
115+
[[SharedAppDelegate locationManager] setDesiredAccuracy:kCLLocationAccuracyThreeKilometers];
116+
[[SharedAppDelegate locationManager] setDistanceFilter:kCLDistanceFilterNone];
117+
[[SharedAppDelegate locationManager] startUpdatingLocation];
118+
NSTimeInterval timeout = [[[OTMEnvironment sharedEnvironment] locationSearchTimeoutInSeconds] doubleValue];
119+
[self performSelector:@selector(stopUpdatingLocationAfterTimeout) withObject:nil afterDelay:timeout];
120+
}
121+
122+
- (void)stopUpdatingLocationAfterTimeout
123+
{
124+
NSLog(@"Stopping location updates for the instance table view after timeout.");
125+
[[SharedAppDelegate locationManager] stopUpdatingLocation];
126+
[[SharedAppDelegate locationManager] setDelegate:nil];
127+
[self.activityIndicatorView stopAnimating];
128+
}
129+
130+
- (void)startLoadingNearbyInstances
131+
{
132+
if ([CLLocationManager locationServicesEnabled]) {
133+
[self.activityIndicatorView startAnimating];
134+
if ([[SharedAppDelegate locationManager] respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
135+
[[SharedAppDelegate locationManager] requestWhenInUseAuthorization];
138136
}
139-
}];
137+
[self startUpdatingLocation];
138+
}
139+
}
140+
141+
- (void)finishLoadingNearbyInstancesWithLocation:(CLLocation*)location
142+
{
143+
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(stopUpdatingLocationAfterTimeout) object:nil];
144+
NSLog(@"Stopping location updates for the instance table view");
145+
[[SharedAppDelegate locationManager] stopUpdatingLocation];
146+
[[SharedAppDelegate locationManager] setDelegate:nil];
147+
148+
[[[OTMEnvironment sharedEnvironment] api]
149+
getInstancesNearLatitude:location.coordinate.latitude
150+
longitude:location.coordinate.longitude
151+
user:nil
152+
maxResults:5
153+
distance:320000 callback:^(id json, NSError *error) {
154+
[self.activityIndicatorView stopAnimating];
155+
if (!error) {
156+
NSLog(@"Retrieved instances: %@", json);
157+
[self updateInstancesFromDictionary:json];
158+
} else {
159+
// TODO: if there is an error, we need to show some list of instances
160+
NSLog(@"Error getting nearby instances: %@", error);
161+
}
162+
}];
140163
}
141164

142165
- (void)updateInstancesFromDictionary:(NSDictionary *)instances
@@ -395,5 +418,19 @@ - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
395418
}
396419
}
397420

421+
#pragma mark - CLLocationManagerDelegate methods
422+
423+
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
424+
{
425+
CLLocation* location = locations.lastObject;
426+
// CoreLocation caches the last location value and passes to the app when the app first asks for the user's
427+
// location. This could be a stale location from far in the past, so we filter out location results that
428+
// are too old.
429+
if ([location ageInSecondsLessThan:kOTMMaxLocationAgeInSeconds]) {
430+
[self finishLoadingNearbyInstancesWithLocation:location];
431+
} else {
432+
NSLog(@"Skipping location %f seconds or more in age.", kOTMMaxLocationAgeInSeconds);
433+
}
434+
}
398435

399436
@end

OpenTreeMap/src/OTM/Controllers/OTMMapViewController.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#import "OTMViewController.h"
2020
#import "OTMAddTreeAnnotationView.h"
2121
#import "OTMTreeDetailViewController.h"
22-
#import "OTMLocationManager.h"
2322

2423
#define kOTMMapViewControllerImageUpdate @"kOTMMapViewControllerImageUpdate"
2524

@@ -30,11 +29,15 @@ typedef enum {
3029
Move,
3130
} OTMMapViewControllerMapMode;
3231

33-
@interface OTMMapViewController : OTMViewController <MKMapViewDelegate, UIGestureRecognizerDelegate, UISearchBarDelegate, OTMAddTreeAnnotationViewDelegate, OTMTreeDetailViewDelegate> {
32+
@interface OTMMapViewController : OTMViewController <MKMapViewDelegate, UIGestureRecognizerDelegate, UISearchBarDelegate, CLLocationManagerDelegate, OTMAddTreeAnnotationViewDelegate, OTMTreeDetailViewDelegate> {
3433
IBOutlet MKMapView *mapView;
3534
IBOutlet UISearchBar *searchBar;
3635
IBOutlet UIButton *findLocationButton;
37-
BOOL firstAppearance;
36+
37+
// We only want to zoom to the user's location when the map first appears, and when
38+
// a zoom is explicitly requested by clicking a button. Location updates are received
39+
// asynchronously, so we use this boolean flag for coordination.
40+
BOOL zoomWhenLocationFound;
3841

3942
MKTileOverlay *plotsOverlay;
4043
}
@@ -58,8 +61,6 @@ typedef enum {
5861
@property (nonatomic, strong) IBOutlet UIView *filterStatusView;
5962
@property (nonatomic, strong) IBOutlet UILabel *filterStatusLabel;
6063

61-
@property (nonatomic, strong) OTMLocationManager *locationManager;
62-
6364
@property (nonatomic,strong) NSDictionary* selectedPlot;
6465

6566
@property (nonatomic) OTMMapViewControllerMapMode mode;
@@ -71,7 +72,7 @@ typedef enum {
7172
-(void)setDetailViewData:(NSDictionary*)plot;
7273

7374
-(IBAction) showTreePhotoFullscreen:(id)sender;
74-
-(IBAction) startFindingLocation:(id)sender;
75-
-(IBAction) stopFindingLocation:(id)sender;
75+
-(IBAction) startZoomToCurrentLocation:(id)sender;
76+
-(IBAction) stopZoomToCurrentLocation:(id)sender;
7677

7778
@end

0 commit comments

Comments
 (0)