Nav(19)~Detail Edit(4)

2011. 01. 18
●詳細ビューコントローラのソースファイルPresidentsDetailController.mの編集

ソースファイルでは表示するテーブルの作成と、行を選択した際のテキストフィールドの編集、およびナビゲーションバーのボタンをタップした際の処理を行います。
(太字が追加・修正した部分)

#import "PresidentDetailController.h"
#import "President.h"

@implementation PresidentDetailController

@synthesize president;
@synthesize fieldLabels;
@synthesize tempValues;
@synthesize textFieldBeingEdited;

#pragma mark -

- (IBAction)cancel:(id)sender {
    [self.navigationController popViewControllerAnimated:YES];
}

- (IBAction)save:(id)sender {
    if (textFieldBeingEdited != nil) {
        NSNumber *tagAsNum = [[NSNumber alloc] initWithInt:textFieldBeingEdited.tag];
        [tempValues setObject:textFieldBeingEdited.text forKey:tagAsNum];
        [tagAsNum release];
    }

    for (NSNumber *key in [tempValues allKeys]) {
        switch ([key intValue]) {
            case kNameRowIndex:
                president.name = [tempValues objectForKey:key];
                break;
            case kFromYearRowIndex:
                president.fromYear = [tempValues objectForKey:key];
                break;
            case kToYearRowIndex:
                president.toYear = [tempValues objectForKey:key];
                break;
            case kPartyIndex:
                president.party = [tempValues objectForKey:key];
                break;
            default:
                break;
        }
    }
    [self.navigationController popViewControllerAnimated:YES];

    NSArray *allControllers = self.navigationController.viewControllers;
    UITableViewController *parent = [allControllers lastObject];
    [parent.tableView reloadData];
}

- (IBAction)textFieldDone:(id)sender {
    [sender resignFirstResponder];
}


#pragma mark -
#pragma mark Initialization

- (id)initWithStyle:(UITableViewStyle)style {
    // Override initWithStyle: if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
    self = [super initWithStyle:style];
    if (self) {

        // Custom initialization.
    }
    return self;
}


#pragma mark -
#pragma mark View lifecycle

- (void)viewDidLoad {
    [super viewDidLoad];

    NSArray *array = [[NSArray alloc] initWithObjects:@"Name:", @"From:", @"To:", @"Party:", nil];
    self.fieldLabels = array;
    [array release];

    UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(cancel:)];
    self.navigationItem.leftBarButtonItem = cancelButton;
    [cancelButton release];

    UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithTitle:@"Save" style:UIBarButtonItemStyleDone target:self action:@selector(save:)];
    self.navigationItem.rightBarButtonItem = saveButton;
    [saveButton release];

    NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
    self.tempValues = dict;
    [dict release];
}


// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations.
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}


