Objective-C
iOS
ios7

iOS7の曇りガラス効果について知っている事まとめ

More than 3 years have passed since last update.


2014/10/07更に追記

iOS8でブラービューが標準機能として搭載されました。

別記事に投稿しましたのでそちらをご覧ください。

http://qiita.com/takabosoft/items/84e2039c19685d1c028b


2014/03/28追記

Twitterにて、@takatronix様より情報を頂きました。

このページの「自前BlurViewの作り方」でご紹介しているUIToolBarを使ったBlurViewを実装しますと、リジェクトになる可能性があります。

ご利用は自己責任でお願いいたします。

なお、Appleが公式で静止画にブラーを掛けるコードを提供していますので、動的ではなく静的なもので宜しければこちらを使っていただく方が確実です。

https://developer.apple.com/downloads/index.action

UIImageEffectsで検索すると出ます。


概要

先日、ドット絵ツールの「EDGE touch」というアプリをリリースいたしまして、そこそこの評価を頂いているようなのですが(ステマです)、その時にiOS7の曇りガラス(磨りガラス)効果について多少経験値が溜まりましたので、知っている範囲でまとめておきたいと思います。


  • Xcode 5.0.2 + SDK 7.0 + iPhone4S + iOS 7.0.4 (2014/02/07)

  • iOS6以前との互換性は一切考慮していません


標準の曇りガラスUIの挙動について


標準の曇りガラスUIの一般的な使い方

標準UIで曇りガラス効果が出る物は、UINavigationBarUIToolbarUITabBarぐらいでしょうか。

SDK7を使えば勝手に曇りガラスになるので便利ですね。

UINavigationControllerを使った例がこちら。


AppDelegate.mの一部

    UINavigationController *navi = [[UINavigationController alloc] initWithRootViewController:[ViewController new]];

self.window.rootViewController = navi;


ViewController.m

- (void)loadView

{
[super loadView];
self.view.backgroundColor = [UIColor whiteColor];

UIView *box = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
box.backgroundColor = [UIColor redColor];
[self.view addSubview:box];
}


実行結果:

Screenshot 2014.02.07 11.04.14.png

これだけでiOS7らしさが出るので楽しいですね。


曇りガラス効果が得られない事案

ところが、一部の機種や設定によっては、曇りガラス効果が得られない事が判っています。


  • 曇りガラス効果非対応の機種一覧情報が見つかりませんでした。ご存知の方はコメントをお願いします。

設定>一般>アクセシビリティ>コントラストを上げる...をオンに設定すると曇りガラス効果が無効になります。

Screenshot 2014.02.07 11.18.14.png

実行結果:

Screenshot 2014.02.07 11.19.48.png


曇りガラスの色を変えるには

さて、「コントラストを上げる」はいったんオフにしまして、これら標準UIの曇りガラスの色味を変えようと思ったら、何の疑いもせずにbarTintColorプロパティを設定すると思いますが、iOS7.0.3以降は曇りガラス効果が上手い事得られません。

参考:iOS 7.0.3からナビゲーションバーにtintColorを指定すると半透明じゃなくなった (yimajoさん)


AppDelegate.mの一部

navi.navigationBar.barTintColor = [UIColor blueColor];


実行結果:

Screenshot 2014.02.07 11.45.53.png

yimajoさんの記事にもありますが、アルファ値を下げると曇りガラス効果が何故か得られます。


AppDelegate.mの一部

navi.navigationBar.barTintColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:0.2];


実行結果:

Screenshot 2014.02.07 11.48.08.png

うまく曇るので、これで実装しちゃいがちなのですが、先にご紹介した「コントラストを上げる」をオンにすると...

実行結果:

Screenshot 2014.02.07 11.54.42.png

ご覧の通りスッカスカになってしまって駄目です。

(余談ですが、開発の途中まではこのようにアルファ値を設定していました。一緒に開発をしていたデザイナーさんの端末の設定がたまたま「コントラストを上げる」になっており、「スカスカだよ?」と報告されるまで全く気がつきませんでした...危うくそのままリリースする所でした(^_^;)

おそらく一般的な希望としてはデフォルトの動作と同じように、「コントラストを上げる」がオン(もしくは曇りガラス非対応機種)の状態ではバーは透過しないのが理想ではないでしょうか。

ではどうやってそれを実装するかですが、標準UIに色の薄いビュー(またはレイヤー)を被せるという手法を採用しました。

参考:http://stackoverflow.com/questions/18897485/achieving-bright-vivid-colors-for-an-ios-7-translucent-uinavigationbar


AppDelegate.mの一部(テストコードなのでいい加減です)

    UINavigationController *navi = [[UINavigationController alloc] initWithRootViewController:[ViewController new]];

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, -20, 320, 44 + 20)];
view.backgroundColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:0.3];
[navi.navigationBar insertSubview:view atIndex:1];

