jarinosuke blog

about software engineering, mostly about iOS

CADisplayLink について

ユースケース

CADisplayLink を実際に使う例と共にどんなクラスなのか簡単に紹介します。

例えば現在時刻を表示する場合。

画面に表示されている日時を定期的に更新する必要があります。

そのような場合に NSTimer で 0.01 秒など適当なインターバルを設定して更新、みたいなこと実装した経験ありませんか?

僕はあります。

それを解決するための表示されているビューを更新するためのイベントを取得するためのクラス、それが CADisplayLink です。

最近 facebookOSS 化した pop や、長年 iOS の 2D ゲームフレームワークとして親しまれている cocos2d でも、もちろん使われていました。

facebook/pop

cocos2d/cocos2d-iphone

CADisplayLink の使い方

CADisplayLink を以下の様にしてセットアップすることで、ディスプレイの更新タイミングをトリガーにしたイベント実行が可能になります。

///1
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];


///2
[link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

画面更新には CADisplayLink

前述したバッドケースとして NSTimer で更新、というのがありました。

これの何が悪いかというと、メインスレッドのロックなどが無ければ、画面の更新は大抵の場合 1/60 秒間隔で行われます。

なので、NSTimer でそれより細かい間隔で更新してもディスプレイには反映されません。

また画面の更新間隔はメインスレッドの具合によって変動するので、 CADisplayLink を用いて画面と関連づけて更新した方が良いというわけです。

ちなみに Building Paper を見て初めて知りましたが、大体メインスレッドで5ms以上処理がかかると、ドロップフレームが起こるらしいです。

facebookの”Building Paper”はすべてのiOSエンジニアがみるべき

CADisplayLink の考え方としてはゲーム開発などのフレームワークなどに良くある update 関数と近いというか同じだと思います。

画面上に描画する処理をユーザからの入力や、システム内のループで行っているものを CADisplayLink に置き換える事でアプリケーションをヌルヌル動かす事ができるようになるかもしれません。

アップル Apple Mini DisplayPort-Dual-Link DVIアダプタ MB571Z/A

アップル Apple Mini DisplayPort-Dual-Link DVIアダプタ MB571Z/A

UIViewTintAdjustmentMode について

tintColor

iOS 4 以前は tintColor プロパティは UIToolBar など、限られたクラスにのみ提供されていました。

UIToolBar - tintColor | iOS Developer Library

iOS 5 になると UIAppearance が提供され、他にもたくさんの UIView のサブクラスが tintColor プロパティを持つようになりました。

iOS 7 では遂に UIView 自身が tintColor プロパティを持つようになり、アプリケーション全体への tintColor の反映が可能なりました。

どういうことかというと、値を明示的に指定しない限り tintColor プロパティは親ビューの値を受け継ぐので、

たとえば keyWindow の tintColor プロパティを設定するだけでアプリ全体に行き渡る、ということです。

UIViewTintAdjustmentMode

iOS 7 からは tintColor 同様、UIView に tintAdjustmentMode というプロパティが追加されました。

このプロパティは上記の tintColor の継承関係をどのように反映させるかを決定するものです。

subviews の中に tintColor プロパティがデフォルトのものしか無ければ値は UIViewTintAdjustmentModeNormal になります。

UIViewTintAdjustmentModeDimmed を明示的に tintAdjustmentMode に設定すると、

その UIView の tintColor からは dimmed 加工が施された UIColor が return されるようになります。

ユースケース

f:id:jarinosuke0808:20140406193757p:plain

そもそもこのプロパティを見つけるに至ったきっかけになるのですが、

UIAlertView のようなモーダルで覆うカスタムビューを作る場合などに使用します。

以下のコードが非常に勉強になりました。

TeehanLax/TLAlertView

ちなみに UIAlertView を show したときに後ろ側に回る keyWindow を見るとわかりますが、

UIAlertView はしっかり keyWindow の tintAdjustmentMode を dimmed に設定しています。

なので UIKit の作法に従うためにも、細かいですがしっかりと実装していきたいところですね。

参考

UIView - tintAdjustmentMode | iOS Developer Library

[iOS 7] UIViewにtintColorが追加されました!| Developer.IO

Adopting iOS 7 APIs

TeehanLax/TLAlertView

UIAppearance | NSHipster

Asset Catalog の AppIcon を使って複数ターゲットで出し分ける

Asset Catalog 以前

Asset Catalog が出るまでは、アプリのアイコンをターゲット毎に出し分ける際は

以前に僕が書いた

アプリの情報をアイコンにオーバーレイさせる方法

と同じ要領で、ビルド後のスクリプトを用いてターゲット毎に用意された画像を適宜配置する必要がありました。

Asset Catalog 以後

Xcode 5 から Asset Catalog という画像管理に便利な機能が追加されました。

Xcode 5 で新しくプロジェクトを作成すると、Images.xcassets というフォルダがデフォルトで作成されます。

内部には AppIcon と LaunchImage という Image Set があり、これらに画像を適用するといった具合です。

画像の slicing を直感的に指定できたり、AppIcon も iOS/iPad 毎、iOS のバージョン毎に指定できたりと便利です。

Asset Catalog を用いたアイコン出し分け

では上記の AppIcon や Launch Image を出し分けるかという肝心な所ですが、

Build Settings を覗くと、 以下の項目がある事に気付くかと思います。

Asset Catalog Compiler - Options

その中に、

Asset Catalog App Icon Set Name
Asset Catalog Launch Image Set Name

という設定項目があり、デフォルトでは AppIcon と LaunchImage が設定されています。

これを ReleaseAppIcon などの自身で設定した Image Set 名に設定する事で、

ターゲット毎に Image Set を変更する事が可能になります。

ちなみに xcconfig などで書くのであれば、上記2項目のディレクティブは以下です。

ASSETCATALOG_COMPILER_APPICON_NAME
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME

Asset Catalog のとても残念な所

ここまで Asset Catalog の良い所だけ書いてきましたが、実のところ残念な所も多いです。

たとえば上記で行ったターゲット毎の Image Set の出し分けを、

アプリ内で利用するリソースに対しても行うのは非常に困難で、

結局ビルド後のスクリプトで Images.xcassets 配下の画像リソースを上書きする手間が出てきます。

詳細は下書き程度に書いた僕のブログを読んでください。

Asset Catalogs with Multiple Targets | jarinosuke stash

iOS 7.1 から iAd 経由でのアプリインストールのコンバージョン計測が可能に

コンバージョン計測

iOS 7.1 から以下の API が iAd.framework に追加されています。

iOS 7.1 API Diffs

ADClient

- (void)determineAppInstallationAttributionWithCompletionHandler:(void (^)(BOOL appInstallationWasAttributedToiAd))completionHandler NS_AVAILABLE_IOS(7_1);

この completionHandler を登録しておくと、 iAd 経由でユーザがアプリをインストールした場合に発火するといった具合です。

また設定において、Limit Ad Tracking を行い、広告トラッキングをオプトアウトしている場合には appInstallationWasAttributedToiAd が NO で発火するようです。

iAd の発展

iOS 6 から advertisingIdentifier が追加され、UDID や MAC Address の取得が禁止になり、 iOS 7 ではiAd の導入が劇的に簡単になりました。

以下のような噂(1年前の記事ですが)も立っているようなので、今後も Apple の広告への取り組みは注目されそうです。

Appleがクッキーを利用しているアプリを拒絶へ: Ad Identifierへの統一がねらい

参考

AdとID周辺の動き

iAd 実装が iOS7 から恐ろしく簡単になってる件

iAd on iOS 7

iOS 6 までは iAd を表示しようとすると、ADBannerView をインスタンス化して layout してなど色々と面倒でした。

iOS 7 では iAd.framework に以下のカテゴリが追加されています。

UIViewController (iAdAdditions)

これを用いる事で、今までのような煩わしいことをほとんど気にせずに iAd を表示する事が出来る様になります。

ユースケース毎に見ていきましょう。

バナー広告を表示したい

以下のプロパティをセットするだけ。

///(1)フレームワークをimport
@import iAd;

///(2)初期化時などに
self.canDisplayBannerAds = YES;

これだけでバナーが画面下に表示されます。

Interstitial 広告を表示したい

事前準備として以下を実行すると Interstitial 広告の情報をプリフェッチしておいてくれます。

//起動時などに
[UIViewController prepareInterstitialAds];

自動的に出すだけならカテゴリで追加された UIViewController の以下のセットするだけです。

@import iAd;

self.interstitialPresentationPolicy = ADInterstitialPresentationPolicyAutomatic;

自動だとさすがに煩わしい、意図したタイミングで、という場合は以下の様に

@import iAd;

self.interstitialPresentationPolicy =  ADInterstitialPresentationPolicyManual;

//意図したタイミングで
[self requestInterstitialAdPresentation];

iAd という選択肢

「iAd だけじゃやってけない」、「SSP 使って出し分けしたいし」、「広告興味ありません」などの意見には、

上記だけでは充分なメリットはないですが、

「広告導入に手間はかけずに、プロダクトに注力したい」「けど最低限の運営費は欲しい」みたいな開発者には一つの手段には成り得ると思います。

ということで知らない間に驚くほど簡単になってた iAd の紹介でした。

iAd Production Beginner’s Guide

iAd Production Beginner’s Guide