#pragma mark -
#pragma mark Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of rows in the section.
    return kNumberOfEditableRows;
}

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *PresidentCellIdentifier = @"PresidentCellIdentifier";


    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PresidentCellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:PresidentCellIdentifier] autorelease];

        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 75, 25)];
        label.textAlignment = UITextAlignmentRight;
        label.tag = kLabelTag;
        label.font = [UIFont boldSystemFontOfSize:14];
        [cell.contentView addSubview:label];
        [label release];

        UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(90, 12, 200, 25)];
        textField.clearsOnBeginEditing = NO;
        [textField setDelegate:self];
        textField.returnKeyType = UIReturnKeyDone;
        [textField addTarget:self action:@selector(textFieldDone:) forControlEvents:UIControlEventEditingDidEndOnExit];
        [cell.contentView addSubview:textField];

    }

    NSUInteger row = [indexPath row];

    UILabel *label = (UILabel *)[cell viewWithTag:kLabelTag];

    UITextField *textField = nil;

    for (UIView *oneView in cell.contentView.subviews) {
        if ([oneView isMemberOfClass:[UITextField class]])
            textField = (UITextField *)oneView;
    }

    label.text = [fieldLabels objectAtIndex:row];

    NSNumber *rowAsNum = [[NSNumber alloc] initWithInt:row];

    switch (row) {
        case kNameRowIndex:
            if ([[tempValues allKeys] containsObject:rowAsNum])
                textField.text = [tempValues objectForKey:rowAsNum];
            else
                textField.text = president.name;
            break;
        case kFromYearRowIndex:
            if ([[tempValues allKeys] containsObject:rowAsNum])
                textField.text = [tempValues objectForKey:rowAsNum];
            else
                textField.text = president.fromYear;
            break;
        case kToYearRowIndex:
            if ([[tempValues allKeys] containsObject:rowAsNum])
                textField.text = [tempValues objectForKey:rowAsNum];
            else
                textField.text = president.toYear;
            break;
        case kPartyIndex:
            if ([[tempValues allKeys] containsObject:rowAsNum])
                textField.text = [tempValues objectForKey:rowAsNum];
            else
                textField.text = president.party;
            break;
        default:
            break;
    }


    // Configure the cell...
    if (textFieldBeingEdited == textField)
        textFieldBeingEdited = nil;

    textField.tag = row;
    [rowAsNum release];

    return cell;
}

#pragma mark -
#pragma mark Table view delegate

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    return nil;
}

#pragma mark -
#pragma mark Text field delegate

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    self.textFieldBeingEdited = textField;
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    NSNumber *tagAsNum = [[NSNumber alloc] initWithInt:textField.tag];
    [tempValues setObject:textField.text forKey:tagAsNum];
    [tagAsNum release];
}


#pragma mark -
#pragma mark Memory management

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Relinquish ownership any cached data, images, etc. that aren't in use.
}

- (void)viewDidUnload {
    // Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
    // For example: self.myOutlet = nil;

    self.president = nil;
    self.fieldLabels = nil;
    self.tempValues = nil;
    self.textFieldBeingEdited = nil;

}

- (void)dealloc {
    [president release];
    [fieldLabels release];
    [tempValues release];
    [textFieldBeingEdited release];

    [super dealloc];
}

@end

1080

1)President.hのインポート

サブビューDetail Editで選択された行に基づいて生成されたPresidentクラスのインスタンスを受け取って処理するため、ヘッダファイルPresident.hをインポートします。

また旧版の本書にあるNavAppDelegate.hのインポートは、navigationControllerプロパティを使用するため不要になります。

2)4つのプロパティの実装

ヘッダファイルPresidentDetailController.hで宣言した4つのプロパティ(president、fieldLabels、tempValues、textFieldBeingEdited)を実装します。

3)cancel:の実装

アクションメソッドcancel:は、詳細ビューでナビゲーションバーの左側に表示されるCancelボタンがタップされた際に呼び出されます。

ここでは単純にpopViewControllerAnimated:メソッドで親ビューであるサブビューDetail Editに戻る(ポップする)処理を行います。

旧版の本書ではサブビューのプッシュにアプリケーションデリゲートのインスタンスを作成し、UINavigationControllerのアウトレットnavControllerにプッシュしていますが、サンプルコードや自動生成されるテンプレートのようにnavigationControllerプロパティを使用して置き換えています。


popViewControllerAnimated:

- (UIViewController *)popViewControllerAnimated:(BOOL)animated

ナビゲーションスタックから最上層のビューコントローラをポップして、表示を更新します。

戻り値は、スタックからポップされたビューコントローラです。

このメソッドは、スタックから最上位のビューコントローラを削除し、アクティブなビューコントローラを新たな最上位のビューコントローラにします。

スタックの最上位のビューコントローラがルートビューコントローラの場合、このメソッドは何もしません。

言い換えると、スタック上の最後のアイテムはポップできません。

また、スタックの最上位にある新しいビューコントローラに関連付けされたビューを表示する場合、このメソッドはそれに応じてナビゲーションバーとツールバーを更新します。

