Pickers(8)~CustomPicker(1)

2010. 10. 09
5つ目となる最後のビューは、画像を要素とした5つのホイールで構成されるスロットマシンを作成します。

792

ホイールはこれまでのようにフリックで回転させるのではなく、『Spin!』というタイトルのRound Rect Buttonをタップすることで各ホイールがランダムに回転します。

各ホイールには6つの画像オブジェクトがあり、その内同じ画像が3つ以上のホイールが連なると勝ちとなります。


●ヘッダファイルの編集

CustomPickerViewController.hを開き、ピッカーとラベル、ボタンのアウトレットと5つのホイールのプロパティ、そしてホイールを回転させるアクションメソッドを定義します。
(太字が追加した部分)

#import <UIKit/UIKit.h>

@interface CustomPickerViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource> {
    UIPickerView *picker;
    UILabel *winLabel;
    UIButton *button;
    NSArray *column1;
    NSArray *column2;
    NSArray *column3;
    NSArray *column4;
    NSArray *column5;

}

@property (nonatomic, retain) IBOutlet UIPickerView *picker;
@property (nonatomic, retain) IBOutlet UILabel *winLabel;
@property (nonatomic, retain) IBOutlet UIButton *button;
@property (nonatomic, retain) NSArray *column1;
@property (nonatomic, retain) NSArray *column2;
@property (nonatomic, retain) NSArray *column3;
@property (nonatomic, retain) NSArray *column4;
@property (nonatomic, retain) NSArray *column5;

- (IBAction)spin;


@end

863


●nibファイルの編集

基本的にはこれまでの~ComponentPickerView.xibと同じです。

CustomPickerView.xibファイルをダブルクリックしてInterface Builderで開き、ピッカーと(おまけで背景画像と)ボタンを設置する所までは同じです。

864

これにホイール上の同じ画像が3つ以上揃った時に表示するラベルを設置します。

LibraryウィンドウからViewウィンドウへLabelをドラッグ&ドロップし、ラベルのTextを『Win!』とします。

865

そして見栄えをよくするために、InspectorウィンドウのAttributesタブを開き、

・『Layout』の『Alignment』を中央揃えに
・『Font』を『Helvetica Bold, 36.0』に
・『Text』の色を『Blue』に

などと調整します。

866

867

フォントの種類やサイズ、色は好みで構いませんが、Interface Builderで表示されるフォントの種類の候補はMac用のもので、iOSデバイスには搭載されていないフォントがあるので注意してください。

実機に搭載されているフォントは、KanotomoさんのTypeface(以前のCédilleからアプリ名が変わりました)などで確認してください。
(iPhone 3G、OS 3.1.3で確認した時の一覧が『『基礎からのiPhone SDK改訂版』を始めてみた』にあります)

調整が済んだら『Text』に入力されている『Win!』の文字列を削除し、通常時に表示されないよう空にしておきます。

それと、ピッカーを直接手動で動かされると不都合なので、『Interaction』の『User Interaction Enable』のチェックを外しておきます。

868

後はピッカーやボタン、ラベルを接続するだけです。

869

870


●イメージリソースの追加

ホイールに表示する6つの画像ファイルは、Apress社のサイト内のサンプルコードの『07 Pickers/Custom Picker Images』をコピーして使うようになっています。

ここでは同サイズ(52x46ピクセル)で背景色を透明にしたものを用意し、Resourcesに追加しています。

871


●ソースファイルの編集

CustomPickerViewController.mを開き、プロパティとアクション、ピッカーのデリゲートとデータソースの実装を行います。
(太字が追加・変更した部分)

#import "CustomPickerViewController.h"

@implementation CustomPickerViewController

@synthesize picker;
@synthesize winLabel;
@synthesize button;
@synthesize column1;
@synthesize column2;
@synthesize column3;
@synthesize column4;
@synthesize column5;

- (IBAction)spin {
    BOOL win = NO;
    int numInRow = 1;
    int lastVal = -1;
    for (int i = 0; i < 5; i++) {
        int newValue = random() % [self.column1 count];
        if (newValue == lastVal)
            numInRow++;
        else
            numInRow = 1;
        lastVal = newValue;
        [picker selectRow:newValue inComponent:i animated:YES];
        [picker reloadComponent:i];
        if (numInRow >= 3)
            win = YES;
    }
    if (win)
        winLabel.text = @"WIN!";
    else
        winLabel.text = @"";
}


// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {

        // Custom initialization
    }
    return self;
}


/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/


// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];
    UIImage *minna = [UIImage imageNamed:@"wolf.png"];
    UIImage *yoshika = [UIImage imageNamed:@"mameshiba.png"];
    UIImage *lynette = [UIImage imageNamed:@"scottishfold.png"];
    UIImage *mio = [UIImage imageNamed:@"doberman.png"];
    UIImage *gertrud = [UIImage imageNamed:@"german-pointer.png"];
    UIImage *sw = [UIImage imageNamed:@"sw.png"];
    for (int i = 1; i <= 5; i++) {
        UIImageView *minnaView = [[UIImageView alloc] initWithImage:minna];
        UIImageView *yoshikaView = [[UIImageView alloc] initWithImage:yoshika];
        UIImageView *lynetteView = [[UIImageView alloc] initWithImage:lynette];
        UIImageView *mioView = [[UIImageView alloc] initWithImage:mio];
        UIImageView *gertrudView = [[UIImageView alloc] initWithImage:gertrud];
        UIImageView *swView = [[UIImageView alloc] initWithImage:sw];
        NSArray *imageViewArray = [[NSArray alloc] initWithObjects:minnaView, yoshikaView,
        lynetteView, mioView, gertrudView, swView, nil];
        NSString *fieldName = [[NSString alloc] initWithFormat:@"column%d", i];
        [self setValue:imageViewArray forKey:fieldName];
        [fieldName release];
        [imageViewArray release];
        [minnaView release];
        [yoshikaView release];
        [lynetteView release];
        [mioView release];
        [gertrudView release];
        [swView release];
    }
    srandom(time(NULL));
}


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


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

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

- (void)viewDidUnload {
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;

}

- (void)dealloc {
    [picker release];
    [winLabel release];
    [button release];
    [column1 release];
    [column2 release];
    [column3 release];
    [column4 release];
    [column5 release];

    [super dealloc];
}

#pragma mark -
#pragma mark Picker Data Source Methods

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
    return 5;
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    return [self.column1 count];
}

#pragma mark Picker Delegate Methods

- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
    NSString *arrayName = [[NSString alloc] initWithFormat:@"column%d", component+1];
    NSArray *array = [self valueForKey:arrayName];
    return [array objectAtIndex:row];
}


@end

872

プロパティの実装とdeallocでの解放、initWithNibName:bundle:、viewDidLoad、shouldAutorotateToInterfaceOrientation:メソッドのコメントアウト解除はこれまでと同様です。


・spinアクションメソッド

アクションメソッドspinは、Round Rect Buttonをタップすると呼び出されます。

winは画像が3つ以上揃ったことを判定するブール値で、初期値はNOにしてあり、3つ以上連続で揃った場合にYESになります。

numInRowは同じ画像が何個連続で揃っているかを表す値で、初期値は1にしてあり、直前のホイールの画像と同じであれば加算され、違った場合は1に戻ります。

lastValは直前のホイールで選択された画像の値を保持する値で、初期値(ホイールの画像がまだ選択されていない時の値)は-1にしてあり、要素数分の値(ここでは6つの要素なので0~5の値)を取ります。

forループは、各ホイールの選択行をランダムで決定し、選択した行と直前のホイールで選択された行の値を比較して、同じならnumInRowの値を加算、異なれば1にリセットし、3つ以上連続で揃えば判定値winをYESにしています。
(途中に選択した行の決定とアニメーションによるホイールの再描画も行っています)

判定値がYESの時にはラベルを『WIN!』と表示し、そうでない時は非表示(ラベルのテキストを空)にします。

randomはstdlib.hで定義されているlong型の乱数を発生させる関数です。

この関数自体は毎回同じ乱数を出力するので、viewDidLoadメソッド内でsrandom関数で乱数の種を作っています。
(time関数で現在時刻を種に使用)

乱数はcolumn1(左端のホイール)の要素数(ここでは6)で割っていますので、newValueに入る剰余は0~5の値になります。
(column1を固定で使用しているので、全ホイールの要素数が同じであることを前提にしています)


・viewDidLoadメソッド

5つのホイールにそれぞれ6つの画像配列を組み込むので文量は多いですが、内容は難しいものではありません。

最初にResourcesに追加したCustom Picker Imagesにある6つの画像ファイルを、imageNamed:メソッドでUIImageオブジェクトにしています。
(imageNamed:メソッドについては『スライドショーのメソッド(1)』を参照してください)

次にforループで各ホイールの初期化を行っています。

まずinitWithImage:メソッドでUIImageオブジェクトをUIImageViewオブジェクトにしています。
(ピッカーで表示するにはUIImageViewなどビューオブジェクトにする必要があるため)

