2013-05-02
評判のいいiOSアプリ開発のTips ベスト20
Tips, iOS, Objective-C, Xcode, ライブラリ
『iOSアプリ開発に役立つTips』という Facebook ページをやっておりまして、そこで評判が良かった投稿(※)を 20 個ほど紹介します。
アプリ実行中にコードを修正してそのまま実行中のアプリに反映させる
アプリ実行中にコードを修正してそのまま実行中のアプリに反映させることができるXcodeプラグイン。
実機でもシミュレータでも使用可能とのこと。実機での動作検証中に修正して再インストールは時間かかるので、超ありがたいかも。
「PCのwebブラウザからiPhoneのカメラロールにアクセスできるようにするアプリ」のしくみ
アプリ側でURLを発行している画面キャプチャがあるので、アプリにHTTPサーバ機能をもたせて(古いけどCocoaHttpServerとか)、アプリ側でカメラロールにアクセスしてその情報をweb経由で見せる、というしくみでしょうか。

新作!iPhoneのカメラロールをブラウザからアクセス出来るようにするアプリ「AirLib」を公開します | 関西/大阪のiPhone・iPadアプリ開発 feedtailor Inc. 社長ブログ
イラレで書いたパスデータを、UIBezierPathのコードに変換してくれるイラレ拡張
イラレで書いたパスデータを、UIBezierPathのコードに変換してくれるイラレの拡張。
UIBezierPath でコードだけでいい感じの図形とか画像を描くのは無理があるので、これは便利かも。
Drawscript - convert Illustrator shapes into code
「アプリを操作中の写真」を自動生成してくれるサービス
スクリーンショットの画像をアップすると、ハメコミ合成で「アプリを操作中の写真」を自動生成してくれるサイト。
ちょっと手元のスクリーンショットでやってみましたがなかなかいい感じの仕上がりでした。
![]()
PlaceIt by Breezi - Generate Product Screenshots in Realistic Environments
コマンドラインからJSONをごにょごにょする
特にiOSアプリ開発に限った話ではないのですが、jqという、「コマンドラインからJSONをごにょごにょできる」コマンドがかなり便利そうです。
たとえば、ルート直下の要素だけを取り出したい場合は、
$ jq ".[0]" hoge.json
"entities"要素内の"hashtags"要素内の"text"という要素を取り出したい場合は
$ jq ".[].entities.hashtags[].text" hoge.json
みたいな感じで簡単にとれて、はじめて使うAPIのレスポンスの構造を調べるときとかに活躍しそうです。
NSNumberFormatterで英語の勉強
NSNumberFormatterのいろいろなサンプル。3桁ごとにカンマ区切りにする「以外」のサンプルってあまり見つからないので参考になりました。
とくに、NSNumberFormatterSpellOutStyle がおもしろくて、たとえば、
[formatter setNumberStyle:NSNumberFormatterSpellOutStyle]; NSString *formattedOutput = [formatter stringFromNumber:@129];
こうやると、
one hundred twenty-nine
って出力してくれます。
これで英語の勉強アプリつくれそうです。
Formatting Numbers – NSNumberFormatter Examples
iPhoneの設定から通信速度を制限する
久しぶりに設定の「デベロッパ」のとこ見てみたら、この通信速度以外にも、iAdのテスト用設定とかも追加されてました。

iPhone5実機(iOS6.0.2)で通信速度を制限する #iOS #iPhone - Qiita
『Core Animation Programming Guide』大幅改定
『Core Animation Programming Guide』が大幅改定されたもよう。前回の更新から実に2年4ヶ月ぶり。『Animation Types and Timing Programming Guide』の内容もこっちに組み込んだとのこと。
Core Animation Programming Guide
CAEmitterLayer, CAEmitterCellを使ったパーティクルエフェクトの実装方法
iOS5からCore Animationに追加された、CAEmitterLayer, CAEmitterCellを使ったパーティクルエフェクトの実装方法について書きました。
UIKit上でパーティクルエフェクトを表示する - Over&Out その後
コマンドラインから.ipaをつくる
コマンドラインから.ipaをつくると"make adhoc"だけでできて楽ですよ、という話。
最初だけMakefileをつくるひと手間が必要ですが、curlコマンドも書いておけばビルド完了後に自動でtestflightにアップもできる、というのは便利そうと思いました。
.ipaをサッとつくる - blog.ishkawa.org
(派生記事)
バッテリーの超詳細情報を取得する
これを実機で実行すると、バッテリーの相当細かい情報まで取得できます。電圧までとれる。
ここまでとれるのか〜、とソースの中身みてみると、もろにプライベートAPI使ってました。。OSDBatteryっていうクラスがあるようです。
自然言語のテキストを属性で区分
NSLinguisticTaggerについて書きました。
自然言語のテキストを品詞(名詞、動詞、代名詞)や「個人名」「地名」といった属性で区分することができます。あと、日本語の形態素解析も可能です。