実行結果:曇りガラスオン

Screenshot 2014.02.07 13.17.08.png

実行結果:曇りガラスオフ

Screenshot 2014.02.07 13.17.19.png

狙った色&ぼやけ方にするのは難しいですが、iOS7のマイナーバージョンによる挙動の違いに左右されませんし、端末/設定による曇りガラス効果に適した見た目になりますので、落としどころとしては無難なのではないでしょうか。


標準曇りガラスでも重いものは重い

単純に上部にナビゲーションバー、下部にツールバーが出ているだけでも、コンテンツのフレームレートが著しく低下する現象を確認しています。iPhone5sなどではおそらく問題が無くても、iPhone4Sではカクカクになってしまう可能性があります。

見た目ではなく速度を重視する場合は素直に toolbar.translucent = NO; で曇りガラス効果を無効にしてしまいましょう。


標準曇りガラスのUIImageViewへの影響について

あんまりやらないとは思いますが、UIImageViewで画像を表示している時に、曇りガラス効果付きの標準UIを表示/非表示と切り替えると、UIImageViewで表示している画像が微妙に変化する現象を確認しています。

再現条件はよく判っていませんが、曇りガラスはある程度の大きさで、画像も補間が掛かった状態だと起きやすいようです。


ViewController.m

#import "ViewController.h"


@implementation ViewController
{
UIToolbar *_tb;
}

- (void)loadView
{
[super loadView];
self.view.backgroundColor = [UIColor whiteColor];

UIImage *img = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"z33" ofType:@"jpg"]];
UIImageView *v = [[UIImageView alloc] initWithFrame:self.view.bounds];
v.contentMode = UIViewContentModeScaleAspectFit;
v.image = img;
[self.view addSubview:v];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (_tb) {
[_tb removeFromSuperview];
_tb = nil;
}
else {
_tb = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 100)];
[self.view addSubview:_tb];
}
}

@end


タッチするとツールバーを表示したり消したりしてるだけのサンプルコードです。

画面の見た目は変わっているのですが、スクリーンショットには影響が出ないようでして、残念ながらここで比較画像を出す事ができませんでした。

ただ、曇りガラスが出ていると、補間が無効になるような感じになるので、だったら最初から補間処理を無効にしておけばいいじゃない!というわけで、UIImageViewを v.layer.magnificationFilter = kCAFilterNearest; という感じにセットしておけば、画像拡大時においては曇りガラスの表示/非表示に関わらず見た目が同じになる事を確認しています。

ところが画像縮小表示時に関しては、 minificationFilter = kCAFilterNearest; としても誤差が発生してしまいました。kCAFilterTrilinearが一番差が少なくて気がつかれにくいかもしれません。


カスタムUIで曇りガラス効果を得るには


公式でそういうビューがあるんじゃないの?

iOS7になったので、せっかくならば自前のUIでも曇りガラス効果を使いたくなるかと思いますが、標準UIと同じ曇りガラス効果が得られるビューというのもは公式では存在していません。Appleの公式サンプルでは、UIImageをぼかすというものを見かけた事がありますが、開発者が期待するのはそんな物ではありませんよね。

というわけで、オープンソースをまとめて下さっている方がいらっしゃいましたのでご紹介。

参考:オープンソースの曇りガラス風ビューをいろいろ試してみた(questbeatさん)

私も参考にさせてもらって、いくつか試してはみたのですが、組み込みやすく、使い勝手が良く、標準UIのような速度が出て、かつ端末やコントラスト設定によって曇ったり曇らなかったりするもの...というオープンソースが無さそうでしたので、最終的にはUIToolbarの派生クラスを作って自前でBlurViewを用意しました。

将来UIToolbarの挙動が変わった時には対処が必要ではありますが、先の条件を満たすにはこの方法が一番だと判断しました。


自前BlurViewの作り方

UIToolbarの派生クラスを作ります。

色の付け方は「曇りガラスの色を変えるには」でご紹介した方法と同様です。

セパレータが付いていますが、init内などでclipToBounds = YES;としてしまえば見えなくする事ができます。

と、まあ特に説明する事もないのですが、一点だけ変な挙動がありました。

自前BlurViewにラベル等をサブビューとして登録して[UIView animate...]でフェードイン/アウトを行うと、期待した結果にならない事がありました。こうした場合には、BlurViewとラベルを親子関係にするのではなく、兄弟関係にすると期待した結果に近づけられると思います。

(グループ的なフェードにはなりませんが。)

ちなみに自作アプリではこのBlurViewを使ってコントロールセンターもどきのメニューを実装しました。

menu.png


最後に

結構いい加減な性格なので、まずい箇所などがありましたらコメントをお願いいたします。