UIImageViewオブジェクトにした画像は、initWithObjects:メソッドで画像配列にします。

そしてsetValue:forKey:メソッドでcolumn~プロパティに画像配列を設定しています。

setValue:forKey:によるキー値コーディングによって、プロパティcolumn1~5を一つ一つ実装せずに、プロパティ名を動的に生成して実装することができるということのようです。

その後、不要になった文字列や画像配列、UIImageViewオブジェクトを解放し、最後にspinアクションメソッドで使うランダム関数の種を作っています。


・initWithImage:
(UIImageVIewクラス)

- (id)initWithImage:(UIImage *)image

指定した画像で初期化したイメージビューオブジェクトを返します。

このメソッドは、指定した画像のサイズに合わせて、レシーバのフレームを調整します。

またデフォルトでは、イメージビューに対するユーザの操作を無効にしています。

image:イメージビューに表示する初期化画像を指定します。


・initWithObjects:
(NSArrayクラス)

- (id)initWithObjects:(id)firstObj, ...

引数リスト内に置かれたオブジェクトで、新規に割り当てた配列を初期化します。

返されるオブジェクトは、元のレシーバと異なる場合があります。

不変配列をこの方法で初期化した後、変更することはできません。

firstObj,...:コンマ区切りのオブジェクトのリストで、終端はnilになります。


・NSKeyValueCodingプロトコル

NSKeyValueCoding非形式プロトコルは、名前(またはキー)によって間接的にオブジェクトのプロパティにアクセスする、言い換えるとアクセサメソッドを介さずにインスタンス変数に直接アクセスできる手法を定義するものです。

したがって全てのオブジェクトのプロパティに一貫した方法でアクセスすることができます。

オブジェクトの値にアクセスする基本メソッドはsetValue:forKey:で、指定したキーで識別されたプロパティに値を設定することができ、またvalueForKey:は、指定したキーで識別されたプロパティの値を返します。

デフォルトの実装では、通常のオブジェクトによって実装されたアクセサメソッド(または必要であればインスタンス変数への直接アクセス)に使用します。


・setValue:forKey:
(NSKeyValueCodingプロトコル)

- (void)setValue:(id)value forKey:(NSString *)key

指定したキーと値を用いて、レシーバのプロパティを設定します。

(Discussionの項で、対一リレーションシップや対多リレーションシップについての説明があるのですが、『詳解 Objective-C 2.0』を読んでも理解できなかったので訳せませんでしたので、原文を載せておきます)

If key identifies a to-one relationship, relate the object specified by value to the receiver, unrelating the previously related object if there was one. Given a collection object and a key that identifies a to-many relationship, relate the objects contained in the collection to the receiver, unrelating previously related objects if there were any.
The search pattern that setValue:forKey: uses is described in Accessor Search Implementation Details in Key-Value Coding Programming Guide.

value:キーで識別されるプロパティの値を指定します。

key:レシーバのプロパティの内の一つの名前を指定します。


・データソースメソッド

numberOfComponentsInPickerView:でコンポーネント数を5に、pickerView:numberOfRowsInComponent:でコンポーネントの行数をcolumn1の要素数に設定しています。


・デリゲートメソッド

先のDependentComponentPickerで使っていたpickerView:viewForRow:forComponent:reusingView:メソッドを使用しています。

ここでもキー値コーディングを利用しており、ホイールのプロパティ名を動的に生成し、valueForKey:メソッドで配列を呼び出して、要素を返しています。


・valueForKey:
(NSKeyValueCodingプロトコル)

- (id)valueForKey:(NSString *)key

指定したキーで識別されたプロパティの値を返します。

key:レシーバのプロパティの内の一つの名前を指定します。


・実行結果

実行すると下図のようになります。

873

Spin!ボタンをタップする度に、5つのホイールがそれぞれランダムな値を取って回転します。

3つ以上のホイールが連続して同じ絵になればWIN!とラベルが表示されます。

本書で説明されているように、この時点では

・ホイールが回転したり、絵が揃った時の効果音が無く、物足りない
・Spin!ボタンが回転終了前に連続してタップできてしまう

などの問題がありますので、次回に改良します。



参考文献

UIImageView Class Reference

NSArray Class Reference

NSKeyValueCoding Protocol Reference

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

商品詳細を見る

詳解 Objective-C 2.0 第3版詳解 Objective-C 2.0 第3版
(2011/12/28)
荻原 剛志

商品詳細を見る






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