自然言語のテキストを属性で区分する - Over&Out その後
Auto Layoutの詳説スライド
70ページにわたるAuto Layoutの解説スライド(日本語)
Auto Layoutの基礎知識から、IBでの設定方法、ソースでの設定方法などなど。

画像を使用せずアイコンを描画
FacebookとかTwitterアイコンのUIButtonを描画してくれるOSS。
UIBezierPathでパス描画してUIImageを生成してるので画像ファイル不使用。与えたサイズに対して相対位置で描画するように実装してあるので大きさも自在。
MacのキーボードからiPhoneに文字入力
MacのキーボードからiPhoneとかiPadの文字入力できるようになります。今設定してみたばかりだけど超便利な予感。

MacをWinodwsのキーボードにできるアプリ、1Keyboardが理想的すぎて感動した
ファイルテンプレートの詳細な解説ページ
Xcode4の、ファイルテンプレートまわりの詳細な解説ページ。
TemplateInfo.plistの各要素の説明とか、"___FILEBASENAMEASIDENTIFIER___"とかのテンプレート関連マクロが何に展開されるか等、とにかく徹底解説な感じです。
このあたりはAppleの正式なドキュメントがないので、レア情報かと。

Creating Custom Xcode 4 File Templates
画面の解像度種別を返してくれるカテゴリ
iPhone5の画面サイズ対応についての質問と回答。回答2つめあたりの、UIDevice+Resolutionsっていうenumで定義した画面の解像度種別を返してくれるカテゴリはつくっとくと便利そう。

iOS 6 apps - how to deal with iPhone 5 screen size? - Stack Overflow
AppStoreのレビューをスクレイピングしてDBに保存
AppStoreのレビューをスクレイピングしてDBに保存したり表示したりするツール。
保存したレビューの一覧表示だけじゃなく、★の数を集計してグラフ表示したり、頻出キーワードを抽出したりもできるとのこと。
iOSアプリ開発の細かい仕事で小遣い稼ぎ
仕事をアウトソースできるwebサービス。Skill RequiredをiPhoneに絞ると結構iOSアプリ開発まわりの仕事もでてきます。
アプリまるっと作ってくれ系は安すぎるものが目立ちますが、リンク先はGPUImageの調整をしてほしいというもので、こういう細かい仕事は小遣い稼ぎにはいいかもしれません。(この案件は既にclose)

GPUImage adjustment | iPad | iPhone
ビューにスポットライトをあてる
スポットライトのように、中心をピークに、放射状に徐々に暗くするOSS。addSpotlightInView:atPoint: っていう感じでスポットライトをあてるビューと位置を指定するだけなので直観的。
ソースを見たところ、内部実装としては、CoreGraphics の CGGradientRef を使用して実装している。ちゃんとadd/removeのときにふわっとするアニメーションも実装してある。

MLPSpotlight for iOS - Cocoa Controls
おわりに
今回20個ほどピックアップしましたが、日々ちまちまと投稿しているので、Facebookページの方にはこの10倍ぐらいはTipsがたまってると思われます。
「いいね!」しておくとTipsがタイムラインに流れるようになるので、よろしければどうぞ!
『iOSアプリ開発に役立つTips』
http://www.facebook.com/iOSDevTips
2013-03-19
UILabelでのテキスト表示をリッチにする
UILabelはiOSアプリ開発において使用頻度の高いUIコンポーネントですが、カユいところに手が届かなかったりします。例えばテキストの一部だけ強調表示したい、色を変えたい、フォントを変えたいといったHTMLのような使い方ができません。
iOS6以上であれば、attributedText というNSAttributedStringオブジェクトをセットできるプロパティを標準で持っているので、そのままでテキストの装飾が可能なのですが、残念ながらiOS5以下では使えません。
OHAttributedLabelとTTTAttributedLabelは、そんなUILabelの機能を拡張するOSSです。
OHAttributedLabel
特長
NSAttributedString をラップして UILabel に持たせたもので、テキストの一部だけフォントや色を変えたり、リンクの付与といった指定が簡単にできるようになります。

