ラベル オブジェクト分割 の投稿を表示しています。 すべての投稿を表示
ラベル オブジェクト分割 の投稿を表示しています。 すべての投稿を表示

2011年7月16日土曜日

データソースクラスを分離する

iPhoneAppに限らず、基本はビュー、ビューコントローラ、データソースの3層構造でシステムを作るのは常識だ。

しかしながら、iPhone Appの解説本を見ても、Appleが提供しているひな形をベースにUIKitを無秩序に組み込むプログラム例が多く、あまり参考にならない。

とりあえず、基本となるplistでのファイル格納クラスを分割してみた。なお、初期のロード時にバージョンアップメッセージを表示し、ユーザに新しい機能を伝える必要があるため、バージョン情報を保管し、データロード時にバージョンが異なる場合、新機能の通知をロードメソッドに実装する。

*.h
NSObjectから派生して作ればいい。
基本は、母体となるメインのビューコントローラから参照するアドレスまたはポインターをプロパティ宣言する。
また、データソースのクラスであるため永続化のためのsaveメソッド、及び読み出すためのloadメソッドを宣言する。


#import <Foundation/Foundation.h>
@interface SetupData : NSObject {
NSString *adress;
NSString *subject;
double VAT;
BOOL soundON;
double volume;
}
@property (nonatomic, retain) NSString *adress;
@property (nonatomic, retain) NSString *subject;
@property double VAT;
@property BOOL soundON;
@property double volume;

-(void)load;
-(void)save;


@end


*.m
プロパティ宣言をしたクラスをシンセサイズし、外部に提供するloadメソッド及びsaveメソッドを実装する。

なお、ファイルのディレクトリを取得するメソッドも内部用に実装したほうがいい。


#import "SetupData.h"
ここでファイル名を定義する。
#define settingFileName @"setting.plist"


@implementation SetupData
@synthesize adress, subject, VAT, soundON, volume;


共通的なメソッドとして、ディレクトリを取得するメソッドを実施しておく。

- (NSString *)settingFilePath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:settingFileName];
}


loadメソッドを実装する。
-(void) load{
NSString *VATstring = [[NSString alloc] init];
NSNumber *number = [[NSNumber alloc]init];
NSNumber *volumeSwitch = [[NSNumber alloc] init];
NSString *version = [[NSString alloc]init];

NSString *notSettingBundle = [self settingFilePath];
NSDictionary *dictionary = [[NSDictionary alloc]initWithContentsOfFile:notSettingBundle];


ファイルがない場合、dictionaryはnilとなる。ファイルがある場合のみディクショナリの内容を読み込む。

if (dictionary != nil) {
adress = [[dictionary objectForKey:@"adress"]retain];
subject = [[dictionary objectForKey:@"subject"]retain];
VATstring = [[dictionary objectForKey:@"VAT"]retain];
number =  [[dictionary objectForKey:@"volume"]retain];
volumeSwitch = [[dictionary objectForKey:@"switch"]retain];
version = [[dictionary objectForKey:@"version"]retain];
}

ファイルがない場合はデフォルト値を実装する。
if (adress == nil) {
adress = [[NSString alloc] initWithString:@""];
}
if (subject == nil) {
subject  = [[NSString alloc] initWithString:@"Roll Paper Calculator"];
}

数値の場合、NSNumberもしくはNSStirngで格納しなくてはならないため、読み込み時に逆変換する。
if (VATstring == nil) {
VAT = 0.05;
}
else {
VAT = (double)[VATstring intValue];
VAT = VAT / 100.0;
}
if (number == nil) {
volume = 0.2;
}
else {
volume = [number doubleValue];
}
if (volumeSwitch == nil) {
soundON = YES;
}
else {
soundON = [volumeSwitch boolValue];
}

新規導入時、もしくはバージョンアップ時のメッセージを表示する。初回以降は表示されない。
if (version == nil || ![version isEqualToString:@"4.18"] ){
  UIAlertView *alertView = [[UIAlertView alloc
initWithTitle:@"Version 4.18"
message:@"Internal Design Renewal. Save volume off state whent App end"            
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
  [alertView show];
  [alertView release];
}


一時オブジェクトを解放する。
[version release];
[number release];
[VATstring release];
[dictionary release];

}

-(void)save{

保存のためのディクショナリを確保する。
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc]init];
[dictionary setObject:adress forKey:@"adress"];
[dictionary setObject:subject forKey:@"subject"];

数値データはNSNumberまたはNSStringに変換してディクショナリに格納する。
[dictionary setObject:[[NSNumber numberWithInt:(int)(VAT*100.0)] stringValue] forKey:@"VAT"];
[dictionary setObject:[NSNumber numberWithDouble: volume] forKey:@"volume"];
[dictionary setObject:[NSNumber numberWithBool: soundON] forKey:@"switch"];
[dictionary setObject:@"4.18" forKey:@"version"];

ディクショナリを所定のファイルに格納する。
[dictionary writeToFile:[self settingFilePath] atomically:YES];

ディクショナリを解放する。
[dictionary release];
}

