viewDidUnload はメモリ不足時だけに呼ばれる

先日、instruments でリークを発見した際にあまりにも恥ずかしい勘違いがあったので、メモしておきます。
viewDidUnload は、メモリ不足警告時においてのみ呼び出される。ということ。

何気なく、- (id)init でアロケートし、- (void)dealloc で解放してましたが、理解不足でした。
よく覚えておこう。

NSCalendar : 月末が第何週目か取得する方法

NSCalendar クラスの ordinalityOfUnit:inUnit:forDate:メソッドが便利だったのでメモしておきます。
例えば、月末が月の第何周であるか?とか調べるのに利用できます。
その他、年内で第何周であるか?とかも調べられるはず。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// NSDate にカテゴリでメソッドを追加しておく
@interface NSDate (DateAdditions)
 
// 月の日数を取得する
- (NSUInteger)getNumberOfDaysInMonth
{
    return [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:self].length;
}
 
		:
		:
// 月の日数を取得する
NSDate *todayDate = [NSDate date];
NSUInteger lastdayInMonth = [todayDate getNumberOfDaysInMonth];
 
// 本日日付を年月日で分解する
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *dateComps = [cal components:NSYearCalendarUnit | 
                                              NSMonthCalendarUnit |
                                              NSDayCalendarUnit
                                     fromDate:[NSDate date]];
// 月末日付を取得する
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setYear:dateComps.year];
[comps setMonth:dateComps.month];
[comps setDay:lastdayInMonth];
NSDate *lastDate = [cal dateFromComponents:comps];
[comps release];
 
// 月末が第何週目か取得する
NSUInteger lastdayWeekNo = [cal ordinalityOfUnit:NSWeekCalendarUnit 
                                          inUnit:NSMonthCalendarUnit 
                                         forDate:lastDate];
// 本日が月の第何週目か取得する
NSUInteger todayWeekNo = [cal ordinalityOfUnit:NSWeekCalendarUnit 
                                        inUnit:NSMonthCalendarUnit 
                                       forDate:[NSDate date]];

xcode4.3をインストール

本日はまたいろいろ Apple 関連のニュース(OS X Mountain Lion とか)があったが、xcode がバージョンアップしたとのことだったので、早速インストールした。
インストール作業は滞りなく進んだが、xcode4.3を起動することができず、少し悩んだ。
xcode4.3 は アプリケーション フォルダにインストールされるらしい。

また、こんなメニュー項目も追加されているみたい。

キーボードに入力領域が重ならないようにする方法

以下の様なテーブル ビューをデザインしたとします。
テーブルのセルにはテキスト フィールドが配置されており、キーボードからの入力が可能とします。

ボタン10のタイトル行などにキーボード入力を行おうとフォーカスを移動すると、画面下部から表示されるキーボードで入力領域が隠されてしまうことになります。

そこで入力領域が重ならないように、以下の様にテーブルを引っ張り上げます。(表現がいまいちですが、通常のスクロールでは移動できない位置にセルを上げ下げします。)
このような表示は、キーボードの表示、非表示時に UITableView の UIEdgeInsets の値を調整することで、編集中セルを画面中央へ移動させることが可能です。
あと、iOS 5 では、日本語キーボードの上部に予測変換候補が表示される仕様となっているようなので考慮しておきます。

手順は、以下の通り
1. UITableViewController でキーボードの通知を取得できるようにする
2. キーボードの表示、非表示に UITableView の UIEdgeInsets の値を調整することで、編集中セルを画面中央へ移動させる

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
 
        // ソフトキーボードの表示、非表示の通知を登録する
        NSNotificationCenter *center;
        center = [NSNotificationCenter defaultCenter];
        [center addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
        [center addObserver:self selector:@selector(keybaordWillHide:) name:UIKeyboardWillHideNotification object:nil];
    }
 
    return self;
}
 
