iPhoneのMVC:「モデル」(パート1)

CocoaTouchはMVCパラダイムに着目して最初から構築さています。 ユーザーのほとんどすべてのテンプレート、ビュー、およびそれらのコントローラーの準備ができています。 重要なクラスはUIViewUIViewControllerです。 多くの場合、「 UIView 」メソッドは単独で適用できます-IBエディターの一般的な「 UIView 」にユーザーインターフェイス要素を追加します。 独自の関数を作成するには、サブクラスを「 UIViewController 」に追加します。 IBOutlet修飾子を使用すると、ユーザーインターフェイス要素をビューに関連付けて、それらへのアクセスを提供できます。

しかし、 「モデル」の概念はどうですか? 私は彼に関する情報を実際には見つけませんでした。 プログラミングレッスンでは、コントローラーにコードを直接入力してモデルを操作することを好みません。

実装で良い結果が得られたので、ここで議論と評価のために提案します。 簡単に説明します。 私のモデルの「 NSObject 」を拡張する「 Singleton 」クラスを作成しています。 次に、キー/変数を監視することにより、更新について学習します。 誰かが「 Flex 」で作業しなければならなかった場合、これは「 Cairngorm 」の「 ModelLocator 」によく似ています。

最初に、いくつかのビューを持つプロジェクトを作成します。 それらの1つは、ユーザーが値を変更できるようにします。 この値はモデルに設定され、その結果、別のビューで変更がトリガーされます。 この目的には、「 ユーティリティアプリケーション 」テンプレートが非常に適しています。 そのため、それに基づいてプロジェクトを作成し、「 MVC 」という名前を割り当てます。 (原則として、名前は何でも構いませんが、レッスンでの作業の都合上、複製する方が良いです。)

結果は次のようになります。

mvc_01

ご覧のとおり、「 メインビュー 」および「 フリップサイドビュー 」というオブジェクトがあり、どちらにもコントローラーとnibファイルがあります 。 プロジェクトを実行し、コードに慣れます。 私は主にモデルに関するポイントに集中し、残りはすでに明らかであると信じています。

次に、ビューにラベルとテキストボックスを追加します。 ラベル-「 メインビュー 」のフィールド、「 フリップサイドビュー 」のフィールド。 最初に、 outletのような変数を見てみましょう。

ファイル「 MainViewController.h 」は、次のようになります。

#import

@ interface MainViewController : UIViewController {
UILabel *label;
}

@property (nonatomic, retain) IBOutlet UILabel *label;
@end
[/cc]

"<strong>MainViewController.m</strong>" — :

[cc lang= "objc" ]
#import "MainViewController.h"
#import "MainView.h"

@implementation MainViewController
@synthesize label;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
//
}
return self;
}

