2015年06月16日

Apple Watch対応のアプリを作ってみました

iOS担当の田中です。
Apple Watchが発売され結構な日が経ちましたが、開発するにあたりどんな感じか確かめるために簡単なアプリを開発してみました。

機能


・地図の表示
・コメント入力
・情報保存

Apple Watch側はこの3機能のみです。
iPhone側は保存データをリストに表示したり編集したりと実装してみましたが、今回はApple Watch側の部分にのみ触れていきたいと思います。

動作


地図表示


アプリ起動時は現在地を表示しています。

map1.png

地図を表示するまでのコードはこんな感じになっています。
#import <CoreLocation/CoreLocation.h>

@interface InterfaceController()
<
  CLLocationManagerDelegate
>

@property (weak, nonatomic) IBOutlet WKInterfaceMap *map;
@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic) CLLocationManager *locationManager;

@end

- (void)awakeWithContext:(id)context {
    [super awakeWithContext:context];
    
    self.locationManager = [CLLocationManager new];
    self.locationManager.delegate = self;

    [self.locationManager startUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation *nowLocation = locations.lastObject;
    double lat = nowLocation.coordinate.latitude;
    double lng = nowLocation.coordinate.longitude;
    
    MKCoordinateSpan currentSpan = MKCoordinateSpanMake(0.01f, 0.01f);
    CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(lat, lng);
    MKCoordinateRegion region = MKCoordinateRegionMake(coordinate, currentSpan);
    
    self.coordinate = coordinate;
    
    MKMapPoint newCenterPoint = MKMapPointForCoordinate(coordinate);
    [self.map setVisibleMapRect:MKMapRectMake(newCenterPoint.x, newCenterPoint.y, currentSpan.latitudeDelta, currentSpan.longitudeDelta)];
    [self.map setRegion:region];

    [self.map addAnnotation:coordinate
               withPinColor:WKInterfaceMapPinColorPurple];
}

iPhone側で位置情報取得の許可を取っていない場合は位置情報が取得できないため、先に確認処理を行う必要があります。

文字入力


位置情報の保存に合わせてコメントを残せるようにしました。
Apple Watchではキーボードが使えないため、自前で作成するか絵文字か音声で文字を入力することになります。

map2.png map3.png

絵文字や音声入力のviewの表示は以下のコードで使用することができます。

[self presentTextInputControllerWithSuggestions:nil
                               allowedInputMode:WKTextInputModeAllowAnimatedEmoji
                                     completion:
 ^(NSArray *results) {
    if (results.count) {
        id result = results.firstObject;
    }
 }];
 

入力後はresultsの中に入力結果が入っていますので取得後適切な形で処理をします。

保存


最後に取得したデータをiPhone側に保存します。

[WKInterfaceController openParentApplication:dict
                                       reply:
 ^(NSDictionary *replyInfo, NSError *error) {
 }];

このメソッドを使用することによりiPhone側にデータが送られます。
dictの部分に保存したいデータをDictionaryにしています。

あとはiPhone側のAppDelegateに以下のメソッドを追加しデータを取得し保存処理を行います。

- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *replyInfo))reply
{
}

以上の流れになります。

今の仕様だと大分iPhone側の処理に依存してしまい制限が大きいですが、Watch OS 2.0からネイティブの開発が可能になるはずなので自由度は大きく広がりそうです。
posted by Seesaa京都スタッフ at 18:02| Comment(0) | iOS | このブログの読者になる | 更新情報をチェックする

2014年08月13日

[iOS] Swiftでログをとりたい

こんにちは。先日の健康診断で昨年に比べ 6kg 太っていた、 iOS 担当の加島です。身長は相変わらずでした。野菜中心の生活に移行したいと思います。

さて今回は、 Swift でログをカスタマイズしてみたいと思います。Objective-C では基本的に NSLog を使っていましたね。Swift でも使えます。

let abc = "def"
NSLog("%@", abc)
// => 2014-08-13 19:00:00.000 MySwiftApp[11379:260330] def

ただ、日時やスレッド番号は必要なく、関数名や行番号などが欲しい場合もあるのではないでしょうか。このような場合は、 println が使えます。さらに、Swiftでは文字列内に変数・定数を "\(...)" で展開できるため、以下のように書けます。

println("[\(__FUNCTION__) : \( __LINE__)] \(abc)")
// => [viewDidLoad() : 17] def

さて、ここまで直接 NSLog や println を呼んでログをとってきましたが、毎回 __FUNCTION__ などを呼ぶのは面倒ですね。Objective-C では #define でマクロを作ることができましたが、 Swift ではできません。そこで、ログ用の関数を自作して表示させます。

func LOG(    body: AnyObject!,
         function: String = __FUNCTION__,
             line: Int = __LINE__)
{
    println("[\(function) : \(line)] \(body)")
}