また、 iOS6のテキスト装飾の手順と同じ(NSMutableAttributedString オブジェクトを作成して attributedText プロパティにセットする)で、シンプルでわかりやすいAPIも特長です。
使用準備
1. ソースコードをダウンロード
https://github.com/AliSoftware/OHAttributedLabel
2. クラスファイル一式をプロジェクトに追加
OHAttributedLabelフォルダ配下にあるクラスファイルをすべてプロジェクトに追加します。必要最小限のファイルのみ追加したい場合は
CoreTextUtils, NSTextCheckingResult+ExtendedURL, OHParagraphStyle, NSAttributedString+Attributes, OHAttributedLabelの5つを追加します。
3. CoreText.framework をプロジェクトに追加
4. ヘッダをインポート
#import "OHAttributedLabel.h"
基本的な使用方法
指定範囲の文字色を変更する方法を例にとり、OHAttributedLabelの基本的な使い方の流れを説明します。
1. NSMutableAttributedStringオブジェクトを生成
引数には表示する文字列を渡します。
NSString *txt = @"表示する文字列";
NSMutableAttributedString *attrStr = [NSMutableAttributedString attributedStringWithString:txt];
2. 文字色と範囲を指定
setTextColor:range: メソッドで、変更する文字色と、変更する範囲を指定します。
NSRange range = [txt rangeOfString:@"文字"]; UIColor *color = [UIColor colorWithRed:0.f green:0.f blue:0.f alpha:1.f]; [attrStr setTextColor:color range:range];
3. attributedTextプロパティにセット
OHAttributedLabel オブジェクトの attributedText プロパティに NSMutableAttributedString オブジェクトをセットして適用完了です。
self.ohLabel.attributedText = attrStr;
ちなみに、setTextColor:range: の代わりに range 引数のない setTextColor: メソッドを用いると、文字列全体に適用されます。
[attrStr setFont:[UIFont fontWithName:@"Helvetica" size:16]]; [attrStr setTextColor:[UIColor grayColor]];
その他の使用方法
太字指定は setTextBold:range: メソッドで YES をセットするか、
[attrStr setTextBold:YES range:range];
ボールドフォントの UIFont オブジェクトを setFont:range: メソッドに渡します。
UIFont *boldFont = [UIFont boldSystemFontOfSize:18];
[attrStr setFont:boldFont range:strongRange];
リンクを貼る場合は、setLink:range: メソッドにURLと範囲を指定します。
NSRange linkRange = [txt rangeOfString:linkStr];
NSURL *linkUrl = [NSURL URLWithString:@"http://www.facebook.com/iOSDevTips"];
[attrStr setLink:linkUrl range:linkRange];
OHAttributedLabel のソースコードに付属しているサンプルプロジェクトには、デリゲートメソッドを実装してアラートを出したり、タップ済みのリンクの文字色を変えたりといったサンプルも入っています。
centerVertically プロパティに YES をセットすると中央揃え、NO で上寄せになります。
self.ohLabel.centerVertically = YES;
UILabel の textAlignment に指定できる値として、UITextAlignmentJustify が追加されています。これは
#define UITextAlignmentJustify ((UITextAlignment)kCTJustifiedTextAlignment)
このように CoreText で定義されているアライメントタイプである kCTJustifiedTextAlignment を UITextAlignment 型にキャストしたもので、これを指定するとパラグラフの末尾が自然に揃えられます。
self.ohLabel.textAlignment = UITextAlignmentJustify;
TTTAttributedLabel
特長
OHAttributedLabel と同じく、NSAttributedString の機能を UILabel に持たせたものです。APIの手軽さでは OHAttributedLabel に軍配が上がりますが、機能の豊富さではこちらが勝ります。