@end



以上です。

2011年6月26日日曜日

UIViewControllerを分割する

UIが複雑になってくるとUIViewControllerが非常に巨大になってきて困っている方も多いのではないでしょうか。
そのような場合には以下の方法があります。
・再利用可能なUIView部品を作成し、コントロールの一部を組み込む。
・UIViewControllerをオブジェクト分割する

再利用可能なUIView部品の作成方法については下記にあるため参考とされたい。ここでのContrntViewは恐らくIBOutletで本体のViewに接続されてたものと思われます。
http://www.awaresoft.jp/development/35-iphone-app/93-create-reusable-ui-with-interface-builder.html

ここでは、後者、すなわち、Nibファイルは一個のままでUIViewControllerを分割する方法を記録します。

−−−*.h−−−−

まず分割したいオブジェクトのヘッダファイルを記述します。

#import
@interface decimalMode : NSObject {
NSString *now;
UIButton *zeroButton, *add2Button , *twoButton, *FButton;

}
@property (nonatomic, retain) NSString *now;
@property (nonatomic, retain) IBOutlet UIButton *zeroButton, *add2Button , *twoButton, *FButton;
-(void) zero ;
-(void) add2 ;
-(void) two ;
-(void) flowting ;
@end

オブジェクト分割時の基本は、オブジェクトの操作をメソッド化(ここではzero- flowting)し、状態の読み取りをプロパティ化することにあります。
そして、操作するUIをアウトレットか化します(ここでは、zeroButton-FButton)。


−−−*.m−−−−


つぎに、実行ファイル(*.m)を記載しますが、ここでの注意点はNibが起動した際に、このオブジェクトを包含して起動してもらうことです。そのために以下のコードを付加します。


-(void) awakeFromNib {
    [super awakeFromNib];
}

あとは普通にzeroからflowtingまでの処理(ここではUIButtonの色操作)を実装します。


−−−*.xib−−−−

次にNibファイルにLibraryからオブジェクトを導入します。
この際、Command+F4でオブジェクトを追加したオブジェクトに変更します。
そうするとIBOutletで指定した足がCommand+F2とかで見ることができると思います。
この足を、マウス右クリック等で、該当するUI部品(この場合はボタン)と連接します。
ーーー*.UIViewController.hーーー

さらにUIViewContrller.hに、当該オブジェクトのヘッダファイルをインポートし、
さらに当該オブジェクトをアウトレット指定します。


#import "decimalMode.h"
@interface makidenViewController : UIViewController{
      ....


decimalMode *DecimalMode;
}

....
@property (nonatomic, retain) IBOutlet decimalMode *DecimalMode;

@end



さらに、InterfaceBuilderに戻り、File's OWnerから実装したオブジェクト(ここではDecimalMode)
に向けて、マウス右クリックしながらのドラックなどで、このアウトレットをオブジェクトとリンクします


ーーー*.UIViewController.m ーーー

お決まりの@synthesizeや=nil, [... release]を実装さえすれば、
オブジェクトが自動でロードされるため、初期化処理なしでいきなり当該オブジェクトを呼び出すことができます。

[DecimalMode zero];


以上です。やったね。

2011年6月22日水曜日

クラスを分割する。

プログラムの機能を増やして行くと、1つのオブジェクト内のコードが膨大となってしまい、メンテナンスがしずらくなります。この解決のためには、やはりオブジェクトとして意味のある単位で分割してまとめるが王道です。

小さく切出す場合は、UIViewの派生オブジェクトとして作るか、ビューコントローラの一部を切出してコントロールするかになるかと思います。しかしながら後者についてはAppleとして推奨しないようです。

実装する際は、これまでグローバル変数などで管理していた状態等を隠蔽しながら実装します。
そのため、状態遷移のためのメソッドを切出したオブジェクトに実装します。
また、呼び出しもとのオブジェクトの状態を確認するためには、プロパティとして状態変数を表に出します。

その結果、ヘッダファイルとしては以下のようなものとなります。


@interface decimalMode : UIView {
NSString *now;
}
// 状態をプロパティ化し、読み出し可能とする ex decimalMode.now
@property (nonatomic, retain) NSString *now;
// 状態を変化させる操作をインスタンス化する。インスタンスの名称には操作の意味を持たせる。
-(void) zero ;
-(void) add2 ;
-(void) two ;
-(void) flowting ;

-(IBAction) decimalPointButtonPressed:(id)sender;
@end