LOG(abc)
// => [viewDidLoad() : 17] def

引数に __FUNCTION__ などを代入することによって、表示させることができました。

さらに、 Swift はヘッダファイルをもたず、各ファイル間で変数や関数を参照することができるため、あるファイルのクラス外に書いておけばプロジェクト内のどこからでもこの関数を使うことができます。マクロ用のファイルを別で用意してもいいかもしれません。

以上です。

----- 追記 (2014/08/21) -----
以上の例だと、 LOG() というように引数がない場合にエラーが出るようです。そこで、他の引数と同じようにデフォルト引数を入れることで回避します。

func LOG(  _ body: AnyObject! = "",
         function: String = __FUNCTION__,
             line: Int = __LINE__)
{
    println("[\(function) : \(line)] \(body)")
}

LOG()
// => [viewDidLoad() : 17] 

以上の方法で、引数がない場合もLOG関数を用いることができました。ちなみに、デフォルト引数を設定すると呼び出し時にラベルが必須になる(Xcode 6 beta 6)ので、アンダースコアで調整しています。前もこうだったかな・・・?
posted by Seesaa京都スタッフ at 19:00| Comment(0) | iOS | このブログの読者になる | 更新情報をチェックする

2014年05月17日

[iOS] 今更聞けないRetinaディスプレイの話

エモトです.Microsoftが新しいSurfaceを発表すると言われる日が給料日近くで,とてもそわそわしています.Microsoftが出すハードウェアはやっぱり良いですよね.

最近のニュースで,iPhone6で解像度がさらに上がるかもしれないという話が出てましたね.Retinaディスプレイは2010年に登場しましたが,改めてRetinaディスプレイの話と,3倍Retina(仮)が来たときに開発現場で何が起こるのかを話しましょう.

Retinaってなに?


iPhone4からRetinaディスプレイが採用され,画面解像度が480x320ピクセルから960x640ピクセルの2倍になりました(iPhone5で画面サイズは縦長に変わりましたが,考え方は同じなので省略).これは画面サイズを大きくするためではなく,従来の1ピクセルを2x2ピクセルで表示することで,画面をより精彩に表示するのが目的です.

解像度が異なる端末間でアプリ内の座標系に互換性を持たせる(Retina端末でも非Retina端末でも同じ配置で表示させる)必要があるため,アップルはRetinaディスプレイ採用後に,座標系にポイントという新しい概念を導入しました.
非Retinaの座標系において,1ポイント = 1ピクセル
Retinaの座標系において,1ポイント = 2ピクセル
これにより、Retina端末でも非Retina端末でも座標系は480x320ポイント(iPhone5なら568x320ポイント)になり,解像度が異なっても同じ座標を維持することが出来ます.

Retinaでデザインするときの注意点は?

ポイント座標系でボタンなどの配置を考えます.しかしながら,画像に関しては,精彩に表示するため,本来のピクセル座標系でのサイズも必要になります.この2つの座標系を行き来して作成するのは億劫なので,ポイント座標系ではなくピクセル座標系でデザインをしたいと考えますが,落とし穴があるので,注意が必要です.

例えば,ピクセル座標系で(x,y) = (1,1)を指定すると,実際のアプリ内でのポイント座標系では(x,y) = (0.5,0.5)となります.座標は整数値でしか指定できないので,四捨五入するかして,指定通りには配置はできません.乱暴ですが,ピクセル座標系で指定する場合は,偶数でしか指定できないと思ってください(今のところは).

Retinaが三倍になったら何が起こる?

3倍Retina端末において,1ポイント=3ピクセルになりますが,アプリはこれまで通り,ポイント座標系で作成していきます.

起こりうる問題として,ポイント座標系を意識せずにデザインを行った場合です.ピクセル座標系でデザイン指定を6ピクセル(2と3の最小公倍数)単位で行えば,問題なくポイント座標系へ変換できますが,それ以外では整数値にはならないため,ポイント座標系への変換が上手く行われません.折角のデザインが不格好になるだけです.

それではどうしたら・・・

ポイント座標系でデザインしましょう.画像で複数の座標系を扱うので億劫になりますが,iOS7から採用されたフラットデザインをうまく活用して,なるべく画像を使わずに作成・表現するなどして,対応するといいでしょう.

ちなみに,私は3倍Retinaを見越してるわけではないですが,簡単な形状であれば,画像は使わずにアプリ内で生成して,アプリ内での画像使用を控えるように心がけています.できる・できないもありますので,デザイナーの方は,開発者と相談するといいでしょう.
posted by Seesaa京都スタッフ at 12:00| Comment(0) | iOS | このブログの読者になる | 更新情報をチェックする