使用準備
1. ソースコードをダウンロード
https://github.com/mattt/TTTAttributedLabel
2. CoreText.framework をプロジェクトに追加
3. TTTAttributedLabel.h をインポート
#import "TTTAttributedLabel.h"
使い方
OHAttributedLabel と使用方法の比較ができるよう、テキストの一部を赤字・太字にする方法、リンクを貼る方法を紹介します。
文字色、太字等の属性の指定は、setText:afterInheritingLabelAttributesAndConfiguringWithBlock: メソッドのブロック内で行います。
[self.tttLabel setText:txt afterInheritingLabelAttributesAndConfiguringWithBlock: ^NSMutableAttributedString *(NSMutableAttributedString *attrStr) { // ベースの設定 [attrStr setFont:baseFont]; [attrStr setTextColor:[UIColor grayColor]]; // 文字色 [attrStr setTextColor:strongColor range:strongRange]; // Bold [attrStr setFont:boldFont range:strongRange]; return attrStr; }];
リンク
リンクの指定は addLinkToURL:withRange: メソッドをコールします。
[self.tttLabel addLinkToURL:linkUrl withRange:linkRange];
また、TTTAttributedLabelでは、リンクをタップされたときの処理用の delegate メソッドを実装する必要があります。
そのため、プロトコルへの準拠を宣言しておき、
<TTTAttributedLabelDelegate>
TTTAttributedLabelのdelegateプロパティに移譲先をセットします。
self.tttLabel.delegate = self;
attributedLabel:didSelectLinkWithURL: メソッドを実装します。
- (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithURL:(NSURL *)url { [[UIApplication sharedApplication] openURL:self.linkUrl]; }
OHAttributedLabel はここはラップしてくれているのでひと手間増えることになりますが、openURL: ですぐに Safari や iTunes に飛ばすのではなく、一旦確認を挟みたい場合には便利です。(ただし、OHAttributedLabel もデリゲートメソッドの attributedLabel:shouldFollowLink: を実装すれば同等のことができます。)
サンプルコード
OHAttributedLabel と TTTAttributedLabel ともにサンプルプロジェクトがそれぞれのリポジトリに付属していますが、両者のAPIの違いを比較しやすいよう、両OSSを使用したサンプルアプリをGithubにアップしてあります。
[LabelDemo](https://github.com/shu223/LabelDemo)

参考
[UILabelでのテキスト表示をリッチにする機能拡張サブクラス3種](http://gihyo.jp/dev/serial/01/ios_oss/0004?ard=1363646129)
2012-12-17
Core Image の全エフェクトを試せるサンプルコードを公開しました
ライブラリ, iPhone, Objective-C, iOS6
Core Image のフィルタ(画像にエフェクトをかけたり、色を調整したりするもの)を一通り試せるサンプルプロジェクトをgithubに上げました。
というか、これ、1年以上前にアップしてこちらの記事に書いたのですが、ほとんど認知されることがなかったので、改めて紹介させていただきます。
こんな感じでフィルターを試せます。
(フィルタのパラメータはランダムに生成されるので、かける度に変わる場合もあります)
プロジェクト一式、こちらからダウンロードできます。
https://github.com/shu223/FilterDemo
注意点として、選んでも何も起こらないフィルターがたくさんあります。
詳細は後述しますが、こちら、Apple の PocketCoreImage というサンプルコードを数行いじっただけのものなので、あんまり多種多様なフィルタに対応したつくりになってはいないのです。。
そういうフィルタはリストから除いてもよかったのですが、「どんなフィルタがあるのか?」を一覧できる意味もあるので、残してあります。
PocketCoreImageからの改変点
CIFilterは、下記のように、NSString型の名前を引数に渡してインスタンスを生成します。
CIFilter *newFilter = [CIFilter filterWithName:name];
で、ここに渡せる名前をずらずらっと列挙するために、awakeFromNibで次のようなコードで全ビルトインフィルタの名前を取得しています。
NSArray *filterNames; filterNames = [CIFilter filterNamesInCategory:kCICategoryBuiltIn]; _availableFilters = [NSArray arrayWithArray:filterNames];
それと、PocketCoreImageでは、フィルタをかける箇所で、次のようにKVCを利用して元画像をフィルタに渡しています。
[filter setValue:_filteredImage forKey:@"inputImage"];
ここで、inputImageというアクセサを持たないフィルタもあるので、
if (![filter respondsToSelector:NSSelectorFromString(@"inputImage")]) { continue; }
とチェックを入れて例外を回避しています。
(詳細はこちらの記事をご参照ください)
その他、コードの解説
もともと PocketCoreImage にあった部分なのですが、多種多様なパラメータを持つ不特定多数のフィルタに対してランダム値を設定する部分はこうなっているよ、というのを紹介させていただきます。
まず、CIFilterのattributesメソッドでパラメータ一覧を取得します。
(このあとここでは設定しないパラメータを取り除く処理が入りますが割愛)
NSDictionary *filterAttributes = [filter attributes];
NSNumber型のパラメータだけを取り出し、
for (NSString *key in editableAttributes) { NSDictionary *attributeDictionary = [editableAttributes objectForKey:key]; if ([[attributeDictionary objectForKey:kCIAttributeClass] isEqualToString:@"NSNumber"]) { // 後述 } }
BOOL/Decimal/Integerそれぞれの型に応じて、ランダム値を設定しています。
(下記は、上記コードのif文の中身)
if ([attributeDictionary objectForKey:kCIAttributeType] == kCIAttributeTypeBoolean)
{
NSInteger randomValue = (rand() % 2);
NSLog(@"Setting %i for key %@ of type BOOL", randomValue, key);
[filter setValue:[NSNumber numberWithInteger:randomValue] forKey:key];
}
else if([attributeDictionary objectForKey:kCIAttributeType] == kCIAttributeTypeScalar ||
[attributeDictionary objectForKey:kCIAttributeType] == kCIAttributeTypeDistance ||
[attributeDictionary objectForKey:kCIAttributeType] == kCIAttributeTypeAngle)
{
// Get the min and max values
float maximumValue = [[attributeDictionary valueForKey:kCIAttributeSliderMax] floatValue];
float minimumValue = [[attributeDictionary valueForKey:kCIAttributeSliderMin] floatValue];
float randomValue = randFloat(minimumValue, maximumValue);
NSLog(@"Setting %f for key %@ of type Decimal", randomValue, key);
[filter setValue:[NSNumber numberWithFloat:randomValue] forKey:key];
}
else
{
// Get the min and max values
NSInteger maximumValue = [[attributeDictionary valueForKey:kCIAttributeMax] integerValue];
NSInteger minimumValue = [[attributeDictionary valueForKey:kCIAttributeMin] integerValue];
NSInteger randomValue = (rand() % (maximumValue - minimumValue)) + minimumValue;
NSLog(@"Setting %i for key %@ of type Integer", randomValue, key);
[filter setValue:[NSNumber numberWithInteger:randomValue] forKey:key];
}
Core Image の参考書籍
iOS Core Frameworksテクニカルガイド
2012年9月と比較的最近出版されたものですが、原著の出版は2011年9月。したがって iOS5 時代のものです。『Core Image』の章があるのですが、広く浅く、という感じです。
iOS5プログラミングブック
Core Imageの章あり。この本は全体的に説明が詳しく、あまり他の書籍やブログにないところまで踏み込んだ情報も多いので、個人的にはこちらの方がおすすめです。
おまけ:CIFilterのフィルタ名一覧(iOS 6 版)
前回記事(2011年11月)時点では 48 個だったものが、なんと 94 個に増えていました。
CIAdditionCompositing
CIAffineClamp
CIAffineTile
CIAffineTransform
CIBarsSwipeTransition
CIBlendWithMask
CIBloom
CIBumpDistortion
CIBumpDistortionLinear
CICheckerboardGenerator
CICircleSplashDistortion
CICircularScreen
CIColorBlendMode
CIColorBurnBlendMode
CIColorControls
CIColorCube
CIColorDodgeBlendMode
CIColorInvert
CIColorMap
CIColorMatrix
CIColorMonochrome
CIColorPosterize
CIConstantColorGenerator
CICopyMachineTransition
CICrop
CIDarkenBlendMode
CIDifferenceBlendMode
CIDisintegrateWithMaskTransition
CIDissolveTransition
CIDotScreen
CIEightfoldReflectedTile
CIExclusionBlendMode
CIExposureAdjust
CIFalseColor
CIFlashTransition
CIFourfoldReflectedTile
CIFourfoldRotatedTile
CIFourfoldTranslatedTile
CIGammaAdjust
CIGaussianBlur
CIGaussianGradient
CIGlideReflectedTile
CIGloom
CIHardLightBlendMode
CIHatchedScreen
CIHighlightShadowAdjust
CIHoleDistortion
CIHueAdjust
CIHueBlendMode
CILanczosScaleTransform
CILightenBlendMode
CILightTunnel
CILinearGradient
CILineScreen
CILuminosityBlendMode
CIMaskToAlpha
CIMaximumComponent
CIMaximumCompositing
CIMinimumComponent
CIMinimumCompositing
CIModTransition
CIMultiplyBlendMode
CIMultiplyCompositing
CIOverlayBlendMode
CIPinchDistortion
CIPixellate
CIRadialGradient
CIRandomGenerator
CISaturationBlendMode
CIScreenBlendMode
CISepiaTone
CISharpenLuminance
CISixfoldReflectedTile
CISixfoldRotatedTile
CISmoothLinearGradient
CISoftLightBlendMode
CISourceAtopCompositing
CISourceInCompositing
CISourceOutCompositing
CISourceOverCompositing
CIStarShineGenerator
CIStraightenFilter
CIStripesGenerator
CISwipeTransition
CITemperatureAndTint
CIToneCurve
CITriangleKaleidoscope
CITwelvefoldReflectedTile
CITwirlDistortion
CIUnsharpMask
CIVibrance
CIVignette
CIVortexDistortion
CIWhitePointAdjust
2012-12-07
Method Swizzling をうまく使っている実用例
Method Swizzlingは、既存のメソッドの実装を、自前の実装に差し替えるための手法です。
・・・ということを知ってはいても、どういうときに使うと便利なのかイマイチわかってなかったので、Method Swizzlingをうまく使った実用例を2つほど探してきました。
実用例その1:既存ソースコードに手を入れずに機能追加
xib ファイルのローカライズを IB 上でできるようにする AutoNibL10n
通常、xibで作成したUIをローカライズする場合、
- xibファイルを言語ごとに用意する
- アウトレットを定義してプログラム側からローカライズした文言をセットする
といった面倒な作業が必要でしたが、 AutoNibL10n を使用すると、xibファイルを IB から直接多言語対応できるようになります。
たとえば、RootViewController.xibというファイルがあり、その中のUILabelオブジェクトを多言語化したい場合、直接 IB から UILabel のtextプロパティに "mytext" を指定すれば、Localizable.strings に"mytext"というキーで定義された各言語の文字列が挿入されるようになります。
AutoNibL10n における Method Swizzling
この AutoNibL10n、使用するために新たにコードを書く必要は一切なく、ヘッダをインポートする必要すらありません。
どうやっているかというと、Method Swizzling を利用して、load メソッドがコールされるタイミングで(=メモリにアプリケーションがロードされるタイミングで) NSObject の awakeFromNib メソッドを自前の localizeNibObject メソッドに入れ替えることで実現しています。
+(void)load { Method localizeNibObject = class_getInstanceMethod([NSObject class], @selector(localizeNibObject)); Method awakeFromNib = class_getInstanceMethod([NSObject class], @selector(awakeFromNib)); method_exchangeImplementations(awakeFromNib, localizeNibObject); }
awakeFromNib の差し替え後のメソッド localizeNibObject の実装はこんな感じです。
-(void)localizeNibObject { LocalizeIfClass(UIBarButtonItem); else LocalizeIfClass(UIBarItem); else LocalizeIfClass(UIButton); else LocalizeIfClass(UILabel); else LocalizeIfClass(UINavigationItem); else LocalizeIfClass(UISearchBar); else LocalizeIfClass(UISegmentedControl); else LocalizeIfClass(UITextField); else LocalizeIfClass(UITextView); else LocalizeIfClass(UIViewController); if (self.isAccessibilityElement == YES) { self.accessibilityLabel = localizedString(self.accessibilityLabel); self.accessibilityHint = localizedString(self.accessibilityHint); } [self localizeNibObject]; // actually calls awakeFromNib as we did some method swizzling }
この中でコールされている localizedString メソッドで、NSBundle の localizedStringForKey:value:table: メソッドを用いて Localizable.strings に定義されたローカライズ文字列を取得しています。
return [[NSBundle mainBundle] localizedStringForKey:aString value:nil table:nil];
実用例その2:iOSバージョンの違いを吸収する
UIRefreshControl を iOS5 でも使えるようにする ISRefreshControl
ISRefreshControl は、iOS6で導入された UIRefreshControl を iOS5 でも使えるようにするライブラリです。
UIRefreshControl と同じ API なので、導入する側は UIRefreshControl と同じように実装するだけ。
self.refreshControl = (id)[[ISRefreshControl alloc] init]; [self.refreshControl addTarget:self action:@selector(refresh) forControlEvents:UIControlEventValueChanged];
これだけで、iOS6 で動作させると UIKit の UIRefreshControl が呼ばれ、iOS5 で動作させると ISRefreshControl (UIRefreshControl と同じ見た目、挙動)が呼ばれます。
ISRefreshControl における Method Swizzling
UITableViewController+RefreshControl というカテゴリが用意されていて、これの load メソッド内で、次のように Method Swizzling が行われています。
+ (void)load { @autoreleasepool { if ([[[UIDevice currentDevice] systemVersion] hasPrefix:@"5"]) { Swizzle([self class], @selector(refreshControl), @selector(iOS5_refreshControl)); Swizzle([self class], @selector(setRefreshControl:), @selector(iOS5_setRefreshControl:)); } } }
(※Swizzle()は同ファイルに定義されているMethod Swizzlingのラッパー関数)
iOSのバージョンが "5" だった場合に、UITableViewController の refreshControl というプロパティの setter と getter を差し替える実装になっていることがわかります。
iOS5のときに差し替える実装は、
- (ISRefreshControl *)iOS5_refreshControl { return objc_getAssociatedObject(self, @"iOS5RefreshControl"); } - (void)iOS5_setRefreshControl:(ISRefreshControl *)refreshControl { objc_setAssociatedObject(self, @"iOS5RefreshControl", refreshControl, OBJC_ASSOCIATION_RETAIN); }
となっていて、Associated Objectの仕組みを利用して iOS5 で UITableViewController の refreshControl というプロパティにアクセスできるようにしています。
で、KVOでrefreshControlプロパティを見張り、
[self addObserver:self forKeyPath:@"refreshControl" options:options context:NULL];
refreshControlプロパティに値(UIViewサブクラスのオブジェクト)がセットされたときに、所定の位置にそのビューを貼付ける、という実装になっています。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (object == self && [keyPath isEqualToString:@"refreshControl"]) { UIView *oldView = [change objectForKey:@"old"]; UIView *newView = [change objectForKey:@"new"]; if ([oldView isKindOfClass:[UIView class]]) { [oldView removeFromSuperview]; } if ([newView isKindOfClass:[UIView class]]) { newView.frame = CGRectMake(0, -50, self.view.frame.size.width, 50); newView.autoresizingMask = UIViewAutoresizingFlexibleWidth; [newView setNeedsLayout]; [self.view addSubview:newView]; } return; } // 後略 }
このように、Method Swizzling を用いることで、導入する側は iOS のバージョン判定も、このライブラリ独自のAPIを使う必要もなく、UIRefreshControlと同じように実装するだけ、というカンタン導入を実現しています。
Method Swizzlingの参考書籍
日本語の本だと、『iOS SDK Hacks』に解説と事例が掲載されています。
オライリージャパン
売り上げランキング: 226658
まとめ
Method Swizzlingをうまく使っている実用例を2つ紹介しました。
これ以外にも、単体テストで使える OCMock というライブラリも、Method Swizzling を利用して既存オブジェクトのメソッドを差し替えてテストできるようにしていたりと、Github上にはたくさんの実用例が転がっているようなので、また追記したいと思います。
2012-11-27
たった1行でWebサービスと連携!UIActivity のまとめリポジトリをつくりました
UIActivityとは?
地味だからかあまり話題になってない気がするのですが、iOS 6 の便利な新機能の1つに UIActivity というのがあります。これは、
こんな感じでメールとか写真アプリとかFacebook/Twitterとかに写真やテキストを渡すためのUIです。(超ざっくり)
実装も超簡単!
投稿も写真アルバムへの保存もこれ一つでOKかつ実装も超簡単なので、もうTwitter.frameworkとか、Social.frameworkとか、MessageUI.frameworkとかは使わなくなってしまいました。(※UIActivityViewControllerが内部的には使ってると思います)
UIActivity (UIActivityViewController) の実装コードはこんな感じです。
- (IBAction)pressBtn { NSString *text = @"投稿するテキスト"; NSURL *url = [NSURL URLWithString:@"http://投稿するURL.com"]; NSArray *activityItems = @[text, url]; UIActivityViewController *activityView = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:@[]]; [self presentViewController:activityView animated:YES completion:nil]; }
「Twitter投稿の実装が簡単になった!」と iOS5 時代に大歓迎された TWTweetComposeViewController とそんなに実装量が変わらないですよね?
そんなわけで、僕はここ最近はほぼ確実に UIActivity を入れています。(OSのバージョン判定して、iOS6以上ならUIActivity、iOS5.XならTwitter.frameworkという実装をスニペットから貼り付けるだけなのでだいたい5分ぐらい)
カスタムUIActivityで、簡単に機能追加!
そんな便利な UIActivity ですが、さらに便利なことに、カスタムUIActivityをつくって UIActivityViewController に追加することができるのです。
どういうことかというと、たとえば、ARChromeActivity というライブラリを使うと、UIActivityViewController に Chromeアプリで URL を開くためのアイコンが追加されます。
で、このカスタムUIActivityの追加がまたラクなのです。
たとえば ARChromeActivity を追加したい場合に、上述したコードに追加するコードは1行だけ。
ARChromeActivity *chromeActivity = [[ARChromeActivity alloc] init];
これを、UIActivityController を初期化する際の applicationActivities 引数に渡してやると、
UIActivityViewController *activityView = [[UIActivityViewController alloc] initWithActivityItems:activityItems
applicationActivities:@[chromeActivity]];
これだけの変更で Chrome アプリとの連携機能が実現するという優れものです。
カスタムUIActivityのまとめリポジトリ:UIActivityCollection
ずいぶん前置きが長くなってしまいましたが、そんなカスタムUIActivity を集めたリポジトリをつくりました。
https://github.com/shu223/UIActivityCollection
実装の参考用に、デモアプリも入っています。
ダウンロード方法
リポジトリはサブモジュールの形で入っているので、下記のように git で clone した後に submodule コマンドを実行してください。
$ git clone git://github.com/shu223/UIActivityCollection.git $ cd UIActivityCollection/ $ git submodule update --init --recursive
本家リポジトリからファイル一式がダウンロードされます。
対応サービス/アプリ一覧
11/27現在でリポジトリに入っているサービス/アプリはこちら。
- Buffer
- Dropbox
- Flattr
- Instapaper
- Pinboard
- Pinbook
- Readability
- SoundCloud
- Tumbletail
Githubで検索して集めただけなのですが、まだまだ意外と少ないなぁという印象です。
#この程度の数だと、カスタム UIActivity をオープンソースで配布することで、世界の誰かが見つけてくれてアプリに組み込んでくれたりしそうなので、カスタム UIActivity を配布すること自体がサービス/アプリのプロモーションに繋がるかも。
(2012/12/4追記)以下の2つの UIActivity を追加しました。
- Calendar
- Safari
(2012/12/6追記)以下の2つの UIActivity を追加しました。
- LINE
- KakaoTalk
(2012/12/12追記)以下の2つの UIActivity を追加しました。
- AppStore
- In-App(アプリ内でUIWebViewに表示)
また、デモアプリの対応UIActivityを増やしました。
(2012/12/19追記)以下の2つの UIActivity を追加しました。
- Google Maps
- Nakamap
まとめ
思いのほか長くなってしまいましたが、カスタム UIActivity のまとめリポジトリ『UIActivityCollection』のご紹介でした!
