/*
// viewDidLoad , , nib-.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/

/*
// , .
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/

- ( void )didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // , superview
// ,
}

- ( void )dealloc {
[label release];
[super dealloc];
}

@end


* This source code was highlighted with Source Code Highlighter .


同様に、ファイル「 FlipsideViewController.h 」に変数「 textField 」と「 IBAction 」を追加します。これにより、フィールド内のテキストの変更について通知されます

#import

@ interface FlipsideViewController : UIViewController {
UITextField *textField;
}

@property (nonatomic, retain) IBOutlet UITextField *textField;

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


* This source code was highlighted with Source Code Highlighter .


およびFlipsideViewController.m:

#import "FlipsideViewController.h"

@implementation FlipsideViewController
@synthesize textField;

- ( void )viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
}

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

- ( void )didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}

- (IBAction)textChanged:(id)sender
{

}

- ( void )dealloc {
[textField release];
[super dealloc];
}

@end


* This source code was highlighted with Source Code Highlighter .


これで、「 MainView.xib 」を開き 、「 UILabel 」をビューに追加できます。 ファイルのOwnerオブジェクト(クラス " MainViewController ")からコネクタラインをラベルにドラッグし、対応する変数にリンクします。

mvc_02mvc_03

FlipsideView.xib 」ファイルで「 UITextField 」要素を追加し、「 File's Owner 」(「 FlipsideViewController 」)の「 textField 」変数に関連付けて、同じ操作を行います。 IBActiontextChanged 」をテキストフィールドの「 editingChanged 」イベントに添付します。

mvc_04mvc_05

この時点で、おそらく、ユーザーがフィールドにテキストを入力し、ラベルに表示することがユーザーの目標であることは既に明らかになっています。 しかし、これらはコントローラーを備えた2つの別個のビューですか? それらの間の相互作用を確立する方法は? もちろん、モデルを使用します。 両方のnibファイルを保存したらIBエディターを閉じてXcodeに戻ります

モデル。


モデルを作成します。 プロジェクトに新しいファイルを追加します-「 NSObject 」のサブクラス。 クラスに「 Model 」という名前を付けます。 私が「 シングルトン 」テンプレートの熱烈なファンだったわけではありません。 私は彼らの欠点を十分に認識しており、時には不当に使用されることもあると信じていますが、プラグマティストとして、この場合、これは許容できる選択肢であると確信しています。

シングルトンの考え方に耐えられない人は、「 RootViewController 」でモデルを作成し、各View Controllerにインスタンスを渡すことができます。 うまくいくはずです。 [ CocoaWithLove.com ]の機知に富んだSynthesizeSingletonファイルを使用して別の方法を選択します。 ところで、そこでシングルトンに対する態度を表現できます。

次に、ファイル「 SynthesizeSingleton.h 」をプロジェクトに追加し、シングルトンにしたいクラスの実装でマクロ「 SYNTHESIZE_SINGLETON_FOR_CLASS 」を呼び出します。

インターフェイスファイルで静的メソッド「 sharedModel 」を宣言しない限り、このマクロを操作すると常に警告がトリガーされることに気付きました。

モデルには、1つのプロパティ、テキスト、およびカスタムアクセサーも必要です。 「 Model.h 」ファイルは次のとおりです。

#import <Foundation/Foundation.h>

@ interface Model : NSObject {
NSString *text;
}

@property (nonatomic, retain) NSString *text;

+ (Model *)sharedModel;
@end


* This source code was highlighted with Source Code Highlighter .


そして、これが " Model.m "です:

#import "Model.h"
#import "SynthesizeSingleton.h"

@implementation Model

SYNTHESIZE_SINGLETON_FOR_CLASS(Model);

@synthesize text;

- (id) init
{
self = [super init];
if (self != nil) {
text = @"" ;
}
return self;
}

- ( void ) dealloc
{
[text release];
[super dealloc];
}

@end


* This source code was highlighted with Source Code Highlighter .


モデルの設定データ。


テキストフィールドのテキストを変更すると、「 FlipsideViewController 」コントローラーの「 textChanged 」メソッドが呼び出されます。 ここで、モデルのテキストプロパティに新しい値を割り当てることができます。

- (IBAction)textChanged:(id)sender
{
Model *model = [Model sharedModel];
model.text = textField.text;
}


* This source code was highlighted with Source Code Highlighter .


Model.h 」を「 FlipsideViewController.m 」にインポートしてください。

変更をコミットします。


主なアイデアは、モデルがいつ変更されるかを伝えるのに十分です。 これは、キー/値を観察することで実行できます。 したがって、オブジェクトの特定のプロパティに対してオブザーバーを構成し、オブザーバーが変更された場合にすぐにメソッドを呼び出すようにする必要があります。 ここで、シングルトンモデルクラスのテキストプロパティがいつ変更されるかを知る必要があります。 この目的のために、「 MainViewController 」クラスの「 initWithNibNameメソッドを使用します 。 これは次のようなものです。

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
//
Model *model = [Model sharedModel];
[model addObserver:self forKeyPath: @"text" options:NSKeyValueObservingOptionNew context:nil];
}
return self;
}


* This source code was highlighted with Source Code Highlighter .


まず、「 Model 」シングルトンへのリンクを取得し、次に「 addObserver:forKeyPath:options:context 」メソッドを呼び出します 。 ブラウザは「 self 」、クラスは「 MainViewController」です。 "KeyPath "-監視するプロパティ、文字のテキスト文字列。 これらのオプションにより、変更するデータを指定できます。 プロパティの新しい値は次のとおりです。 必要に応じて、初期値、廃止された値、以前の値、およびそれらの任意の組み合わせを要求できます。 コンテキストとして、ゼロを追加できます。

ブラウザによって監視される変更は、この場合にのみ呼び出される特別なメソッドを導入する必要があります。 彼の署名は次のとおりです。

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)オブジェクトの変更:(NSDictionary *)コンテキストの変更:(void *)コンテキスト

keyPath 」は文字列としてのプロパティの名前、「 object 」はこのプロパティ(この場合はモデル)を所有するオブジェクト、「 change 」は指定された古い値、新しい値、前の値、初期値を格納する辞書、「 contex t」は任意コンテキスト関連コンテキスト(ゼロがあります)。

モデルの複数のプロパティを一度に観察することが目標である場合、どのプロパティが変更されたかを報告する「 keyPath 」を持つ選択演算子が必要になります。 私たちはただテキストを見ているだけですが、それが私たちがそれを知っている理由です。 辞書の設定を変更するリクエストを送信することにより、新しいプロパティ値を取得できます。 別のオプションは、モデルテキストプロパティに直接アクセスすることですが、以下のデータを使用します。

- ( void )observeValueForKeyPath:(NSString *)keyPath ofObject:(id) object change:(NSDictionary *)change context:( void *)context
{
label.text = [change valueForKey: @"new" ];
}


* This source code was highlighted with Source Code Highlighter .


ここでは、変更のオブジェクトに「新しい」値(これはすべての内容になります。これは指定したオプションであるためです)。 結果を「 label.text 」に割り当てます-これで完了です。

まあ、それだけです。 データを保存し、イベントが変化したときにイベントを報告するモデルがあります。 注: Cocoa MVCパラダイムでは、通常、ビューはモデルに直接従いません。 View Controllerはこれを行い、ビューに何をすべきかを伝えます。 このアプローチには法律の地位はありませんが、一般的に受け入れられています。

キー/値を観察する代わりに「 NSNotification 」を使用して、別の概念を考え出しました 。 これが本当に価値のある代替手段であるか、その長所と短所が何であるかはわかりません。

上記の例には実用的な価値はありません-これは単なる例です。 実際のアプリケーションでは、たとえば、すべての編集イベントを修正するのではなく、「 フリップサイド 」ビューを閉じた後にのみイベントに移動するなど、さまざまな方法で行うことができます。 しかし、レッスンの目標はこれではなく、モデルの視覚的な実装(少なくとも1つの方法)でした。

ありがとう、成功!

レッスンのソースコードはこちらからダウンロードできます

Source: https://habr.com/ru/post/J70481/


All Articles