Nav(7)~Check One

2011. 01. 06
●FirstLevelViewController.mの訂正

FirstLevelViewController.mのtableView:didSelectRowAtIndexPath:メソッドにおいて、プッシュしたサブビューコントローラnextControllerの解放を追加したところ、ルートビューで項目を再選択した際に強制終了する不具合を確認しました。

元記事『Nav(3)~ルートビューコントローラ』に訂正を入れましたが、改めて告知します。

お手数ですが最後の文『[nextController release];』は削除してください。


●Check Oneの概要

二つ目のサブビューとなるCheck Oneの概要を説明します。

1009

ルートビューの2行目にある『Check One』をタップするとサブビューのテーブルに移行します。

1029

開いた直後はどの行にもチェックマークが入っていません。

行をタップするとその行にチェックマークが入り、別の行をタップすると以前の行のチェックマークは消えて、その行にチェックマークが入るラジオボタンのような挙動になります。

前回のDisclosure Buttonsのような詳細ビューは持たず、この階層のみとなります。


●CheckListControllerクラスの作成

本書ではUIViewController subclassテンプレートで作ることになっていますが、ここではUITableViewController subclassテンプレートで作成します。

XcodeのClassesを右クリックして『追加』→『新規ファイル...』で、左ペインのiOSカテゴリからCocoa Touch Classを選択し、右上ペインでUIViewController subclassを、右下ペインのオプションでUITableViewController subclassにチェックを入れ、ファイル名をCheckListControllerとします。


●サブビューCheck oneのヘッダファイルCheckListController.hの編集

SecondLevelViewControllerクラスを継承し、テーブルに表示する配列と選択された行を保持するインデックスパスのプロパティを宣言します。
(太字が追加・修正した部分)

#import <UIKit/UIKit.h>
#import "SecondLevelViewController.h"

@interface CheckListController : SecondLevelViewController <UITableViewDelegate, UITableViewDataSource> {
    NSArray *list;
    NSIndexPath *lastIndexPath;

}

@property (nonatomic, retain) NSArray *list;
@property (nonatomic, retain) NSIndexPath *lastIndexPath;


@end

1030

SecondLevelViewControllerを継承するため、ヘッダファイルSecondLevelViewController.hのインポートとクラス宣言でスーパークラスをUITableViewControllerからSecondLevelViewControllerへの変更を行います。

それとApress社のサイト内のサンプルコードでは省略されていますが、テーブルを扱うので旧版の本書にあるようにUITableViewDelegateとUITableViewDataSourceプロトコルを採用します。

配列プロパティのlistは、サブビューCheck Oneでテーブルに表示する内容を定義します。

インデックスパスプロパティのlastIndexPathは、前回選択された行番号を保持し、別の行が選択された際に前回選択した行のチェックマークを解除するのに使用します。


●サブビューCheck oneのソースファイルCheckListController.mの編集

ソースファイルでは、表示するテーブルの作成と、行をタップした際の処理を行います。
(太字が追加・修正した部分)

#import "CheckListController.h"

@implementation CheckListController

@synthesize list;
@synthesize lastIndexPath;


#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:@"A6M3a", @"Bf109G-2",
    @"Spitfire Mk.IX", @"VG39", @"Bf109G-6", @"Fw190D-6", @"G-55 Centauro", @"P-51D",
    @"Bf109G-2", @"MiG60", nil];
    self.list = array;
    [array 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 [list count];
}

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

    static NSString *CheckMarkCellIdentifier = @"CheckMarkCellIdentifier";

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

    // Configure the cell...
    NSUInteger row = [indexPath row];
    NSUInteger oldRow = [lastIndexPath row];
    cell.textLabel.text = [list objectAtIndex:row];
    cell.accessoryType = (row == oldRow && lastIndexPath != nil) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;

    return cell;
}

