Intelligent Technology's Technical Blog

株式会社インテリジェントテクノロジーの技術情報ブログです。

iOS7 と iOS8 の、両方に対応したアプリを作る

こんにちは。中山です。

先日、iOS8 が正式に公開されました。
いろいろと新しい機能が追加されているようですが、しかし、多くの iOS 開発者がいちばん気にしているのは、

これまでつくったアプリは、そのまま動くのだろうか?!

というところだと思います。

今回は、iOS8 で変更があったところを中心に、iOS7、iOS8 の両方で動作するアプリを作るためのテクニックを、いくつかピックアップして紹介してみようと思います。

変更その1:UIScreen の bounds、applicationFrame の size が、端末の向きに依存するように

例えば以下のような、UIScreen の bounds、applicationFrame プロパティを用いてスクリーンのサイズを取得する処理があるとします。

NSLog(@"bounds: width = %f, height = %f", 
        [UIScreen mainScreen].bounds.size.width, 
        [UIScreen mainScreen].bounds.size.height);
NSLog(@"applicationFrame: width = %f, height = %f", 
        [UIScreen mainScreen].applicationFrame.size.width, 
        [UIScreen mainScreen].applicationFrame.size.height);

iOS7 のインストールされている iPad でこのコードを実行すると、以下のように表示されます。

iPad を縦向きに持っている場合:

bounds:           width = 768.000000, height = 1024.000000
applicationFrame: width = 768.000000, height = 1004.000000

iPad を横向きに持っている場合:

bounds:           width = 768.000000, height = 1024.000000
applicationFrame: width = 748.000000, height = 1024.000000

 
しかし、iOS8 をインストールした iPad で、同じコードを実行すると、以下のようになります。

iPad を縦向きに持っている場合:

bounds:           width = 768.000000, height = 1024.000000
applicationFrame: width = 768.000000, height = 1004.000000

iPad を横向きに持っている場合:

bounds:           width = 1024.000000, height = 768.000000
applicationFrame: width = 1024.000000, height = 748.000000

 
iOS7 の場合は、iPad を持つ向きを変えても、必ず、
短いほうが「width」、長いほうが「height」
となっていました。

しかし iOS8 の場合、
「width」「height」が iPad の向きに依存する
ようになりました。
つまり、横向きに持った場合は、
長いほうが「width」、短いほうが「height」
になります。(常に横方向が「width」、縦方向が「height」となるようです。)

もし、
「[UIScreen mainScreen].bounds.size の width は、必ず短いほうの値だ」
という前提で処理を行っている部分があるようでしたら、注意が必要です。
 

変更その2:画面回転の検知方法

iOS7 では、画面が回転すると、以下のような delegate メソッドが呼び出されていました。

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 
                                duration:(NSTimeInterval)duration {
    // 画面回転前
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    // 画面回転後
}

しかし iOS8 では、これらのメソッドはいずれも「非推奨」となり、代わりに以下の
viewWillTransitionToSize:withTransitionCoordinator:delegate メソッド
が呼び出されるようになっていました。

- (void)viewWillTransitionToSize:(CGSize)size 
       withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    if (size.width <= size.height) {
        // 画面回転後、縦向きになった
    } else {
        // 画面回転後、横向きになった
    }
}

iOS7、iOS8 の両方に対応させるためには、とりあえず、これらの delegate メソッドをすべて用意しておく必要があります。
 

変更その3:UIAlertView から UIAlertController へ

「UIAlertView」は、任意のメッセージをポップアップ表示するために用いられるクラスです。
iOS8 では、これの代わりに、新しく用意された「UIAlertController」クラスが利用できるようになりました。

iOS7、iOS8 の両方で、適切にメッセージのポップアップ表示を行うためには、例えば以下のようにコードを記述します。