iOS 3.0以降では、組み込まれているナビゲーションツールバーのコンテンツは、新しいビューコントローラのツールバーアイテムを反映して更新されます。

ナビゲーションバーの更新についての詳細は、『ナビゲーションバーの更新』を参照してください。

animated:この値にYESを設定した場合は遷移をアニメーションします。
ビューを表示する前にナビゲーションコントローラを設定する場合はNOを渡してください。

4)save:の実装

アクションメソッドsave:は、詳細ビューでナビゲーションバーの右側に表示されるSaveボタンがタップされた際に呼び出されます。

ここではデータを保存するにあたり、最初に編集中(アクティブ)なテキストフィールドがあるかどうかを判定し、あれば予めテキストフィールドの文字列を一時辞書tempValuesに書き出します。
(編集が完了している場合は、後述するtextFieldDidEndEditing:でtempValuesに書き出されています)

その後、高速列挙でtempValuesの内容をPresidentクラスのインスタンスpresidentに書き出し、サブビューDetail Editに戻ってテーブルビューを再読み込みします。

編集中のテキストフィールドの有無はプロパティtextFieldBeingEditedがnilかどうかで判定します。
(textFieldBeingEditedは、テキストフィールドの編集が開始された時に(textFieldDidBeginEditing:で)そのテキストフィールドのポインタを示し、そうでない場合はnilに設定されます)

テキストフィールドが編集中の場合、tagプロパティでテキストフィールドの持つビューの識別番号を取得し、initWithInt:メソッドで数値オブジェクトtagAsNumとします。
(テキストフィールドのタグは、tableView:cellForRowAtIndexPath:メソッドの終盤にセルの行番号を指定しています)

そしてテキストフィールドの文字列をtextプロパティで取得し、識別番号(行番号)をキーとしてsetObject:forKey:メソッドでtempValuesに書き出します。

高速列挙では一時辞書tempValuesに含まれる全ての行(キー)をPresidentクラスのインスタンスpresidentに書き出すため、allKeysメソッドで行番号を取得し、intValueで整数値に変換してswitch文で各行の書き出しを行っています。

本書やサンプルコードでは、kPartyIndexの場合にbreakが入っていません。

Wikipedia/switch文』によるとC言語におけるswitch文の特性として、case:やdefault:はラベルとして扱われるので、breakが入っていない場合は以下の(ここではdefault:条件の)処理がされることになります。

今回はdefault条件時ではbreakしかしていないので動作としては問題無いのですが、後から行数(分岐条件)が増やた場合やdefault条件での処理が変わった場合に問題が生じるので、後のメンテナンスのことを考えるとbreakを入れておいた方が無難と考え、ここでは追加しています。

一時データの書き出しが済んだら、navigationControllerプロパティにpopViewControllerAnimated:メソッドで親ビューであるサブビューDetail Editに戻る(ポップする)処理を行います。

Saveボタンがタップされた場合、内容が変更されている可能性があるので、サブビューのテーブルを更新します。

最初にviewControllersプロパティでナビゲーションスタックの履歴を配列allControllersに取得します。

次にallControllersからlastObjectメソッドで履歴配列の最後、つまりサブビューDetail Editのビューコントローラを取得し、tableViewプロパティでそのテーブルビューに対しreloadDataメソッドでテーブルビューの更新を行います。

しかし実際には『Nav(16)~Detail Edit(1)』の概要で説明したように、サブビューへの復帰直後はテーブルビューの更新はなされず、当該行を画面外に一度押しやってセルの再描画を行わないと更新されません。

少し調べてみたのですが、一筋縄では解決しそうにないので保留にします。


initWithInt:

- (id)initWithInt:(int)value

指定した値を符号付整数として扱い、初期化したNSNumberオブジェクトを返します。

value:新しい数値の値を指定します。


intValue 

- (int)intValue

レシーバのNSNumberオブジェクトをint値にして返します。


viewControllers

@property(nonatomic, copy) NSArray *viewControllers

