iPhoneで取得・検知できる情報10連発！（サンプルアプリもあるよ）


こんにちは！12/11担当の [@koogawa](https://twitter.com/koogawa) です。どうぞよろしくお願いします。

さて、iPhoneには「加速度センサー」や「電子コンパス」など、いろいろなセンサーが搭載されていることはご存知だと思います。そこで今回は、iPhoneで検知できる様々な情報とその実装方法を、10個ピックアップしてまとめてました。メジャーなものからマニアックなものまで色々揃えましたので、何か一つでもお役に立てたら嬉しいです。

では、いってみましょう！

# 光・音声系

## 1. 輝度センサー

iPhoneの画面輝度（明るさ）が取得・設定できます。

画面輝度＝周りの明るさなので、輝度が高ければユーザが明るい場所にいると判断することもできそうです。ただ、中には画面輝度を固定に設定しているユーザもいるので、一概にこの基準が当てはまるとは限りません。

### 実装方法

画面輝度は次のように取得できます。

```objc
CGFloat brightness = [UIScreen mainScreen].brightness;
```

0.0〜1.0 の値が返ってきます。数値が増えるほど明るくなります。

### 詳しい記事

* [【Tips】iOSで輝度センサーを使う](http://blog.koogawa.com/entry/2013/11/17/111304)

### サンプルコード

* https://github.com/koogawa/iSensor/blob/master/iSensor/BrightnessViewController.m

## 2. 近接センサー

通話中、画面に顔が近付くと自動的に画面をロックしてくれるアレです。もちろん顔じゃなくても反応します。

### 実装方法

まずは近接センサーをオンにします。

```objc
[UIDevice currentDevice].proximityMonitoringEnabled = YES;
```

次に、近接センサーを監視します。

近接センサーが反応すると、`UIDeviceProximityStateDidChangeNotification` が飛んでくるので、それを監視します。

```objc
[[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(proximitySensorStateDidChange:)
                                                 name:UIDeviceProximityStateDidChangeNotification
                                               object:nil];
```

### 詳しい記事

* [【Tips】iOSで近接センサーを使う](http://blog.koogawa.com/entry/2013/11/17/133500)

### サンプルコード

* https://github.com/koogawa/iSensor/blob/master/iSensor/ProximityViewController.m

## 3. シェイクジェスチャー

iPhoneを振ったときの動作を検知します。

### 実装方法

シェイクジェスチャーを検知するためにはファーストレスポンダにならないといけないので、`canBecomeFirstResonder` をオーバライドして `YES` を返します。

```objc
- (BOOL)canBecomeFirstResponder {
    return YES;
}
```

この状態でiPhoneを振ると、次のメソッドが呼ばれるようになります。

```objc
// シェイク開始
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake)  {
        NSLog(@"Motion began");
    }
}

// シェイク完了
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
        NSLog(@"Motion ended");
    }
}
```

このメソッドを利用して、シェイクジェスチャーを検知します。

### 詳しい記事

* [【Tips】iOSでシェイクジェスチャーを検知する](http://blog.koogawa.com/entry/2013/11/20/222242)

### サンプルコード

* https://github.com/koogawa/iSensor/blob/master/iSensor/ShakeViewController.m

## 4. マイクの音

Core Audioを使用して、マイクから音を検知します。

### 実装方法

記録するデータフォーマットを決めます。

```objc
AudioStreamBasicDescription dataFormat;
dataFormat.mSampleRate = 44100.0f;
dataFormat.mFormatID = kAudioFormatLinearPCM;
dataFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian | kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
dataFormat.mBytesPerPacket = 2;
dataFormat.mFramesPerPacket = 1;
dataFormat.mBytesPerFrame = 2;
dataFormat.mChannelsPerFrame = 1;
dataFormat.mBitsPerChannel = 16;
dataFormat.mReserved = 0;
```

音の監視を開始します。

```objc
AudioQueueNewInput(&dataFormat, AudioInputCallback, (__bridge void *)(self), CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &_queue);
AudioQueueStart(_queue, NULL);
```

マイクのレベルを取得できるように、レベルメータを有効化しておきます。

```objc
UInt32 enabledLevelMeter = true;
AudioQueueSetProperty(_queue, kAudioQueueProperty_EnableLevelMetering, &enabledLevelMeter, sizeof(UInt32));
```

一定時間ごとにレベルを取得して、画面に表示します。

```objc
- (void)detectVolume:(NSTimer *)timer
{
    // レベルを取得
    AudioQueueLevelMeterState levelMeter;
    UInt32 levelMeterSize = sizeof(AudioQueueLevelMeterState);
    AudioQueueGetProperty(_queue, kAudioQueueProperty_CurrentLevelMeterDB, &levelMeter, &levelMeterSize);

    // 最大レベル、平均レベルを表示
    self.peakTextField.text = [NSString stringWithFormat:@"%.2f", levelMeter.mPeakPower];
    self.averageTextField.text = [NSString stringWithFormat:@"%.2f", levelMeter.mAveragePower];
}
```

通常、`mPeakPower`、`mAveragePower` には負の値がセットされており、音量に比例して値も大きくなっていきます。最大値は 0 になります。

### 詳しい記事

* [【Tips】iOSでマイクの音を検知する](http://blog.koogawa.com/entry/2013/11/24/121807)

### サンプルコード

* https://github.com/koogawa/iSensor/blob/master/iSensor/AudioViewController.m

# 位置情報系

## 5. 緯度・経度

GPSを使って、現在地の緯度・経度を取得します。

### 実装方法

現在地の取得を開始します。

```objc
if ([CLLocationManager locationServicesEnabled])
{
    // インスタンスを生成
    _locationManager = [[CLLocationManager alloc] init];

    // デリゲートを設定
    _locationManager.delegate = self;

    // 位置情報の取得開始
    [_locationManager startUpdatingLocation];
}
```

この状態で位置情報が更新されると、次のメソッドが呼ばれます。

```objc
// 位置情報が更新されるたびに呼ばれる
- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation
{
    self.latTextField.text = [NSString stringWithFormat:@"%.2f", newLocation.coordinate.latitude];
    self.lonTextField.text = [NSString stringWithFormat:@"%.2f", newLocation.coordinate.longitude];
}
```

引数 `newLocation` から現在地を取得することができます。

### 詳しい記事

* [iOSで緯度・経度を取得する方法メモ](http://blog.koogawa.com/entry/2013/11/23/190909)

### サンプルコード

* https://github.com/koogawa/iSensor/blob/master/iSensor/LocationViewController.m

## 6. 電子コンパス

磁力センサーを利用して、iPhoneの向きを計測することができます。

### 実装方法

取得を開始します。

```objc
    if ([CLLocationManager headingAvailable])
    {
        // インスタンスを生成
        _locationManager = [[CLLocationManager alloc] init];

        // デリゲートを設定
        _locationManager.delegate = self;

        // 何度動いたら更新するか（デフォルトは1度）
        _locationManager.headingFilter = kCLHeadingFilterNone;

        // デバイスの度の向きを北とするか（デフォルトは画面上部）
        _locationManager.headingOrientation = CLDeviceOrientationPortrait;

        // ヘディングイベントの開始
        [_locationManager startUpdatingHeading];
    }
```

この状態でiPhoneの向きが変わると、次のメソッドが呼ばれます。

```objc
// デバイスの方位が変わるたびに呼ばれる
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
    CLLocationDirection heading = newHeading.magneticHeading;
    self.textField.text = [NSString stringWithFormat:@"%.2f", heading];
}
```

`magneticHeading` には 0.0 から 359.9 の値が入ります。北の方向がちょうど 0.0 になり、それから東、南、西の順に値が増えていき、北に戻ると再び 0.0 に戻ります。

| 方角 | 値 |
|:-----------|------------:|
| 北 | 0.0 |
| 東 | 90.0 |
| 南 | 180.0 |
| 西 | 270.0 |

### 詳しい記事

* [【Tips】iOSで電子コンパスを使う](http://blog.koogawa.com/entry/2013/11/22/155025)

### サンプルコード

* https://github.com/koogawa/iSensor/blob/master/iSensor/HeadingViewController.m

## 7. 標高

GPSを使って、現在地の標高を測定できます。

### 実装方法

現在地情報の取得を開始します。

```objc
if ([CLLocationManager locationServicesEnabled])
{
    // インスタンスを生成
    _locationManager = [[CLLocationManager alloc] init];

    // デリゲートを設定
    _locationManager.delegate = self;

    // 位置情報の取得開始
    [_locationManager startUpdatingLocation];
}
```

この状態でiPhoneの位置情報が更新されると、次のメソッドが呼ばれます。

```objc
// 位置情報が更新されるたびに呼ばれる
- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation
{
    self.textField.text = [NSString stringWithFormat:@"%.2f m", newLocation.altitude];
}
```

引数 `newLocation` の `altitude` プロパティで標高が取得できます。単位はメートルです。

### 詳しい記事

* [【Tips】iOSで標高を測定する](http://blog.koogawa.com/entry/2013/11/23/112832)

### サンプルコード

* https://github.com/koogawa/iSensor/blob/master/iSensor/AltitudeViewController.m

# 移動・動作系

## 8. 加速度センサー

加速度センサーを利用することで、例えば iPhone の傾きによって画面上のボールが転がる、などと言った動きのあるアプリを作ることができます。

### 実装方法

インスタンスを生成します。

```objc
_motionManager = [[CMMotionManager alloc] init];
```
適切な更新間隔を設定をして加速度センサーを有効化します。

```objc
if (_motionManager.accelerometerAvailable)
{
    // センサーの更新間隔の指定
    _motionManager.accelerometerUpdateInterval = 1 / 10;  // 10Hz

    // ハンドラを指定
    CMAccelerometerHandler handler = ^(CMAccelerometerData *data, NSError *error)
    {
        // 画面に表示
        self.xLabel.text = [NSString stringWithFormat:@"x %f", data.acceleration.x];
        self.yLabel.text = [NSString stringWithFormat:@"y %f", data.acceleration.y];
        self.zLabel.text = [NSString stringWithFormat:@"z %f", data.acceleration.z];
    };

    // 加速度の取得開始
    [_motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:handler];
}
```

`startAccelerometerUpdatesToQueue`メソッドを実行すると、指定した更新間隔でハンドラの内容が実行されます。

### 詳しい記事

* [【Tips】iOSで加速度センサーを使用する](http://blog.koogawa.com/entry/2013/11/26/022710)

### サンプルコード

* https://github.com/koogawa/iSensor/blob/master/iSensor/AccelerometerViewController.m

## 9. 歩数・進行状況

CoreMotionを使って、歩数カウントを取得できます。（現時点ではiPhone 5Sのみ対応）

### 実装方法

`CMStepCounter ` インスタンスを生成します。

```objc
_stepCounter = [[CMStepCounter alloc] init];
```

歩数のカウントを開始します。

```objc
[_stepCounter startStepCountingUpdatesToQueue:[NSOperationQueue mainQueue]
                                     updateOn:1
                                  withHandler:^(NSInteger numberOfSteps, NSDate *timestamp, NSError *error) {
                                      // 歩数が更新されるたびに呼ばれる
                                  }];
```

* 第1引数：handler を呼び出す queue を指定
* 第2引数：何歩歩いたら handler を呼び出すか（実際にはこの指定通りには動かなかった）
* 第3引数：歩数更新時に呼び出す handler

第3引数で指定する Blocks には、計測を始めてからの累計歩数、更新時のタイムスタンプ、エラー情報が返ってきます。

また、歩数以外にもユーザの歩行情報（歩いているのか、走っているか等）も取得できます。詳しくは、下記の記事を参照してください。

### 詳しい記事

* [【Tips】iOSで歩数をカウントする](http://blog.koogawa.com/entry/2013/11/28/013437)

### サンプルコード

* https://github.com/koogawa/iSensor/blob/master/iSensor/MotionActivityViewController.m

# その他

## 10. 顔検出

Core Image を使って、顔を検出することができます。

### 実装方法

インスタンスを生成します。

```objc
NSDictionary *options = [NSDictionary dictionaryWithObject:CIDetectorAccuracyHigh
                                                    forKey:CIDetectorAccuracy];
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeFace
                                          context:nil
                                          options:options];
```

`detectorOfType` には検出タイプを指定します。今回は顔検出なので、`CIDetectorTypeFace` を指定します。((今のところ CIDetectorTypeFace しか設定できないようです))

`options` には検出精度 `CIDetectorAccuracy` 等を指定できます。

次に、顔検出を実行します。

```objc
CIImage *ciImage = [[CIImage alloc] initWithCGImage:originalImage.CGImage];
NSDictionary *imageOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:1]
    forKey:CIDetectorImageOrientation];

NSArray *array = [detector featuresInImage:ciImage options:imageOptions];
```

`featuresInImage` には、顔検出したい画像を `CIImage` クラスで指定します。

`options` には画像の向きなどを指定します。カメラの画像を利用する場合には、デバイスの方向を正しく指定しないと顔検出ができないので注意してください。

正常に顔が検出されると、検出結果が `CIFaceFeature` オブジェクトで返ってきます。このオブジェクトには、検出された顔の範囲や、目と口の位置などが含まれます。

* bounds - 顔の範囲
* leftEyePosition - 左目の位置
* mouthPosition - 口の位置
* rightEyePosition - 右目の位置

ここで注意したいのは、`CoreImage` は、<b>左下の座標が (0,0) となる</b>点です。よって、検出元画像の上に部品を載せたい場合は、座標をUIKitの座標系に変換する必要があります。詳しくは、下記の記事を参照してください。

### 詳しい記事

* [【Tips】iOSの顔検出機能を使ってみる](http://blog.koogawa.com/entry/2013/11/30/154347)

### サンプルコード

* https://github.com/koogawa/iSensor/blob/master/iSensor/FaceDetectionViewController.m

# さいごに

輝度センサーから顔検知までざっと書いてみましたが、いかがでしたでしょうか。すべての項目を詳しく書いていると、相当な量になってしまうため、各項目は簡単な説明に留めました。もっと詳しく知りたい場合はぜひ「詳しい記事」も参照してみてください。

なお、今回紹介した10項目のサンプルコードは、ひとつのアプリにまとめてGithubにアップしてあります。すぐにビルドして使えるようにしてあるので、ぜひダウンロードしてみてくださいね。

[https://github.com/koogawa/iSensor](https://github.com/koogawa/iSensor)

Swift 4 対応版もあるよ！ https://github.com/koogawa/iSensorSwift

この記事が皆様のお役に立てれば光栄ですm(_ _)m