if ([UIAlertController class]) {
    // iOS バージョンが 8 以上で、UIAlertController クラスが利用できる場合
    UIAlertController *alertController = 
        [UIAlertController alertControllerWithTitle:@"Title" 
                                            message:@"Message" 
                                     preferredStyle:UIAlertControllerStyleAlert];
    // Close ボタンを表示する
    UIAlertAction *alertAction = 
        [UIAlertAction actionWithTitle:@"Close" 
                                 style:UIAlertActionStyleCancel 
                               handler:nil];
    [alertController addAction:alertAction];

    [self presentViewController:alertController animated:YES completion:nil];
} else {
    // iOS バージョンが 8 未満で、UIAlertController クラスが利用できない場合
    UIAlertView *alertView = 
        [[UIAlertView alloc] initWithTitle:@"Title" 
                                   message:@"Message" 
                                  delegate:nil 
                         cancelButtonTitle:@"Close" 
                         otherButtonTitles:nil];
    [alertView show];
}

 

変更その4:位置情報測位許可の確認

iOS7 では、アプリ起動後に初めて位置情報測位の処理を実行しようとした場合、以下のような、位置情報測位の許可を求めるためのメッセージが自動的に表示されていました。
f:id:IntelligentTechnology:20140702173942p:plain:w240
 
しかし iOS8 では、このようにして位置情報測位の許可を求めるために、「CLLocationManager」クラスに新しく追加された、

  • requestAlwaysAuthorization
  • requestWhenInUseAuthorization

のいずれかのメソッドを、明示的に呼び出さないといけないようになりました。

さらに、これに加えて「Info.plist」ファイルにも、

  • NSLocationAlwaysUsageDescription
  • NSLocationWhenInUseUsageDescription

の定義を追加しておく必要があるようです。

これらを踏まえますと、iOS7、iOS8 の両方で動作する、位置情報測位の処理は、例えば以下のように表すことができます。

.h ファイル:

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>

@interface SampleViewController : UIViewController<CLLocationManagerDelegate>

@property (nonatomic, retain) CLLocationManager *locationManager;

@end

 
.m ファイル:

@implementation SampleViewController

@synthesize locationManager;

- (void)viewDidLoad {
    [super viewDidLoad];

    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;

    if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
        // iOS バージョンが 8 以上で、requestAlwaysAuthorization メソッドが
        // 利用できる場合

        // 位置情報測位の許可を求めるメッセージを表示する
        [self.locationManager requestAlwaysAuthorization];
//      [self.locationManager requestWhenInUseAuthorization];
    } else {
        // iOS バージョンが 8 未満で、requestAlwaysAuthorization メソッドが
        // 利用できない場合

        // 測位を開始する
        [self.locationManager startUpdatingLocation];
    }
}

- (void)locationManager:(CLLocationManager *)manager 
    didChangeAuthorizationStatus:(CLAuthorizationStatus)status {

    if (status == kCLAuthorizationStatusAuthorizedAlways ||
        status == kCLAuthorizationStatusAuthorizedWhenInUse) {
        // 位置情報測位の許可状態が「常に許可」または「使用中のみ」の場合、
        // 測位を開始する(iOS バージョンが 8 以上の場合のみ該当する)
        // ※iOS8 以上の場合、位置情報測位が許可されていない状態で
        //  startUpdatingLocation メソッドを呼び出しても、何も行われない。
        [self.locationManager startUpdatingLocation];
    }
}

- (void)locationManager:(CLLocationManager *)manager 
     didUpdateLocations:(NSArray *)locations {

    CLLocation *location = [locations lastObject];
    NSLog(@"%f %f", 
            location.coordinate.latitude,
            location.coordinate.longitude);

    [self.locationManager stopUpdatingLocation];
}

@end

 
Info.plist ファイル:

<key>NSLocationAlwaysUsageDescription</key>
<string>Always</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>When in use</string>

 

まとめ

このほかにも、まだまだ注意すべき点が出てくることでしょう。
またわかってきたところで、当ブログでも紹介できるとよいなと思っています。