- (void)dealloc
{
    // ソフトキーボードの表示、非表示の通知を解除する
    NSNotificationCenter *center;
    center = [NSNotificationCenter defaultCenter];
    [center removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [center removeObserver:self name:UIKeyboardWillHideNotification object:nil];
 
    [super dealloc];
}
 
#pragma mark -
#pragma mark Software Keyboard Mthods
 
- (void)keyboardWillShow:(NSNotification*)notification
{
    NSDictionary *userInfo;
    userInfo = [notification userInfo];
 
    // 1. キーボードの top を取得する
    CGRect keyboardFrame;
    keyboardFrame = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
 
    CGFloat keyboardTop = (APP_CONTENT_HEIGHT) - (keyboardFrame.size.height + 55.f );   // 55.f:予測変換領域の高さ
 
 
    // 2. 編集中セルの bottom を取得する
    // テーブル ビューはスクロールしていることがあるので、オフセットを考慮すること
    UITableViewCell *cell;
    cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath_.row 
                                                                    inSection:indexPath_.section]];
    CGRect cellFrame;
    cellFrame = cell.frame;
 
    CGPoint offset = CGPointZero;
    offset =  self.tableView.contentOffset;  
 
    CGRect cellRectOrigin = CGRectZero;    
    cellRectOrigin.origin.x = cellFrame.origin.x - offset.x;  
    cellRectOrigin.origin.y = cellFrame.origin.y - offset.y; 
 
    CGFloat cellBottom = cellRectOrigin.origin.y + cellFrame.size.height + 30.f;   // 30.f:マージン
 
 
    // 編集中セルの bottom がキーボードの top より上にある場合、
    // キーボードに隠れないよう位置を調整する処理対象外とする
    if (cellBottom < keyboardTop) {
        return;
    }
 
 
    // 3. 編集中セルとキーボードが重なる場合、編集中セルを画面中央へ移動させる
    // キーボードの高さ分の insets を作成する    
    UIEdgeInsets insets;
    insets = UIEdgeInsetsMake(0.0f, 0.0f, keyboardTop, 0.0f);
 
    NSTimeInterval duration;
    UIViewAnimationCurve animationCurve;
    void (^animations)(void);
    duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    animationCurve = [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
    animations = ^(void) {
        self.tableView.contentInset = insets;
        self.tableView.scrollIndicatorInsets = insets;
    };
    [UIView animateWithDuration:duration delay:0.0 options:(animationCurve << 16) animations:animations completion:nil];
 
    // 編集中セルの位置を調整する
    CGRect rect = cell.frame;
    rect.origin.y = cellFrame.origin.y - 300.f;
 
    [self.tableView scrollRectToVisible:rect animated:YES];
}
 
