まずOOPで行うべきことは、オブジェクトのクラスを検討することです。 CocoaはMVC(Model、View、Controller)アーキテクチャを使用しているため、クラスはこれらの3つのカテゴリのいずれかに収まる必要があります。 CocoaはすでにNSTableViewクラスを提供していますので、モデルとコントローラを残すことができます。
あなたが取ることができるモデルクラスに多くの異なるアプローチがあります。
- あなたは
- 別々の配列で、xとyの値を保持している関数テーブルクラスを書くことができますが、関数テーブルを書くことができます(x、y)のペアの単一の配列を持つクラスです。
- この実装または以前の実装では、両方のアレンジメントをサポートするパブリックインターフェイスを提供することができます(つまり、与えられたxとx、yおよび(x、y)コレクション)。いくつかの実装の詳細は、テーブルビューをデータ(バインディング、または古いNSTableViewDataSource protocol)にどのように接続するかによって異なります。
- x値の配列を使用して、value transformerを作成することもできます。このアプローチでは、y値はモデルではなくテーブルビューに存在します。
- そしてそう
上のアプリケーションの要件を取るためにどのアプローチを決定します。最小限のコードしか必要としないため、バリュートランスの手法を紹介します。
コントローラの場合は、NSArrayController(NSTableViewで非常にうまくいく)に依存することも、自分でNSArrayControllerを作成することもできます。たとえば、NSMutableArrayをモデルとして使用し、配列の値を他の値にマップするコントローラを作成できます。このコントローラーは、定義したブロックまたは一部の関数クラスを使用してマッピングを実行できます。
ご覧のとおり、いくつかのオプションがあります。最小のコーディングを必要とするオプション、つまり、値トランスフォーマ、コントローラ用のNSArrayController、モデル用のNSMutableArray(値トランスフォーマも格納されたオブジェクトに格納されている)を使用します。以下では、コードは標準的な規則に従ってファイルに保存する必要があります。各インタフェースと実装は、クラスと同じ名前の別ファイルにあり、インタフェースの場合は ".h"、実装の場合は ".m"の拡張です。また、Cocoa/Cocoa.hや各クラス実装の独自のインターフェイスなど、共通のインポートステートメントについても気にしません。
まず、バリュートランスフォーマです。実際には抽象スーパークラスと具象サブクラスの2つがあります。この分離は、後で簡単に他の関数タイプを追加することができます。スーパークラスFunctionTransformer
は非常に簡単です。その基部からオーバーライドする必要があるすべて、NSValueTransformer
、変換された値のクラスを返すメソッドである、transformedValueClass
:transformedValue:
:
@interface FunctionTransformer : NSValueTransformer
+ (Class)transformedValueClass;
@end
@implementation Function
+ (Class)transformedValueClass {
return [NSNumber class];
}
@end
コンクリートサブクラス、LinearTransformer
は、値の変圧器の一次メソッドをオーバーライドする必要があります。線形変換は可逆であるため、reverseTransformedValue:
も提供します。また、傾きと切片の値のプロパティも必要です。
#import "FunctionTransformer.h"
@interface LinearTransformer : FunctionTransformer {
NSNumber *m_;
NSNumber *b_;
}
@property (nonatomic,retain) NSNumber *slope;
@property (nonatomic,retain) NSNumber *intercept;
+ (BOOL)allowsReverseTransformation;
-(id)init;
-(id)initWithSlope:(float)slope;
-(id)initWithIntercept:(float)intercept;
-(id)initWithSlope:(float)slope intercept:(float)intercept;
-(void)dealloc;
-(NSNumber*)transformedValue:(id)value;
-(NSNumber*)reverseTransformedValue:(id)value;
@end
@implementation LinearTransformer
@synthesize slope=m_, intercept=b_;
+(BOOL)allowsReverseTransformation {
return YES;
}
-(id)initWithSlope:(float)m intercept:(float)b {
if ((self = [super init])) {
m_ = [[NSNumber alloc] initWithFloat:m];
b_ = [[NSNumber alloc] initWithFloat:b];
}
return self;
}
-(id)init {
return [self initWithSlope:1.0 intercept:0.0];
}
-(id)initWithSlope:(float)slope {
return [self initWithSlope:slope intercept:0.0];
}
-(id)initWithIntercept:(float)intercept {
return [self initWithSlope:1.0 intercept:intercept];
}
-(void)dealloc {
[b release];
[m release];
[super dealloc];
}
-(NSNumber*)transformedValue:(id)value {
return [NSNumber numberWithFloat:([value floatValue] * [m floatValue] + [b floatValue])];
}
-(NSNumber*)reverseTransformedValue:(id)value {
return [NSNumber numberWithFloat:(([value floatValue] - [b floatValue])/[m floatValue])];
}
@end
あなたが傾きと切片を設定することができるように使用されるように登録する必要がLinearTransformer
特定。アプリケーションデリゲートはこのトランスフォーマーを(x値のコレクションと共に)所有することも、カスタムコントローラーを作成することもできます。私たちはx値と値トランスフォーマーを束ねたモデルクラスを書くつもりです。名前はFunctionTable
です。ファンクション・トランスを設定するには、トランスをバリュー・トランスとして登録するサブタスクが必要です(+setValueTransformer:forName:
を使用)。これは、関数トランスフォーマープロパティ(f
)に独自のセッター(setF:
)を提供する必要があることを意味します。デフォルトでは
#import "FunctionTransformer.h"
extern NSString* const kFunctionTransformer;
@interface FunctionTable : NSObject {
NSMutableArray *xs;
FunctionTransformer *f;
}
@property (nonatomic,retain) IBOutlet NSMutableArray *xs;
@property (nonatomic,retain) IBOutlet FunctionTransformer *f;
@end
// FunctionTable.m:
#import "LinearTransformer.h"
NSString* const kFunctionTransformer = @"Function Transformer";
@implementation FunctionTable
@synthesize xs, f;
-(id) init {
if ((self = [super init])) {
xs = [[NSMutableArray alloc] init];
self.f = [[LinearTransformer alloc] init];
[f release];
}
return self;
}
-(void)dealloc {
[f release];
[xs release];
[super dealloc];
}
-(void)setF:(FunctionTransformer *)func {
if (func != f) {
[f release];
f = [func retain];
[NSValueTransformer setValueTransformer:f forName:kFunctionTransformer];
}
}
@end
、FunctionTable
はLinearTransformer
を使用しています。別のものを使用する場合は、FunctionTables
のf
プロパティを設定するだけです。 Interface Builder (IB) by using bindingsでこれを行うことができます。この単純化された実装では、値トランスフォーマーは常に "Function Transformer"という名前で登録され、実際にはFunctionTable
に制限されています。より複雑なスキームは、を登録するときに使用される固有の変圧器名をすべてFunctionTable
に与えることです。すべてを設定するには
:
- は、IBにおけるアプリケーションのメインウィンドウのペン先を開きます。
- NSArrayControllerとFunctionTable(およびもしあればカスタムアプリケーションデリゲート)をインスタンス化します。メインウィンドウに
- 、追加:要素を追加および削除する
- ボタン、
- 傾きと切片のラベルとNSTextFields、
- NSTableViewを。 「X」と「Y」(アプリが動作するために必要ではない)
- がconnectionsを設定するには、テーブルヘッダーを設定
- :
- は追加&をお持ちのボタンがNSArrayControllerはの
add:
に送る削除し、 remove:
アクション。
- NSTextFieldsの値をFunctionTablesの
f.slope
とf.intercept
のキーパスにバインドします。
- NSTableViewの両方の列の値をFunctionTablesの
xs
にバインドします。
- 第2列の値トランスフォーマーを「機能トランスフォーマー」に設定する
- NSArrayControllerのコンテンツ配列をFunctionTableの
xs
キーにバインドします。
- アプリデリゲートがある場合は、File's Ownerの
delegate
店舗に接続します。
ビルドして実行します。追加ボタンと削除ボタンを使用して、テーブルへの行の追加と削除を行うことができます。行の "x"と "y"列を編集することができます(後者はreverseTransformedValue:
のおかげです)。 「x」または「y」のいずれかの列で並べ替えることができます。スロープとインターセプトを変更することはできますが、行を個別に選択しない限り、表内の更新を気付かないでしょう。
高度なトピック
テーブルビュー更新問題を解決するために、我々は別の上の変化への1つのオブジェクトの(FunctionTransformer
)プロパティ(FunctionTable
)特性に変更を伝播する必要があります。関数トランスのプロパティがFunctionTable
observeに変更され、FunctionTable
にそのようなプロパティが変更されたことが通知されると、xs
プロパティが変更されたことを通知します(これは少し悪いです.hasn '実際に変更されました)。これは少し魔法のようになるので、私と一緒に熊。
オブジェクトは、他のオブジェクトのKVOメソッドaddObserver:forKeyPath:options:context:
を使用して別のオブジェクトの変更をサブスクライブし、サブスクライブはremoveObserver:forKeyPath:
を使用してサブスクライブしません。これらのメソッドは、呼び出されるだけで、書き込まれる必要はありません。通知はオブジェクトのobserveValueForKeyPath:ofObject:change:context:
メソッドで処理されるため、このメソッドを記述する必要があります。最後に、オブジェクトはwillChangeValueForKey:
とdidChangeValueForKey:
を呼び出すことによって独自の通知を送信できます。コレクションの一部のみが変更されたという通知を送信する他の方法もありますが、ここでは使用しません。
私たちのFunctionTable
は、変更サブスクリプションとサブスクリプションを処理することができますが、それでは、トランスフォーマーのどのプロパティを観察するのか知っていなければなりません。あなたはオブザーバーをサブスクライブして解除する各具体的な機能の変圧器にメソッドを追加することができます。
@implementation LinearTransformer
...
-(void)addObserver:(NSObject *)observer
options:(NSKeyValueObservingOptions)options
context:(void *)context
{
[self addObserver:observer
forKeyPath:@"slope"
options:options
context:context];
[self addObserver:observer
forKeyPath:@"intercept"
options:options
context:context];
}
-(void)removeObserver:(id)observer {
[self removeObserver:observer forKeyPath:@"slope"];
[self removeObserver:observer forKeyPath:@"intercept"];
}
@end
しかし、これは、それぞれの方法で、それぞれの具体的な機能の変圧器間のコードの繰り返しの公平なビットが必要になります。いくつかの魔法の使い方(reflectionとclosuresを、またはそれらはObjective-Cで呼ばれているように、blocks([2]))(彼らは&退会をサブスクライブするためKVOのメソッドと機能的に類似しているとして、addObserver:options:context:
とremoveObserver:
という名前)、私たちは、メソッドを追加することができますFunctionTransformer
に、またはNSObject
にさえなります。オブジェクト上のすべてのプロパティを観察することはFunctionTransformer
に限定されていないので、メソッドをNSObject
に追加します。これを行うには、OS X 10.6またはとOS X 10.5が必要です。
上から順に、FunctionTable
に変更してみましょう。関数トランスフォーマーを設定するときには、新しいサブタスクが追加されました。古いトランスフォーマーの変更を取り消し、新しいトランスフォーマーの変更をサブスクライブします。したがって、setF:
メソッドは、NSObject
の新しいメソッドを使用するように更新する必要があります。このメソッドは、「NSObject_Properties.h」という名前のヘッダーで定義されます。これらのメソッドの実装についてはまだ心配する必要はありません。私たちはここでそれらを使うことができ、後で適切な実装を書くという信念を持っています。 FunctionTable
は、変更通知(前述のobserveValueForKeyPath:ofObject:change:context:
)を処理する新しい方法も必要です。
#import "NSObject_Properties.h"
@interface FunctionTable
...
-(void)setF:(FunctionTransformer *)func {
if (func != f) {
[f removeObserver:self];
[f release];
f = [func retain];
[f addObserver:self
options:NSKeyValueObservingOptionPrior
context:NULL];
[NSValueTransformer setValueTransformer:f forName:kFunctionTransformer];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if (object == f) {
if ([[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
[self willChangeValueForKey:@"xs"];
} else {
[self didChangeValueForKey:@"xs"];
}
}
}
次に、NSObject
に新しいメソッドを書きます。変更をサブスクライブまたはサブスクライブ解除するメソッドは、オブジェクトのプロパティをループするので、ループを実行するヘルパーメソッドforEachProperty
が必要になります。このヘルパーメソッドは、各プロパティで呼び出したブロックを受け取ります。サブスクリプションとアンサブスクリプションのメソッドは単にforEachProperty
を呼び出し、サブスクリプションを追加または削除するために各プロパティに標準KVOメソッド(addObserver:forKeyPath:options:context:
とremoveObserver:forKeyPath:
)を呼び出すブロックを渡します。
'makeArrays'のように、' NSNumber'と 'float'の間で切り替えを行う必要はありません。 – outis