jarinosuke blog

about software engineering, mostly about iOS

UIView画像加工レシピ集

リッチなインターフェースにするために。

f:id:jarinosuke0808:20110730183107p:image:w360:right
アプリを作る以上、ダウンロードしてもらったユーザには一回でも多くタップしてもらいたいですよね。

タップワーシィな UI を作るためには、現実のメタファーを反映させたコントロールを作る必要があります。
もちろん iOS SDK に付属している UIKit を用いるだけで十分な場合も多いのですが、今までにない独創的なアプリを作るためには、自分でイチから UI を作り込まなければいけない場合もあります。

しかし、 イチから作るとなると CoreAnimation や Quartz など今まであまり触ったことのないレイヤまで手を加えないといけないような気がして、腰が引けてしまったりすると思います。(自分がそうだっただけですが…)

そこで「CoreAnimation とか良く分かってないけど、ちょっとリッチな UI にしてみたい」っていう人のために、簡単な画像加工のレシピを作ってみました。
基本的にコピペで行けるようにしたつもりなので、試して下さい。
また github にViewRecipeというサンプルアプリを置いておいたので、参考にして下さい。

概要説明。

とはいいつつも、概要程度でも理解していた方が分かりやすいと思うので箇条書きで。

Quartz は Mac OS X の描画コアエンジン。
Objective-C から Quartz を操作する Framework が QuartzCore.framework 。
QuartzCore には、 CoreAnimation, CoreImage, CoreVideo の3つのコンポーネントが含まれている。
CoreAnimation から、大きく分けて4つのクラスが提供されており、その中にアニメーションクラス( CAAnimation ) とレイヤークラス( CALayer )が含まれている。
CoreAnimation は CALayer を扱ってアニメーション処理を行っている。
CALayer は UIView のプロパティに入っており、CALayer も UIView と同じような階層関係を持つことが出来る。
UIView のアニメーション関数は CoreAnimation のラッパーであり、 CoreAnimation を直接扱うのに比べてできないことがある。

こんな感じでしょうか。詳しくは以下のドキュメントを読んでみて下さい。
CoreAnimationプログラミングガイド
CoreAnimation_Introduction

まずは View のセットアップ。

f:id:jarinosuke0808:20110730183543p:image:medium:right

     //setup recipeView
    recipeViewImage = [UIImage imageNamed:@"jarinosuke.tron.png"];
    recipeSubLayer = [CALayer layer];
    recipeSubLayer.frame = CGRectMake(0, 0, recipeView.frame.size.width, recipeView.frame.size.height);
    recipeSubLayer.contents = (id)recipeViewImage.CGImage;
    recipeSubLayer.masksToBounds = YES;
    [recipeView.layer addSublayer:recipeSubLayer];

この後の角丸だったり影を付けるために、あえてもう一枚レイヤーを作成し、その contents に画像を置いています。
これを行わずに UIView の レイヤーに直接画像を代入すると、後で角丸加工を加えた後に影を付けようとすると、陰までマスクされてしまい上手くいかないので、このようにしています。

画像を角丸にする。

f:id:jarinosuke0808:20110730183543p:image:medium:right

- (void)makeCornerToRound {
    recipeView.layer.cornerRadius = 10;
    recipeSubLayer.cornerRadius = 10;
}

iOS 開発では画像の四隅を丸くしたい、と思うことは多々あると思います。
そんなときは CALayer のプロパティである cornerRadius に角丸の半径を与えてあげるだけで可能になります。

影を付ける。

f:id:jarinosuke0808:20110730183109p:image:medium:right

- (void)addShadow {
    recipeView.layer.shadowOpacity = 0.4;
    recipeView.layer.shadowOffset = CGSizeMake(15.0, 15.0);
}

影を付けるのなら、 CALayer プロパティの shadowOffset に値を代入します。デフォルトでは(0, 0)なので、少しずらしてあげることで、影が付くようになります。

押し込みを付ける。

- (void)pushView {
    recipeView.layer.frame = CGRectMake(recipeView.layer.frame.origin.x + 5, recipeView.layer.frame.origin.y + 5, recipeView.frame.size.width, recipeView.frame.size.height);
    recipeView.layer.shadowOffset = CGSizeMake(5.0, 5.0);
}

UIButton では touchEvent にともない画像にエフェクトがかかり押したかどうかのフィードバックを行っています。
しかし、 UIView で単純に toucheEvent を実装しただけでは、 UIView は何のフィードバックも行いません。
ここでは一例として、画像をタップした際に画像が画面の奥にへこむような実装を行います。
内容自体は単純でレイヤーと影の位置を少しずらしているだけです。

画像を回転させる。

- (void)rotate {
    recipeView.transform = CGAffineTransformMakeRotation(0);
    [UIView animateWithDuration:0.5 animations:^{
        recipeView.transform = CGAffineTransformMakeRotation(2 * M_PI * 180.0 / 360.0 -0.000001);
        //-0.000001 is magic number to rotate right.
    }
                     completion:^(BOOL finised){
                        [UIView animateWithDuration:0.5 delay:0.5 options:nil animations:^{
                             recipeView.transform = CGAffineTransformMakeRotation(0);
                        } completion:^(BOOL finished){
                           
                        }];
                     }];
}

画像の回転のアニメーションも充分にユーザへのフィードバックとなり得ます。
たとえば、 Twitter for iPhone ではテーブルビューの更新のために下へフリックすると、更新テーブルセルが出てきてそこにのっている矢印アイコン下から上へ、クルっと回転します。そうすることでユーザへ、今アップデートを行っていることを視覚的に伝えています。
この例では、 UIView のアニメーションを行っています。
UIView のプロパティである transform に、アフィン変換のキー値を入れることで回転の他にも様々なアニメーションが可能になります。

レシピ

一貫性を意識しながら現実のメタファーに沿って、アニメーションや画像加工を行うことで確実にユーザ体験は向上すると思っています。
他にも画像加工に関してはたくさんの実装方法や、アイディアがあると思いますので今後も追っていきたいと思います。