- (void)keybaordWillHide:(NSNotification*)notification
{
    NSDictionary *userInfo;
    userInfo = [notification userInfo];
 
    NSTimeInterval duration;
    UIViewAnimationCurve animationCurve;
    void (^animations)(void);
    duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    animationCurve = [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
    animations = ^(void) {
        // insets を 0 にする
        self.tableView.contentInset = UIEdgeInsetsZero;
        self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
    };
    [UIView animateWithDuration:duration delay:0.0 options:(animationCurve << 16) animations:animations completion:nil];  
}

xcode の git ステータスが表示されなくなった際の対処方法

xcode4 で開発を行っている際に git を利用しているとプロジェクト ナビゲータのファイル横に改変ステータスが表示されます。
例えば、追加されたファイルには”A”、編集されたファイルには”M”などのマークです。

これらのマークがある時期から表示されなくなったので何とか表示したいと考えていたのですが、対処方法を調べる時間がなく、そのままの状態で開発を行っていました。
ですが、改版中のファイルが分かるとあとでコードの見直しなどする際にメリットがあるので、少し調べてみました。

私の環境でそのような現象が起きていたのは、作業開始時に開発に関するファイルをコピーしてから作業を開始している為のようです。その際、git の管理ができなくなっており、新規に作成したファイルなどの改変ステータスが表示できなくなっているようです。

というわけで、コマンドラインから新規作成ファイルをプロジェクトにインポートすることで git の管理下に置くことで、プロジェクト ナビゲータの改変ステータスが表示されるようになるか確認したところ、正常に改変ステータスを表示できるようになりました。
その際のコマンドは以下の通り。

git に新しいプロジェクト ソースをインポートする方法を参考に対処することにした。

$ cd (プロジェクトのトップディレクトリ)
$ git init
$ git add .
$ git commit -m "コミット コメント"

参考になるURL
チュートリアル:新規リポジトリのインポート

Git ユーザマニュアル:新規リポジトリの作成

「WEB+DB PRESS vol.50 はじめてのGit」にも git 記事があるようです。

処理を一時停止する方法

UITableView のセルに UITextField を保持したカスタム セルを用意し、設定値を入力できるようにした。

カスタマイズ UITableViewCell

画面に”登録”ボタンを配置したが、UITextField に設定値を入力編集後、”登録”ボタンを押下し、画面遷移すると設定値が保存されていないという問題が発生した。

原因は、以下の通り。
UITextField の UIControlEventEditingDidEndOnExit コントロール イベントを取得し、編集終了と判断して入力された設定値を保存しようとしていたが、UIControlEventEditingDidEndOnExit の通知を受ける前に”登録”ボタン押下時の処理が行われていた。
結果、編集した設定値を保存する前に画面遷移が行われていたのが問題だと分かった。

というわけで、
(対処前)”登録”ボタン押下処理 → (変更前の)設定値 保存 → UITextField 編集終了の通知
(対処後)UITextField 編集終了の通知 → “登録”ボタン押下処理 → (変更後の)設定値 保存
“対処後”の順番で処理が行われるように、NSRunLoop クラスを利用して UITextField 編集終了の通知を受けるまで、処理を一時停止するようにした。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 登録ボタンが押下された際の処理
- (void)registerButtonAction
{
    // 編集終了通知が受信されるまで待つ (0.5秒間隔で受信確認する)
    while (!didEndEditingFlag_) {
        // ソフトキーボードが表示されている場合、非表示にしないと編集終了通知がされないため、非表示とする
        [editingTextField_ resignFirstResponder];
 
        // 処理を一時停止し NSRunLoop へ制御を戻す
        // 0.5秒後、処理を再開する
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
    }
 
    // 設定情報マネージャへデータを保存する
    [self writeSettingManagerValue];
     :
     :

iOS アプリでバージョン情報を保持する方法

iOS アプリのバージョン情報は、プロジェクトで指定することができる。

バージョンとビルドの設定

バージョンとビルドの設定

プログラム コードで参照するには以下の通り。

1
2
3
4
5
6
7
// バージョン情報を取得する
- (NSString *)getVersion 
{
    NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
    NSString *build = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
    return [NSString stringWithFormat:@"バージョン:%@ ビルド:%@", version, build];
}

UITapGestureRecognizer と通常のアクション メソッドを利用する

1
2
3
4
5
6
7
8
UITapGestureRecognizer *tapRecognizer;
tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapColumn:)];
 
// 通常のテーブル ビューのアクションもコールバックさせるため
tapRecognizer.cancelsTouchesInView = NO;
 
[tblView addGestureRecognizer:tapRecognizer];
[tapRecognizer release];

UISwitch のテキストが、”|”と”◯”で表示される場合の対処方法

UISwitch を利用する際、プロジェクトに英語リソースしかない場合、UISwitch コントロールに”|”と”◯”が表示されてしまう。

日本語リソースを追加すると、”オン”と”オフ”で表示してくれる。

でも、結構流通しているアプリでも UISwitch コントロールが”|”と”◯”のアプリって見かけるしなぁ〜 どうなんだろう?

NSArray から NSMutableArray を作成する方法

NSUserDefaults などから NSArray を取得し、NSMutableArray としてオブジェクト配列を利用したい場合、mutableCopy メソッドが利用できる。

1
2
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray* mutableArray= [[defaults arrayForKey:@"STATE"] mutableCopy];