如何用Objective-C实现用户靠近收藏餐厅时触发本地通知?
嘿,刚好做过类似的iOS区域监测+本地通知功能,给你整理了一套可以直接集成到项目里的Objective-C代码方案,分模块来,你直接跟着来就行:
核心思路
我们会用CoreLocation框架的**地理围栏(Region Monitoring)**来监测用户是否进入收藏餐厅的范围,再结合UserNotifications框架发送本地通知。需要先搞定权限申请,再配置区域监测,最后处理进入区域的回调发送通知。
第一步:配置Info.plist权限
首先得在Info.plist里添加必要的权限描述,否则会被系统拒:
- 新增
NSLocationAlwaysAndWhenInUseUsageDescription:填写类似“需要在后台监测你靠近收藏餐厅的位置,以便及时发送通知提醒” - 新增
NSLocationWhenInUseUsageDescription:备用的前台权限说明,比如“需要获取你的位置来监测是否靠近收藏餐厅” - 新增
NSNotificationAlertUsageDescription:通知权限说明,比如“当你靠近收藏餐厅时,我们会发送通知提醒你”
第二步:完整代码实现
你可以创建一个专门的LocationMonitoringManager类来管理这些逻辑,这样代码更清晰:
#import <Foundation/Foundation.h> #import <CoreLocation/CoreLocation.h> #import <UserNotifications/UserNotifications.h> @interface LocationMonitoringManager : NSObject <CLLocationManagerDelegate, UNUserNotificationCenterDelegate> // 单例模式,方便全局调用 + (instancetype)sharedManager; // 添加收藏餐厅的地理围栏(传入你的餐厅数据数组) - (void)addFavoriteRestaurantRegionsWithRestaurants:(NSArray *)restaurants; @end @implementation LocationMonitoringManager @property (nonatomic, strong) CLLocationManager *locationManager; + (instancetype)sharedManager { static LocationMonitoringManager *manager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ manager = [[LocationMonitoringManager alloc] init]; }); return manager; } - (instancetype)init { self = [super init]; if (self) { _locationManager = [[CLLocationManager alloc] init]; _locationManager.delegate = self; _locationManager.desiredAccuracy = kCLLocationAccuracyBest; // 允许后台位置更新,确保后台也能监测区域 _locationManager.allowsBackgroundLocationUpdates = YES; _locationManager.pausesLocationUpdatesAutomatically = NO; // 先申请通知权限 [self requestNotificationAuthorization]; // 申请位置权限 [self requestLocationAuthorization]; } return self; } #pragma mark - 权限申请 - (void)requestNotificationAuthorization { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; center.delegate = self; [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) { if (!granted) { NSLog(@"用户拒绝了通知权限,无法发送餐厅提醒"); } }]; } - (void)requestLocationAuthorization { CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; if (status == kCLAuthorizationStatusNotDetermined) { // 必须申请Always权限,后台监测区域需要这个 [self.locationManager requestAlwaysAuthorization]; } else if (status == kCLAuthorizationStatusAuthorizedAlways) { // 已经有权限,直接添加区域 // 这里可以根据你的需求,传入收藏餐厅数组 // [self addFavoriteRestaurantRegionsWithRestaurants:yourFavoriteRestaurants]; } else { NSLog(@"位置权限不足,无法使用区域监测功能"); } } #pragma mark - 添加地理围栏 - (void)addFavoriteRestaurantRegionsWithRestaurants:(NSArray *)restaurants { // 先移除已有的监测区域,避免重复添加 for (CLRegion *region in self.locationManager.monitoredRegions) { [self.locationManager stopMonitoringForRegion:region]; } // 遍历收藏餐厅,添加对应的地理围栏 for (NSDictionary *restaurant in restaurants) { // 假设你的餐厅数据结构是:name(餐厅名)、lat(纬度)、lon(经度)、radius(监测半径,推荐≥100米) NSString *restaurantName = restaurant[@"name"]; CLLocationDegrees lat = [restaurant[@"lat"] doubleValue]; CLLocationDegrees lon = [restaurant[@"lon"] doubleValue]; CLLocationDistance radius = [restaurant[@"radius"] doubleValue]; CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(lat, lon); CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:coordinate radius:radius identifier:restaurantName]; // 只在进入区域时触发通知 region.notifyOnEntry = YES; region.notifyOnExit = NO; [self.locationManager startMonitoringForRegion:region]; NSLog(@"已添加餐厅监测:%@", restaurantName); } } #pragma mark - CLLocationManagerDelegate // 用户进入监测区域时的回调 - (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region { if ([region isKindOfClass:[CLCircularRegion class]]) { // 发送本地通知 [self sendRestaurantNotificationWithName:region.identifier]; } } // 权限状态变化时的回调 - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { if (status == kCLAuthorizationStatusAuthorizedAlways) { // 权限通过后,可以在这里调用添加地理围栏的方法 // 比如从本地缓存读取收藏餐厅,再调用addFavoriteRestaurantRegionsWithRestaurants: } } #pragma mark - 发送本地通知 - (void)sendRestaurantNotificationWithName:(NSString *)restaurantName { UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; content.title = @"🎯 靠近收藏餐厅啦"; content.body = [NSString stringWithFormat:@"你已经来到「%@」附近,快去打卡吧!", restaurantName]; content.sound = [UNNotificationSound defaultSound]; content.badge = @1; // 可选,设置角标 // 立即发送通知 UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.1 repeats:NO]; NSString *requestID = [NSString stringWithFormat:@"restaurant_notify_%@", [NSDate dateWithTimeIntervalSinceNow:0]]; UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:requestID content:content trigger:trigger]; UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { if (error) { NSLog(@"发送通知失败:%@", error.localizedDescription); } }]; } #pragma mark - UNUserNotificationCenterDelegate // 前台收到通知时的处理(可选,让前台也显示通知) - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionBadge); } @end
第三步:使用方式
在你的AppDelegate或者需要的地方,这样调用:
// 假设你从服务器/本地获取到的收藏餐厅数组 NSArray *favoriteRestaurants = @[ @{@"name": @"老上海面馆", @"lat": @31.2304, @"lon": @121.4737, @"radius": @150}, @{@"name": @"粤式茶餐厅", @"lat": @31.2057, @"lon": @121.5919, @"radius": @120} ]; [[LocationMonitoringManager sharedManager] addFavoriteRestaurantRegionsWithRestaurants:favoriteRestaurants];
重要注意事项
- 真实设备测试:模拟器的位置模拟对地理围栏支持不好,一定要用真机测试
- 地理围栏数量限制:iOS每个App最多允许注册20个地理围栏,如果你的收藏餐厅超过20个,建议只监测用户当前位置附近的几个,或者动态替换
- 半径设置:推荐半径≥100米,太小的话系统可能不会触发监测(因为GPS精度问题)
- 重启设备后重新添加:iOS重启后,之前注册的地理围栏会被清除,所以要在App启动时重新调用添加方法
- 后台权限:必须确保用户授予了「始终允许」的位置权限,否则后台无法监测区域进入事件
内容的提问来源于stack exchange,提问作者Vishal
相关产品推荐
相关产品推荐

