option + command + ← or → でカーソル位置の履歴を行き来することができる。
例えば下のファイルを見ているとする。
その後ファイルを切り替える。
ここで option + command + ← を押すと再び元の位置が表示される。
option + command + → を押すとまた2番目のファイル内へ移動する。
履歴はカーソルの移動毎に記録されるので同じファイル内での移動にも使える。
拡張子から MIME Type を取得する
2010年8月11日水曜日 | Published in Code Snipet, iOS 4.0, iPhone, サンプルあり | 0 コメント
MobileCoreServices.framework に 拡張子とMIME Type の相互変換用の関数が用意されている。
UTType Reference
変換はどちらの場合も一旦 UTIへの変換を経由する。
逆に UTIからの変換には UTTypeCopyPreferredTagWithClass() を使う。
MIME Type から 拡張子を取得する場合はこんなコードになる。
どちらの関数も引数に UTTagClass を取る。これは次の2つが定義されている。
こんな感じ。
UTType系関数をラップするユーティリティクラスを用意してみた。
中身はこんな感じ。
なお UTType系関数の利用には下記のフレームワークを追加する必要がある。
その上で MobileCoreServicesをインポートする。
GitHubからどうぞ。
UUTypeSample at 2010-08-10 from xcatsan's iOS-Sample-Code - GitHub
ソースコードはGitHubからどうぞ。
UTType Reference
変換方法
変換はどちらの場合も一旦 UTIへの変換を経由する。
MIME Type (例:image/png) ↓ UTI(例:public.png) ↓ 拡張子(例:png)UTI への変換には UTTypeCreatePreferredIdentifierForTag() を使う。
逆に UTIからの変換には UTTypeCopyPreferredTagWithClass() を使う。
MIME Type から 拡張子を取得する場合はこんなコードになる。
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag( kUTTagClassMIMEType, (CFStringRef)mimeTYpe, NULL); CFStringRef filenamExtension = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension); : CFRelease(uti); CFRelease(filenameExtension);
どちらの関数も引数に UTTagClass を取る。これは次の2つが定義されている。
const CFStringRef kUTTagClassFilenameExtension; const CFStringRef kUTTagClassMIMEType;また取得した文字列は CFReleaseで開放してやる必要がある。
サンプル
こんな感じ。
UTType系関数をラップするユーティリティクラスを用意してみた。
@interface UTTypeUtility : NSObject {
}
+ (NSString*)filenameExtensionFromMimeType:(NSString*)mimeType;
+ (NSString*)mimeTypeFromFilename:(NSString*)filename;
+ (NSString*)mimeTypeFromFilenameExtension:(NSString*)filenameExtension;
+ (NSString*)UTIfromMimeType:(NSString*)mimeType;
+ (NSString*)UTIfromFilename:(NSString*)filename;
+ (NSString*)UTIfromFilenameExtension:(NSString*)filenameExtension;
@end中身はこんな感じ。
+ (NSString*)filenameExtensionFromMimeType:(NSString*)mimeTYpe
{
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(
kUTTagClassMIMEType, (CFStringRef)mimeTYpe, NULL);
NSString* filenamExtension =
(NSString*)UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension);
CFRelease(uti);
return [filenamExtension autorelease];
}なお UTType系関数の利用には下記のフレームワークを追加する必要がある。
その上で MobileCoreServicesをインポートする。
#import <MobileCoreServices/MobileCoreServices.h>
ソースコード
GitHubからどうぞ。
UUTypeSample at 2010-08-10 from xcatsan's iOS-Sample-Code - GitHub
ソースコードはGitHubからどうぞ。
参考情報
- Able Pear Software: How to get the file extension for a mime type on iOS
- UTType関連の関数の使い方はここで知った。
共通の背景画像を使う
2010年8月10日火曜日 | Published in iOS 4.0, iPhone, UIKit, サンプルあり | 0 コメント
以前、UITableView の背景に画像を表示するブログを書いた。今回は1つの UITableViewだけでなく、全画面共通の背景を表示する方法について調べてみた。
通常 iPhoneアプリは1つの UIWindow を持つが、ここへ UIImageViewを配置し共通で使う画像を表示させる。こんなイメージ。
MainWindow.xib を開き、Windowに UIImageViewを配置し、あとは表示させたい画像を指定してやる。その上で他のビューの背景色を透明にするとこの画像が背景に表示されるようになる。
サンプルを作ってみた。UINavigationController を使い、2つの UITableViewController を行き来する。
初期画面はこう。
行を選択すると次のビューがスライドしてきて
次のビューが表示される。スライド中に背景が動いていないのがポイント(あたりまえだが)。
ソースコードは GitHub からどうぞ。
BackgroundImage at 2010-08-07 from xcatsan's iOS-Sample-Code - GitHub
UITableView の背景色(backgroundColor)を透明色にする必要があるが、これを InterfaceBuilder上で設定すると角の部分が黒くなってしまう。
拡大図。
これを防ぐにはコードで透明色の指定を行う。
するとこうなる。
※理由はわかりません。
Cocoaの日々: UITableView の背景に画像を表示する
UIWindow に UIImageView を貼り付ける
通常 iPhoneアプリは1つの UIWindow を持つが、ここへ UIImageViewを配置し共通で使う画像を表示させる。こんなイメージ。
MainWindow.xib を開き、Windowに UIImageViewを配置し、あとは表示させたい画像を指定してやる。その上で他のビューの背景色を透明にするとこの画像が背景に表示されるようになる。
サンプル
サンプルを作ってみた。UINavigationController を使い、2つの UITableViewController を行き来する。
初期画面はこう。
行を選択すると次のビューがスライドしてきて
次のビューが表示される。スライド中に背景が動いていないのがポイント(あたりまえだが)。
ソースコードは GitHub からどうぞ。
BackgroundImage at 2010-08-07 from xcatsan's iOS-Sample-Code - GitHub
UITableView でのポイント
UITableView の背景色(backgroundColor)を透明色にする必要があるが、これを InterfaceBuilder上で設定すると角の部分が黒くなってしまう。
拡大図。
これを防ぐにはコードで透明色の指定を行う。
- (void)viewDidLoad {
[super viewDidLoad];
:
:
self.tableView.backgroundColor = [UIColor clearColor];
}するとこうなる。
※理由はわかりません。
参考情報
Cocoaの日々: UITableView の背景に画像を表示する
UITableView - 編集モードで左側のアイコンを消す
2010年8月9日月曜日 | Published in iOS 4.0, iPhone, UIKit | 0 コメント
UITableView を編集モードにした時に左側に削除用のアイコンが表示される。
これを消すには UITableViewDelegate を使う。
アイコンを消す(UITableViewCellEditingStyleNone)と共に、インデントしない設定を行っている。
するとこうなる。わかりずらいが編集モードに入ってもアイコンが非表示でインデントも起こらない。
- - - -
UITableView の editing プロパティを変更すると自動的にその上の UITableViewCell にも反映されるので、編集用のユーザインターフェイスを自前で作る時にも editing が使えると便利。その場合に既存の動作を隠す方法が今回の内容。
これを消すには UITableViewDelegate を使う。
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView
editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleNone;
}
- (BOOL)tableView:(UITableView *)tableView
shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}アイコンを消す(UITableViewCellEditingStyleNone)と共に、インデントしない設定を行っている。
するとこうなる。わかりずらいが編集モードに入ってもアイコンが非表示でインデントも起こらない。
- - - -
UITableView の editing プロパティを変更すると自動的にその上の UITableViewCell にも反映されるので、編集用のユーザインターフェイスを自前で作る時にも editing が使えると便利。その場合に既存の動作を隠す方法が今回の内容。
CoreData - awakeFromSnapshotEvents:
2010年8月8日日曜日 | Published in Core Data, iOS 4.0, iPhone | 0 コメント
NSManagedObjectContextに対して rollback を送ると、対象となる NSManagedObject の awakeFromSnapshotEvents: が呼び出される。
NSManagedObject Class Reference - awakeFromSnapshotEvents:
引数には実行された操作の種類が渡される。
このメソッドは操作が行われた後に呼び出される。
- - - -
rollback直前のイベントを取るにはどうしたらいいのか。
NSManagedObject Class Reference - awakeFromSnapshotEvents:
引数には実行された操作の種類が渡される。
enum {
NSSnapshotEventUndoInsertion = 1 << 1,
NSSnapshotEventUndoDeletion = 1 << 2,
NSSnapshotEventUndoUpdate = 1 << 3,
NSSnapshotEventRollback = 1 << 4,
NSSnapshotEventRefresh = 1 << 5,
NSSnapshotEventMergePolicy = 1 << 6
};
typedef NSUInteger NSSnapshotEventType;
このメソッドは操作が行われた後に呼び出される。
- - - -
rollback直前のイベントを取るにはどうしたらいいのか。
NSTimer を Blocks で処理する [2]改良版
2010年8月7日土曜日 | Published in Blocks, iOS 4.0, iPhone | 0 コメント
[前回] Cocoaの日々: NSTimer を Blocks で処理する
前回のコードを少し改良してみる。
前回は userInfo を持回す為に NSMutableDictionary を導入したり、Blocksの引数に userInfo を付けたりしていたが、考えてみると Blocksは定義されているそのレキシカルスコープ内の self やメンバ変数、auto変数が参照できるので userInfo は無くてもいい。そこで userInfo の無い改良版を作ってみた。
まず定義
実装
前回のサンプルを書き換えて上記メソッドを使うようにした。
※ソースコードは GitHubからどうぞ。
NSTimerBlocks at 2010-08-07.2 from xcatsan's iOS-Sample-Code - GitHub
タイマー利用部分はこんな感じ。
これまで見てきた通り、block内ではその外側で定義されている auto変数への参照も可能である。blockがどのようにして auto変数を扱っているかデバッガで調べてみた。
+[NSTimer executeBlock__] が呼び出された時の引数 block の内容は次の通り。
block のメンバ変数に self と array が存在することがわかる。恐らくコンパイラが blockクラスを定義するときに参照している変数を、blockのメンバ変数として定義するのだと思われる。ただ以前の調査では、blockがスタック上にある場合はスコープを外れるとデバッガ上でこれらのメンバ変数が消えていたことが確認できているので、追加・除去は動的に行われるようだ。
前回のコードを少し改良してみる。
改良版 Blocks対応NSTimer
前回は userInfo を持回す為に NSMutableDictionary を導入したり、Blocksの引数に userInfo を付けたりしていたが、考えてみると Blocksは定義されているそのレキシカルスコープ内の self やメンバ変数、auto変数が参照できるので userInfo は無くてもいい。そこで userInfo の無い改良版を作ってみた。
まず定義
typedef void (^TIMER_BLOCK__)(NSTimer*); @interface NSTimer (Extension) + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds block:(TIMER_BLOCK__)block repeats:(BOOL)repeats; @end
実装
+ (void)executeBlock__:(NSTimer*)timer
{
if (![timer isValid]) {
return;
// do nothing
}
TIMER_BLOCK__ block = [timer userInfo];
block(timer);
}
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds
block:(TIMER_BLOCK__)block repeats:(BOOL)repeats
{
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:seconds
target:self
selector:@selector(executeBlock__:)
userInfo:[[block copy] autorelease]
repeats:repeats];
return timer;
}userInfo の持回しが無くなったので、ものすごいシンプルになった。サンプルによる検証
前回のサンプルを書き換えて上記メソッドを使うようにした。
※ソースコードは GitHubからどうぞ。
NSTimerBlocks at 2010-08-07.2 from xcatsan's iOS-Sample-Code - GitHub
タイマー利用部分はこんな感じ。
- (void)viewDidLoad {
[super viewDidLoad];
NSArray* array = [NSArray arrayWithObjects:@"DOG", @"CAT", @"MONKEY", nil];
timer1 = [NSTimer scheduledTimerWithTimeInterval:1.0
block:^(NSTimer* timer) {
self.counterLabel1.text =
[NSString stringWithFormat:@"%d", counter1++];
NSLog(@"%@", array);
}
repeats:YES];
timer2 = [NSTimer scheduledTimerWithTimeInterval:2.0
block:^(NSTimer* timer) {
self.counterLabel2.text =
[NSString stringWithFormat:@"%d", counter2];
counter2 += 10;
NSLog(@"%@", array);
}
repeats:YES];
timer3 = [NSTimer scheduledTimerWithTimeInterval:3.0
block:^(NSTimer* timer) {
self.counterLabel3.text =
[NSString stringWithFormat:@"%d", counter3];
counter3 += 100;
NSLog(@"%@", array);
}
repeats:YES];前回と比べると userInfo が無くなった。また auto変数 array への参照を Block内に記述してある。実行すると NSLog出力で array の内容が参照出来ていることがわかる。NSTimerBlocks[4485:207] (
DOG,
CAT,
MONKEY
)block内でのauto変数参照について
これまで見てきた通り、block内ではその外側で定義されている auto変数への参照も可能である。blockがどのようにして auto変数を扱っているかデバッガで調べてみた。
+[NSTimer executeBlock__] が呼び出された時の引数 block の内容は次の通り。
block のメンバ変数に self と array が存在することがわかる。恐らくコンパイラが blockクラスを定義するときに参照している変数を、blockのメンバ変数として定義するのだと思われる。ただ以前の調査では、blockがスタック上にある場合はスコープを外れるとデバッガ上でこれらのメンバ変数が消えていたことが確認できているので、追加・除去は動的に行われるようだ。
NSTimer を Blocks で処理する
2010年8月6日金曜日 | Published in Blocks, iOS 4.0, iPhone, サンプルあり | 0 コメント
調子に乗って今度は NSTimer に Blocks を導入してみた。
利用イメージはこんな感じ。
NSTimer のカテゴリとして実装した。NSTimer 発火時に呼ばれるメソッドを受け取るインスタンスが必要になるが、今回は NSTimerクラスとした(つまりクラスメソッドを呼び出させる)。このあたりは少々違和感はあるが、一番簡単だと思われるのでそうした。
まずはヘッダから。
実装。
block は copy すると同時に autorelease をかけている。NSTimer のインスタンスが破棄されるタイミングで (NSMutableDictionary*)contextも開放されると思うので、autorelease をかけておけば同時に block も開放されると思われる(多分)。
利用側はこんな感じ。
NSTimerを使って数字をカウントアップするサンプルを作ってみた。異なる周期を持つ3つのタイマー(1秒、2秒、3秒)を作り、画面上の数字をカウントアップする。
ちゃんと動いているようだ。
コードはこんな感じ。
※ソースコードはGitHubからどうぞ。
NSTimerBlocks at 2010-08-06 from xcatsan's iOS-Sample-Code - GitHub
自前で Blocks を実装した他の例など。
- - - -
Blocks が使えると、たった一個とはいえメソッドを作らなくて楽。ましてや複数タイマーを使う場合も面倒が無くかなりいい。可読性もよくなる。Blocks便利だ。
(追記) Block内では、self の他、そのBlockのレキシカルスコープで定義された他の auto変数も参照することができる (*)。そう考えると userInfo が不要なので無くすことができる。こんな感じ。
次回、検証してみる。
(*) 実行側で copyしてヒープへ移しておく必要はある。
利用イメージはこんな感じ。
[NSTimer scheduledTimerWithTimeInterval:1.0
block:^(NSTimer* timer, id userInfo) {
self.counterLabel.text =
[NSString stringWithFormat:@"%d", counter++];
}
userInfo:nil
repeats:YES];実装
NSTimer のカテゴリとして実装した。NSTimer 発火時に呼ばれるメソッドを受け取るインスタンスが必要になるが、今回は NSTimerクラスとした(つまりクラスメソッドを呼び出させる)。このあたりは少々違和感はあるが、一番簡単だと思われるのでそうした。
まずはヘッダから。
typedef void (^TIMER_BLOCK__)(NSTimer*, id); @interface NSTimer (Extension) + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds block:(TIMER_BLOCK__)block userInfo:(id)userInfo repeats:(BOOL)repeats; @endブロックの定義は何箇所かで使うので typedef で定義してみた。
実装。
#define KEY_BLOCK @"block"
#define KEY_USERINFO @"userInfo"
+ (void)executeBlock__:(NSTimer*)timer
{
if (![timer isValid]) {
return;
// do nothing
}
NSDictionary* context = [timer userInfo];
TIMER_BLOCK__ block = [context objectForKey:KEY_BLOCK];
id userInfo = [context objectForKey:KEY_USERINFO];
block(timer, userInfo);
}
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds
block:(TIMER_BLOCK__)block userInfo:(id)userInfo repeats:(BOOL)repeats
{
NSMutableDictionary* context = [NSMutableDictionary dictionary];
[context setObject:[[block copy] autorelease]
forKey:KEY_BLOCK];
if (userInfo) {
[context setObject:userInfo forKey:KEY_USERINFO];
}
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:seconds
target:self
selector:@selector(executeBlock__:)
userInfo:context
repeats:repeats];
return timer;
}ポイントは userInfo を NSDictionary へ入れ替え、そこへ渡ってきた blockを詰めていること。これを発火時(executeBlock__:)に取り出して実行している。block の引数として NSTimer の他、userInfo を渡しているが、これは NSDictionary への入れ替えを行うので -[NSTimer userInfo] が使えないため。block は copy すると同時に autorelease をかけている。NSTimer のインスタンスが破棄されるタイミングで (NSMutableDictionary*)contextも開放されると思うので、autorelease をかけておけば同時に block も開放されると思われる(多分)。
利用側はこんな感じ。
[NSTimer scheduledTimerWithTimeInterval:1.0
block:^(NSTimer* timer, id userInfo) {
self.counterLabel.text =
[NSString stringWithFormat:@"%d", counter++];
}
userInfo:nil
repeats:YES];サンプル
NSTimerを使って数字をカウントアップするサンプルを作ってみた。異なる周期を持つ3つのタイマー(1秒、2秒、3秒)を作り、画面上の数字をカウントアップする。
ちゃんと動いているようだ。
コードはこんな感じ。
timer1 = [NSTimer scheduledTimerWithTimeInterval:1.0
block:^(NSTimer* timer, id userInfo) {
self.counterLabel1.text =
[NSString stringWithFormat:@"%d", counter1++];
}
userInfo:nil
repeats:YES];
timer2 = [NSTimer scheduledTimerWithTimeInterval:2.0
block:^(NSTimer* timer, id userInfo) {
self.counterLabel2.text =
[NSString stringWithFormat:@"%d", counter2];
counter2 += 10;
}
userInfo:nil
repeats:YES];
timer3 = [NSTimer scheduledTimerWithTimeInterval:3.0
block:^(NSTimer* timer, id userInfo) {
self.counterLabel3.text =
[NSString stringWithFormat:@"%d", counter3];
counter3 += 100;
}
userInfo:nil
repeats:YES];
※ソースコードはGitHubからどうぞ。
NSTimerBlocks at 2010-08-06 from xcatsan's iOS-Sample-Code - GitHub
参考情報
自前で Blocks を実装した他の例など。
- Cocoaの日々: 遅延実行を Blocksで記述する
- -[NSObject performSelector:afterDelay:] に Blocksを導入してみた。
- Cocoaの日々: UIActionSheet を Blocks で処理する
- UIActionSheetDelegate を使わず Blocks を使ってボタン押下時の処理を書く。
- - - -
Blocks が使えると、たった一個とはいえメソッドを作らなくて楽。ましてや複数タイマーを使う場合も面倒が無くかなりいい。可読性もよくなる。Blocks便利だ。
(追記) Block内では、self の他、そのBlockのレキシカルスコープで定義された他の auto変数も参照することができる (*)。そう考えると userInfo が不要なので無くすことができる。こんな感じ。
timer1 = [NSTimer scheduledTimerWithTimeInterval:1.0
block:^(NSTimer* timer) {
self.counterLabel1.text =
[NSString stringWithFormat:@"%d", counter1++];
NSLog(@"%@", array);
}
repeats:YES];次回、検証してみる。
(*) 実行側で copyしてヒープへ移しておく必要はある。
登録:
投稿 (Atom)
人気の投稿(過去 30日間)
-
[前回] Cocoaの日々: SimpleCap - Selection History Expose [4] 仮表示と選択 前回は履歴の矩形を NSView に直接描画していた。今回はこれを Core Animation に切り替え、各矩形がアニメーションできるようにお膳立...
-
2010-12-19 [訂正] Objective-Zip ではなく ziparchive の間違いです。 Source Checkout - ziparchive - Project Hosting on Google Code 根本的な間違いで失礼しました。 iPh...
-
iOS 5 から Twitter投稿用の API が追加された。前から興味があったので試してみた。 TWTweetComposeViewController Class Reference (2012/02/24追記あり) 使い方 プロジェクトへフレームワークを追...
-
[前回] Bezelボタンを作る[08]タッチした時のリアクションを実装する 今回はディゼーブル状態の描画を行う。 ディゼーブル状態の描画 Bezelボタンは UIControl のサブクラスなので有効/無効状態を表すプロパティ enabled を持っている。この値...
-
(2011-12-01 追記あり)UIApplicationDelegateの呼び出しが iOS5 から変わった件。 Task Completion を使った iOS4 でのバックグラウンド実行サンプルを作ってみた。 サンプル 実行するとキューにたまった 30個のデータ...

















