iBeaconアプリの作り方(Xcode6.2 + Objective-C)
1, Buld PhasesのLink Binary With Librariesで、CoreLocation.frameworkをプロジェクトに追加する。
2, Infoに、iBeacon使用許可の項目を追加する。Custom iOS Target Propertiesに二行追加(適当な行を選択して+ボタンを押す)
最初の起動で、ここに書いたメッセージが表示されます。
a, NSLocationWhenInUseUsageDescription(アプリ使用時のみ位置情報を利用する理由を書く)
b, NSLocationAlwaysUsageDescription(常に位置情報を利用する理由を書く)
3, AppDelegate.mに通知許可を表示する処理を追加する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. // 通知許可を最初にもらう UIUserNotificationType types = UIUserNotificationTypeBadge| UIUserNotificationTypeSound| UIUserNotificationTypeAlert; UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil]; [application registerUserNotificationSettings:mySettings]; return YES; } |
4, ViewController.hに、CoreLocation.frameworkのインポートとプロトコルを追加する。
※Objective-Cのプロトコルとは、クラス継承する時に「この関数(メソッド)は絶対に実装しなさいよ!」と強制する機能の事です。
また、iBeacon系の変数も追加しておく。
1 2 3 4 5 6 7 8 |
#import <CoreLocation/CoreLocation.h> @interface ViewController : UIViewController<CLLocationManagerDelegate> @property CLLocationManager *locationManager; @property NSUUID *proximityUUID; @property CLBeaconRegion *beaconRegion; @property CLBeacon *nearestBeacon; @property NSString *str; |
5, プロトコルに従い、以下のメソッドを実装していく。
1, 最初に位置情報の許可を得る
didChangeAuthorizationStatus:(CLAuthorizationStatus)status
2, リージョン(iBeaconの電波内)に入った時の処理(モニタリング開始する)
didEnterRegion:(CLRegion *)region
3, リージョン(iBeaconの電波内)から出た時の処理(モニタリング終了する)
didExitRegion:(CLRegion *)region
4, 実際にモニタリング(ibeaconまでの距離測定など)監視開始
didStartMonitoringForRegion:(CLRegion *)region
5, 位置情報取得に成功した時に呼ばれる処理
didUpdateLocations:(NSArray *)locations {
6, 領域内にいるかどうかを確認する処理(この処理が無いと、アプリ起動時にリージョン内だった場合にレンジングが開始されない)
didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
7, 現在の状況を1秒単位で測定する処理
didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
6, 以上の実装をすれば、iBeaconアプリとして利用できるはず…。
具体的なソースはgistにあります。動作的には、こんな感じ
iBeaconの勉強会用デモに寄生獣ごっこアプリを作って、YouTubeにアップしてみた。
[youtube]https://www.youtube.com/watch?v=KxxW55vkhj0[/youtube]
// | |
// ViewController.h | |
// 寄生獣 | |
// | |
// Created by fddcddhdd on 2014/12/11. | |
// Copyright (c) 2014年 fddcddhdd. All rights reserved. | |
// | |
#import <UIKit/UIKit.h> | |
#import <CoreLocation/CoreLocation.h> | |
@interface ViewController : UIViewController<CLLocationManagerDelegate> | |
@property CLLocationManager *locationManager; | |
@property NSUUID *proximityUUID; | |
@property CLBeaconRegion *beaconRegion; | |
@property CLBeacon *nearestBeacon; | |
@property NSString *str; | |
@end | |
ここまでヘッダー | |
ここからメイン | |
// | |
// ViewController.m | |
// 寄生獣 | |
// | |
// Created by fddcddhdd on 2014/12/11. | |
// Copyright (c) 2014年 fddcddhdd. All rights reserved. | |
// | |
#import "ViewController.h" | |
@interface ViewController () | |
@end | |
@implementation ViewController | |
//画面表示用のラベル変数 | |
UILabel *label; | |
CGRect rect; | |
- (void)viewDidLoad { | |
[super viewDidLoad]; | |
// Do any additional setup after loading the view, typically from a nib. | |
// ミギーの画像をロード | |
UIImage *img = [UIImage imageNamed:@"miggie.png"]; | |
// 画像クラスを生成 | |
UIImageView *iv = [[UIImageView alloc] initWithImage:img]; | |
// ミギー画像を、画面の中央に配置 | |
// iv.center = self.view.center; | |
// 画像クラスを画面クラスに追加して見えるようにする。 | |
[self.view addSubview:iv]; | |
// 表示する文字(ラベル)の設置 | |
rect = CGRectMake(0, 0, 120, 200); | |
label = [[UILabel alloc]initWithFrame:rect]; | |
// ラベルのテキストの行数設定 | |
label.numberOfLines = 0; // 0の場合は無制限 | |
// ラベルのテキストの位置を設定 | |
CGPoint point = CGPointMake(self.view.center.x+50, self.view.center.y-50); | |
label.center = point; | |
label.text = @"ミギーのセリフ"; | |
// ラベルをビューに追加 | |
[self.view addSubview:label]; | |
// ラベルのテキストのフォントを設定 | |
//label.font = [UIFont fontWithName:@"Helvetica" size:16]; | |
// ラベルのテキストの色を設定 | |
//label.textColor = [UIColor blueColor]; | |
// ラベルのテキストの影を設定 | |
//label.shadowColor = [UIColor grayColor]; | |
//label.shadowOffset = CGSizeMake(1, 1); | |
// ラベルの背景色を設定 | |
//label.backgroundColor = [UIColor whiteColor]; | |
//iBeaconが使用可能かチェック | |
if ([CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]]) { | |
// CLLocationManagerの生成とデリゲートの設定 | |
self.locationManager = [CLLocationManager new]; | |
self.locationManager.delegate = self; | |
// 生成したUUIDからNSUUIDを作成 | |
self.proximityUUID = [[NSUUID alloc] initWithUUIDString:@"00000000-0000-0000-0000-000000000000"]; | |
// CLBeaconRegionを作成 | |
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:self.proximityUUID | |
identifier:@"jp.classmethod.testregion"]; | |
// いずれもデフォルト設定値 | |
self.beaconRegion.notifyOnEntry = YES; | |
self.beaconRegion.notifyOnExit = YES; | |
self.beaconRegion.notifyEntryStateOnDisplay = NO; | |
// Beaconによる領域観測(モニタリング)を開始。これでdidEnterRegion/didExitRegion/didStartMonitoringForRegionがコールされるようになる | |
[self.locationManager startMonitoringForRegion:self.beaconRegion]; | |
//位置情報取得に対する許可が、iOS7/8で違う | |
if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) { | |
// iOS バージョンが 8 以上で、requestAlwaysAuthorization メソッドが | |
// 利用できる場合 | |
// 位置情報測位の許可を求めるメッセージを表示する | |
[self.locationManager requestAlwaysAuthorization]; | |
// [self.locationManager requestWhenInUseAuthorization]; | |
} else { | |
// iOS バージョンが 8 未満で、requestAlwaysAuthorization メソッドが | |
// 利用できない場合 | |
// 測位を開始する | |
[self.locationManager startUpdatingLocation]; | |
} | |
}else{ | |
label.text = @"シュミレータでは、iBeaconが使えない!"; | |
} | |
} | |
//iOS8から位置情報取得に関する許可システムが変更されました。 | |
//「常に許可」「アプリ起動時のみ許可」の場合だけ位置情報を取得できる | |
- (void)locationManager:(CLLocationManager *)manager | |
didChangeAuthorizationStatus:(CLAuthorizationStatus)status { | |
// アプリ設定のInfoに、以下のkeyを追加して位置情報を取得する説明を書く | |
//「NSLocationAlwaysUsageDescription」「NSLocationWhenInUseUsageDescription」 | |
if (status == kCLAuthorizationStatusAuthorizedAlways || | |
status == kCLAuthorizationStatusAuthorizedWhenInUse) { | |
// 位置情報測位の許可状態が「常に許可」または「使用中のみ」の場合、 | |
// 測位を開始する(iOS バージョンが 8 以上の場合のみ該当する) | |
// ※iOS8 以上の場合、位置情報測位が許可されていない状態で | |
// startUpdatingLocation メソッドを呼び出しても、何も行われない。 | |
[self.locationManager startUpdatingLocation]; | |
}else{ | |
label.text = @"位置情報取得が許可されていない!"; | |
} | |
} | |
//リージョンに入った時の処理(モニタリング) | |
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region | |
{ | |
// ローカル通知 | |
[self sendLocalNotificationForMessage:@"仲間がいるぞ!"]; | |
// Beaconの距離測定を開始する | |
if ([region isMemberOfClass:[CLBeaconRegion class]] && [CLLocationManager isRangingAvailable]) { | |
[self.locationManager startRangingBeaconsInRegion:(CLBeaconRegion *)region]; | |
} | |
} | |
//リージョンから出た時の処理(モニタリング) | |
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region | |
{ | |
// ローカル通知 | |
[self sendLocalNotificationForMessage:@"完全に見失った…。"]; | |
// Beaconの距離測定を終了する | |
if ([region isMemberOfClass:[CLBeaconRegion class]] && [CLLocationManager isRangingAvailable]) { | |
[self.locationManager stopRangingBeaconsInRegion:(CLBeaconRegion *)region]; | |
} | |
} | |
// モニタリング監視開始 | |
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region | |
{ | |
// 開始時に既に領域内に入っているかどうかチェック | |
[self.locationManager requestStateForRegion:(CLBeaconRegion *)region]; | |
// [self.locationManager performSelector:@selector(requestStateForRegion:) withObject:region afterDelay:1]; | |
//_testLabel.text = @"監視開始"; | |
NSLog(@"didStartMonitoringForRegion:%@", region.identifier); | |
} | |
//位置情報取得に成功した時に呼ばれる処理 | |
- (void)locationManager:(CLLocationManager *)manager | |
didUpdateLocations:(NSArray *)locations { | |
CLLocation *location = [locations lastObject]; | |
NSLog(@"%f %f", | |
location.coordinate.latitude, | |
location.coordinate.longitude); | |
[self.locationManager stopUpdatingLocation]; | |
} | |
// 領域内にいるかどうかを確認する処理(この処理が無いと、アプリ起動時にリージョン内だった場合にレンジングが開始されない) | |
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region{ | |
//_testLabel.text = @"領域内にいるかどうかを確認"; | |
switch (state) { | |
//アプリ起動時に、iBeaconのリージョン内 | |
case CLRegionStateInside: | |
if([region isMemberOfClass:[CLBeaconRegion class]] && [CLLocationManager isRangingAvailable]){ | |
//[self sendLocalNotificationForMessage:@"いらっしゃいませ。"]; | |
NSLog(@"Enter %@",region.identifier); | |
//アプリ起動時に領域内にいる時は、明示的にレンジングを開始させる | |
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion]; | |
} | |
break; | |
//アプリ起動時に、iBeaconのリージョン外 | |
case CLRegionStateOutside: | |
label.text = @"近くに仲間は\n居ないようだ"; | |
NSLog(@"Exit %@",region.identifier); | |
//[self sendLocalNotificationForMessage:@"ありがとうございました。"]; | |
break; | |
// リージョンの内か外か不明な状態 | |
case CLRegionStateUnknown: | |
NSLog(@"Unknown %@",region.identifier); | |
[self sendLocalNotificationForMessage:@"よく分からん…。"]; | |
break; | |
default: | |
break; | |
} | |
} | |
//現在の状況を1秒単位で測定する処理 | |
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region | |
{ | |
// iBeaconが1個以上認識できたら | |
if (beacons.count > 0) { | |
// 最も距離の近いBeaconについて処理する | |
CLBeacon *nearestBeacon = beacons.firstObject; | |
// Beacon の距離でメッセージを変える(わりとunknownが最初に来るので、近い順に記述する事!) | |
NSString *rangeMessage; | |
switch (nearestBeacon.proximity) { | |
case CLProximityImmediate: | |
rangeMessage = @"見えていないのか?"; | |
break; | |
case CLProximityNear: | |
rangeMessage = @"目の前だ!"; | |
break; | |
case CLProximityFar: | |
rangeMessage = @"近いぞ!"; | |
break; | |
default: //unknown | |
rangeMessage = @"どこだ!?"; | |
break; | |
} | |
// ローカル通知 | |
//NSString *message = [NSString stringWithFormat:@"major:%@, minor:%@, accuracy:%0.2f, rssi:%d", | |
// nearestBeacon.major, nearestBeacon.minor, nearestBeacon.accuracy, nearestBeacon.rssi]; | |
//_testLabel.text = [rangeMessage stringByAppendingString:message]; | |
//_testLabel.text = @""; | |
//ミギーのセリフ文字列 | |
NSString *msg = [NSString stringWithFormat:@"シンイチ!\n仲間だ!\n%@\n距離は%0.2fm",rangeMessage, nearestBeacon.accuracy]; | |
label.text = msg; | |
} | |
} | |
//メッセージ送信用の自作関数 | |
- (void)sendLocalNotificationForMessage:(NSString *)message | |
{ | |
UILocalNotification *localNotification = [UILocalNotification new]; | |
localNotification.alertBody = message; | |
localNotification.fireDate = [NSDate date]; | |
localNotification.soundName = UILocalNotificationDefaultSoundName; | |
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification]; | |
} | |
- (void)didReceiveMemoryWarning { | |
[super didReceiveMemoryWarning]; | |
// Dispose of any resources that can be recreated. | |
} | |
@end |
それにしてもXcode6.2にしてから、MacBook Air 2011(mid)をクラムシェルで使っていると、常時ファンが回転している…。
充電を止めて、通常使用でもファンが回る〜。流石に4年も使っているとメイン開発機としては、ちょっと辛いって事か。