#pragma mark -
#pragma mark Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    int newRow = [indexPath row];
    int oldRow = (lastIndexPath != nil) ? [lastIndexPath row] : -1;
    if (newRow != oldRow) {
        UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
        newCell.accessoryType = UITableViewCellAccessoryCheckmark;
        UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:lastIndexPath];
        oldCell.accessoryType = UITableViewCellAccessoryNone;
        lastIndexPath = indexPath;
    }
    [tableView deselectRowAtIndexPath:indexPath animated:YES];

}

#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.list = nil;
    self.lastIndexPath = nil;

}

- (void)dealloc {
    [list release];
    [lastIndexPath release];

    [super dealloc];
}

@end

1031

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

配列listとインデックスパスlastIndexPathの両プロパティを実装します。

2)initWithStyle:の実装

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

3)viewDidLoadの実装

viewDidLoadはコメントアウトを解除し、配列listを設定します。

一時配列arrayをinitWithObjects:で初期化し配列listに代入していますが、中身の文字列についは本書やサンプルコードとは異なります。

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

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

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

5)shouldAutorotateToInterfaceOrientation:の実装

本書やサンプルコードでは触れられていませんが、shouldAutorotateToInterfaceOrientation:のコメントアウトを解除してデフォルトの状態(ポートレートのみ有効)で実装しています。

6)numberOfSectionsInTableView:の削除

セクションが一つしか無いので、numberOfSectionsInTableView:は不要のため削除します。

7)tableView:numberOfRowsInSection:の実装

セクションの行数として、tableView:numberOfRowsInSection:ではサブビューの配列listの要素数をcountメソッドで返します。

8)tableView:cellForRowAtIndexPath:の実装

tableView:cellForRowAtIndexPath:では、サブビューCheck Oneのテーブルセルの内容を設定します。

再利用識別子は『CheckMarkCellIdentifier』と設定しています。

if文内のinitWithFrame:reuseIdentifier:メソッドはiOS 3.0以降非推奨なので、自動生成されるinitWithStyle:reuseIdentifier:で初期化しています。

セルの構成は、最初に引数のインデックスパスからrowプロパティで行番号をrowとして取得します。

次に前回選択したインデックスパスlastIndexPathからrowプロパティで、その行番号をoldRowとして取得します。

セルの文字列として、配列listからrowの文字列をobjectAtIndex:メソッドで取得します。

旧版の本書ではテキストの取得にtextプロパティを使用していますが、iOS 3.0以降は非推奨なので、代わりにtextLabeltextプロパティを使用しています。

アクセサリはaccessoryTypeプロパティで設定しますが条件分岐がなされており、現在表示しようとしている行番号rowと前回選択された行番号が同じで、且つ前回選択された行がnilでは無い場合にはチェックマーク(UITableViewCellAccessoryCheckmark)を付け、そうでない場合は何も付けない(UITableViewCellAccessoryNone)となっています。

lastIndexPathはアプリケーション起動時(つまり行が未選択の時)にはnilであり、その場合oldRowには0が入ってしまうため、『lastIndexPath != nil』を設定して起動時に最初の行(行番号0)にチェックマークが付くことを防いでいます。

9)不要なTable view data sourceメソッド

Table view data sourceのtableView:canEditRowAtIndexPath:tableView:commitEditingStyle:forRowAtIndexPath:tableView:moveRowAtIndexPath:toIndexPath:tableView:canMoveRowAtIndexPath:の4つは今回使用しないので削除します。

10)tableView:didSelectRowAtIndexPath:の実装

tableView:didSelectRowAtIndexPath:では、今選択された行と前回選択された行を比較し、異なる場合は双方の行のアクセサリを変更する処理を行います。

今選択された行は、引数indexPathからrowプロパティでnewRowに行番号を格納します。

前回選択された行oldRowは、lastIndexPathからrowプロパティで行番号を取得しますが、行が未選択(lasrIndexPathがnil)の場合は(存在し得ない数値として)-1と設定します。

続いてif文でnewRowとoldRowを判定し、同じ場合は処理をせず、異なる場合はチェックマークを付け替える処理を行います。