ナビゲーションスタック上のビューコントローラの現状(履歴)を示します。

ルートビューコントローラは配列のインデックス0で、配列内のアイテム数をnとした場合、最上位のコントローラのインデックスはn-1、以前のビューコントローラのインデックスはn-2になります。

このプロパティにビューコントローラの新しい配列を割り当てるには、setViewControllers:animated:メソッドをanimatedパラメータをNOに設定して呼び出します。


lastObject

- (id)lastObject

配列内で最も高いインデックス番号のオブジェクトを返します。

配列が空の場合はnilを返します。


tableView

@property(nonatomic, retain) UITableView *tableView

コントローラオブジェクトによって管理されたテーブルビューを返します。

5)textFieldDone:の実装

textFieldDone:メソッドは、テキストフィールドの編集中に表示されるキーボード右下にあるDoneボタンをタップした時に呼び出されるメソッドで、ここでは単純にresignFirstResponderメソッドでファーストレスポンダを放棄、つまりキーボードを閉じて編集モードの解除をしています。

6)initWithStyle:の実装

本書やサンプルコードでは省略されていますが、initWithStyle:メソッドのコメントアウトを解除してデフォルトのまま実装しています。

7)viewDidLoadの実装

viewDidLoadではコメントアウトを解除し、テーブルセルのラベルfieldLabelsとナビゲーションバー左側のCancelボタン、右側のSaveボタンの設定、およびテキストフィールドの値を一時的に保管する可変辞書tempValuesの初期化を行っています。

fieldLabelsは固定配列で、名前、任期開始年、任期終了年、所属政党のラベルとなる文字列をinitWithObjects:で初期化しています。

ナビゲーションバーへ追加するボタンは、UIBarButtonItemのオブジェクトとしてボタンを作成し、ナビゲーションバーに組み込みます。

ボタンの生成・初期化はinitWithTitle:style:target:action:メソッドで、タイトルを『Cancel』と『Save』、スタイルはCancelボタンはUIBarButtonItemStylePlain、SaveボタンはUIBarButtonItemStyleDoneとし、アクションでそれぞれのメソッドを呼び出す設定になっています。

作成したボタンはnavigationItemプロパティで、Cancelボタンはバーの左側(leftBarButtonItem)、Saveボタンはバーの右側(rightBarButtonItem)に設置します。

一時可変辞書tempValuesは、allocとinitで生成・初期化をしただけの空の状態で、具体的な値はsave:メソッドで設定されます。

本書やサンプルコードではスーパークラスでの初期化を最後に行っていますが、ここでは最初に行っています。


leftBarButtonItem

@property(nonatomic, retain) UIBarButtonItem *leftBarButtonItem

このアイテムが上表部のアイテムの時、ナビゲーションバーの左側にカスタムバーアイテムを表示します。

8)不要なView lifecycleのメソッド

viewWillAppear:、viewDidAppear:、viewWillDisappear:、viewDidDisappear:の4つは今回使用しないので削除します。



参考文献

UINavigationController Class Reference

NSNumber Class Reference

NSArray Class Reference

UITableViewController Class Reference

UINavigationItem Class Reference

Wikipedia/switch文

はじめてのiPhone3プログラミングはじめてのiPhone3プログラミング
(2009/12/17)
Dave Mark、Jeff LaMarche 他

商品詳細を見る






Bose QuietComfort 20
0 Comments
Leave a comment
管理者にだけ表示を許可する
Top
0 Trackbacks
Top
Calendar
08 | 2017/09 | 10
Sun Mon Tue Wed Thu Fri Sat
- - - - - 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
Recent Articles
iTunes


Swift
Categories
Tips
Profile

水月杏香

Author:水月杏香
永遠の初心者プログラマ。

Wish List
WACOM


ARC
Technical Q&A
情報プロパティリストキー
Start Developing iOS Apps Today
BOSE

