【问题标题】:What's the easiest way to get the current location of an iPhone?获取 iPhone 当前位置的最简单方法是什么?
【发布时间】:2023-03-13 23:21:01
【问题描述】:

我已经知道如何使用 CLLocationManager,所以我可以通过委托和所有其他方式来做这件事。

但我想要一个方便的方法,只获取当前位置,一次,然后阻塞直到它得到结果。

【问题讨论】:

  • 您应该澄清这个问题:如果您知道如何使用 CLLocationManager - 实施这种“便捷方法”有什么问题?
  • 我认为问题在于它并不那么容易。通常它会首先返回一个旧位置,然后是一个非常不准确的位置,然后是逐渐更好的位置,您在什么时候决定拥有您想要的位置?这完全取决于你。

标签: iphone core-location


【解决方案1】:

没有“方便方法”,除非您自己编写代码,但您仍然需要在任何自定义代码中实现委托方法,以使事情变得“方便”。

委托模式的存在是有原因的,因为委托是 Objective-C 的重要组成部分,我建议您熟悉它们。

【讨论】:

    【解决方案2】:

    我所做的是实现一个单例类来管理来自核心位置的更新。要访问我当前的位置,我会执行 CLLocation *myLocation = [[LocationManager sharedInstance] currentLocation]; 如果您想阻止主线程,您可以执行以下操作:

    while ([[LocationManager sharedInstance] locationKnown] == NO){
       //blocking here
       //do stuff here, dont forget to have some kind of timeout to get out of this blocked    //state
    }
    

    然而,正如已经指出的那样,阻塞主线程可能不是一个好主意,但是当你正在构建一些东西时,这可能是一个很好的起点。您还会注意到我编写的类检查位置更新的时间戳并忽略任何旧的,以防止从核心位置获取陈旧数据的问题。

    这是我写的单例类。请注意,它的边缘有点粗糙:

    #import <CoreLocation/CoreLocation.h>
    #import <Foundation/Foundation.h>
    
    @interface  LocationController : NSObject <CLLocationManagerDelegate> {
        CLLocationManager *locationManager;
        CLLocation *currentLocation;
    }
    
    + (LocationController *)sharedInstance;
    
    -(void) start;
    -(void) stop;
    -(BOOL) locationKnown;
    
    @property (nonatomic, retain) CLLocation *currentLocation;
    
    @end
    @implementation LocationController
    
    @synthesize currentLocation;
    
    static LocationController *sharedInstance;
    
    + (LocationController *)sharedInstance {
        @synchronized(self) {
            if (!sharedInstance)
                sharedInstance=[[LocationController alloc] init];       
        }
        return sharedInstance;
    }
    
    +(id)alloc {
        @synchronized(self) {
            NSAssert(sharedInstance == nil, @"Attempted to allocate a second instance of a singleton LocationController.");
            sharedInstance = [super alloc];
        }
        return sharedInstance;
    }
    
    -(id) init {
        if (self = [super init]) {
            self.currentLocation = [[CLLocation alloc] init];
            locationManager = [[CLLocationManager alloc] init];
            locationManager.delegate = self;
            [self start];
        }
        return self;
    }
    
    -(void) start {
        [locationManager startUpdatingLocation];
    }
    
    -(void) stop {
        [locationManager stopUpdatingLocation];
    }
    
    -(BOOL) locationKnown { 
         if (round(currentLocation.speed) == -1) return NO; else return YES; 
    }
    
    - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
        //if the time interval returned from core location is more than two minutes we ignore it because it might be from an old session
        if ( abs([newLocation.timestamp timeIntervalSinceDate: [NSDate date]]) < 120) {     
            self.currentLocation = newLocation;
        }
    }
    
    - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
        UIAlertView *alert;
        alert = [[UIAlertView alloc] initWithTitle:@"Error" message:[error description] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];
        [alert release];
    }
    
    -(void) dealloc {
        [locationManager release];
        [currentLocation release];
        [super dealloc];
    }
    
    @end
    

    【讨论】:

    • 感谢您的解决方案。这是一个不错的小控制器。我发现的一件事是 locationKnown 方法不是很准确 - 它总是返回 YES 因为 currentLocation 是在 init 方法中初始化的。这是使用 speed 属性来确定是否已收到用户位置的方法的替代版本: -(BOOL) locationKnown { if (round(currentLocation.speed) == -1) return NO;否则返回YES; }
    • 你只分配了资源,还没有释放。你能告诉我你会在哪里发布它不会引起任何问题。
    【解决方案3】:

    没有这样的便利,您不应该创建自己的。在 iPhone 这样的设备上,“阻塞直到得到结果”是非常糟糕的编程习惯。检索位置可能需要几秒钟;你永远不应该让你的用户那样等待,并且代表确保他们不会。

    【讨论】:

    • 线程,是的,当与完成时的回调结合使用时。在原始运行循环上安排回调的位置。这为您提供了一个与 CLLocationManager 的委托完全一样的界面。在最终用户应用程序中,“阻止直到完成”从来都不是一个好的设计。
    • 我不同意,“阻塞直到完成”在各种应用中有许多合法用途,并且不是“冻结 UI 直到完成”的同义词。试图使 everything 异步只会导致奇怪的行为和错误的应用程序。如果需要显示的信息尚不可用,谁在乎 UI 显示/响应的速度?异步操作有自己的位置,阻塞操作也有。这不是一个好一个坏的情况。
    • “谁在乎 UI 出现/响应的速度”?用户可以。冻结的 UI 令人沮丧,应该不惜一切代价避免。
    • 异步代码更难调试,因为它失去了清晰度。轮询循环使用不当会导致 100% 的 CPU 负载并会耗尽电池。 sleep() 导致 UI 延迟。通知和/或委托是要走的路(虽然回调是丑陋的......块ftw)。工程就是权衡取舍、懒惰和满足用户。
    【解决方案4】:

    我很欣赏 Brad Smith 的回答。实现它我发现他使用的其中一种方法在 iOS6 中已被弃用。要编写适用于 iOS5 和 iOS6 的代码,请使用以下代码:

    - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
        if (abs([[locations lastObject] timeIntervalSinceDate:[NSDate date]]) < 120) {
            [self setCurrentLocation:[locations lastObject]];
        }
    }
    
    // Backward compatibility with iOS5.
    - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
        NSArray *locations = [NSArray arrayWithObjects:oldLocation, newLocation, nil];
        [self locationManager:manager didUpdateLocations:locations];
    }
    

    【讨论】:

      【解决方案5】:

      我简化并组合了多个答案,仅当位置有效时才会更新。

      它也适用于 OSX 和 iOS。

      这假设了用户突然需要当前位置的用例。如果在此示例中花费超过 100 毫秒,则将其视为错误。 (假设 GPS IC 和| Wifi(Apple 的 Skyhook 克隆)已经启动并且已经有了很好的修复。)

      #import "LocationManager.h"
      
      // wait up to 100 ms
      CLLocation *location = [LocationManager currentLocationByWaitingUpToMilliseconds:100];
      if (!location) {
          NSLog(@"no location :(");
          return; 
      }
      // location is good, yay
      

      https://gist.github.com/6972228

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-09
        • 2011-04-01
        • 2018-03-17
        • 2011-06-30
        • 2013-03-23
        相关资源
        最近更新 更多