それぞれcellForRowAtIndexPath:メソッドでインデックスパスからセルを取得し、accessoryTypeプロパティで、今の選択行にはチェックマークを付け、前回の選択行はアクセサリ無しに設定します。

そして今の選択行をlastIndexPathとして保持し、次回に使用します。

最後にdeselectRowAtIndexPath:animated:で行の選択状態を解除します。


cellForRowAtIndexPath:

- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath

指定したインデックスパスにあるテーブルセルを返します。

戻り値はテーブルのセルを表すオブジェクトで、セルが表示されない、またはインデックスパスが範囲外の場合はnilを返します。

indexPath:レシーバ内の行の場所を指すインデックスパスを指定します。

11)viewDidUnloadの実装

旧版の本書ではviewDidUnloadは記述されていませんが、サンプルコードにもあるように2つのプロパティの所有権放棄を行います。

12)deallocの実装

deallocでは本書やサンプルコードと同じく、2つのプロパティの解放を行っています。


●ルートビューコントローラFirstLevelViewController.mの編集

ルートビューのテーブルにサブビューCheck Oneの項目を追加します。
(太字が追加した部分)

#import "FirstLevelViewController.h"
#import "SecondLevelViewController.h"
#import "DisclosureButtonController.h"
#import "CheckListController.h"

@implementation FirstLevelViewController

@synthesize controllers;

#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];

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;

    self.title = @"First Level";
    NSMutableArray *array = [[NSMutableArray alloc] init];

    // Disclosure Button
    DisclosureButtonController *disclosureButtonController = [[DisclosureButtonController alloc] initWithStyle:UITableViewStylePlain];
    disclosureButtonController.title = @"Disclosure Buttons";
    disclosureButtonController.rowImage = [UIImage imageNamed:@"disclosureButtonControllerIcon.png"];
    [array addObject:disclosureButtonController];
    [disclosureButtonController release];

    // Check List
    CheckListController *checkListController = [[CheckListController alloc] initWithStyle:UITableViewStylePlain];
    checkListController.title = @"Check One";
    checkListController.rowImage = [UIImage imageNamed:@"checkmarkControllerIcon.png"];
    [array addObject:checkListController];
    [checkListController release];


    self.controllers = array;
    [array release];
}

(後略)

1032

内容はDisclosure Buttonsのものと同じです。

まずサブビューのヘッダファイルCheckListController.hをインポートします。

そしてviewDidLoadメソッドで、Disclosure Buttonの設定の下に以下の内容を追加します。

CheckListControllerのインスタンスを(スーパークラスであるUITableViewControllerクラスの)initWithStyle:メソッドを使用しプレーンスタイル(UITableViewStylePlain)で生成・初期化します。

このインスタンスはaddObject:で可変配列arrayに、そして配列プロパティcontrollersに格納され、tableView:didSelectRowAtIndexPath:でナビゲーションコントローラにプッシュされます。

titleプロパティに設定した文字列、rowImageプロパティで設定した画像ファイルは、tableView:cellForRowAtIndexPath:でセルの表示に使用されます。


●実行

この段階で『ビルドと実行』を行うと、ルートビューのテーブルにCheck Oneのセルが追加されます。

1034

行を選択するとサブビューに移行します。

1035

初回起動時にはどの行も選択されていません。

1029

ある行をタップするとチェックマークが付き、別の行をタップすると前の行のチェックマークが消えて、新しい行にチェックマークが付きます。

チェックマークを付けた選択行の情報はアプリケーションが動作している間、即ちルートビューに戻って他のサブビューを閲覧しても保持されます。

ただし(バックグラウンドでの動作も切って)アプリケーションを終了させた場合、選択行の情報は破棄され、未選択の状態に戻ります。



参考文献

UITableView Class Reference

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

商品詳細を見る






QuietControl 30 wireless headphones
0 Comments
Leave a comment
管理者にだけ表示を許可する
Top
0 Trackbacks
Top
Calendar
10 | 2017/11 | 12
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

QuietControl 30 wireless headphones
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