Bose QuietComfort 20
Reference
NSApplicationDelegateプロトコル
NSArrayクラス
NSAutoreleasePoolクラス
NSBundleクラス
NSBundle UIKit追加分
NSCalendarクラス
NSCoderクラス
NSCodingプロトコル
NSCopyingプロトコル
NSDataクラス
NSDateクラス
NSDateFormatterクラス
NSDictionaryクラス
NSEntityDescriptionクラス
NSEnumeratorクラス
NSErrorクラス
NSExceptionクラス
NSFetchRequestクラス
NSFileHandleクラス
NSFileManagerクラス
NSIndexPathクラス
NSIndexPath UIKit追加分
NSKeyedArchiverクラス
NSKeyedUnarchiverクラス
NSKeyValueCodingプロトコル
NSLocaleクラス
NSManagedObjectクラス
NSManagedObjectContextクラス
NSManagedObjectModelクラス
NSMutableArrayクラス
NSMutableCopyingプロトコル
NSMutableDictionaryクラス
NSMutableSetクラス
NSNotificationクラス
NSNotificationCenterクラス
NSNullクラス
NSNumberクラス
NSObjectクラス
NSObject UIKit追加分
NSObjectプロトコル
NSPersistentStoreクラス
NSPersistentStoreCoordinatorクラス
NSPredicateクラス
NSPropertyListSerializationクラス
NSRunLoopクラス
NSSetクラス
NSStringクラス
NSString UIKit追加分
NSTimerクラス
NSTimeZoneクラス
NSURLクラス
NSURLProtectionSpaceクラス
NSURLRequestクラス
NSUserDefaultsクラス
NSValueクラス

UIActionSheetクラス
UIActionSheetDelegateプロトコル
UIActivityIndicatorViewクラス
UIAlertViewクラス
UIAlertViewDelegateプロトコル
UIApplicationクラス
UIApplicationDelegateプロトコル
UIBarButtonItemクラス
UIBarItemクラス
UIButtonクラス
UIColorクラス
UIControlクラス
UIDatePickerクラス
UIDeviceクラス
UIEventクラス
UIFontクラス
UIGestureRecognizerクラス
UIImageクラス
UIImageViewクラス
UIKit Function
UILabelクラス
UINavigationControllerクラス
UINavigationItemクラス
UIPickerViewクラス
UIPickerViewDataSourceプロトコル
UIPickerViewDelegateプロトコル
UIPinchGestureRecognizerクラス
UIResponderクラス
UIScreenクラス
UIScrollViewクラス
UISearchBarクラス
UISearchBarDelegateプロトコル
UISegmentedControlクラス
UISliderクラス
UISwipeGestureRecognizerクラス
UISwitchクラス
UITableViewクラス
UITableViewCellクラス
UITableViewControllerクラス
UITableViewDataSourceプロトコル
UITableViewDelegateプロトコル
UITapGestureRecognizerクラス
UITextFieldクラス
UITextFieldDelegateプロトコル
UITextInputTraitsプロトコル
UITextViewクラス
UITextViewDelegateプロトコル
UIToolbarクラス
UITouchクラス
UIViewクラス
UIViewControllerクラス
UIWebViewクラス
UIWebViewDelegateプロトコル
UIWindowクラス

AVAudioPlayerクラス
AVAudioPlayerDelegateプロトコル

CADisplayLinkクラス
CAEAGLLayerクラス
CALayerクラス

CGAffineTransform
CGBitmapContext
CGColor
CGColorSpace
CGContext
CGGeometry
CGImage
CGPath

EAGLContextクラス
EAGLDrawableプロトコル

Foundation Constants
Foundation Data Types
Foundation Functions

MPMediaItemクラス
MPMediaItemArtworkクラス
MPMediaPlaylistクラス
MPMediaPropertyPredicateクラス
MPMediaQueryクラス
MPMusicPlayerControllerクラス

Randomization Services

System Sound Services
Amazon


OpenGL ES
SQLite
Monthly Archives
Recent Comments
Recent TrackBacks
RSS Link
Visitors
QR Code
QR