現在、iOS 用のフォーム コントローラーを書き直しています。モデルにバインドされたカスタム オブジェクトであり、フォーム フィールドの編集、前/次のフィールドへのジャンプ、カスタム キーボードの処理、データの検証などを処理します。
最初のバージョンは、フォーム値を格納するための plist に基づいており、フォーム コントローラーはすべてのデータ自体を保持していました。ここで、ストレージ (モデル) をフォームコントローラーから切り離したいので、KVO を使用することにしました。
簡単にするために、不在の期間を編集するように設計されたフォームがあると仮定しましょう。したがって、 と の 2 つのフィールドがleaveDate
ありreturnDate
ます。
私のモデルは次のとおりです。
@interface Absence
@property (strong, nonatomic) NSDate *leaveDate;
@property (strong, nonatomic) NSDate *returnDate;
@property (readonly, nonatomic) BOOL isValid;
@end
私のフォーム コントローラーには、model
このオブジェクトを指すプロパティがあります。
ユーザーが XIB の「休暇日」テキスト フィールドをタップすると、フォーム コントローラーが渡され、モデルの の現在の値に基づいて日付ピッカーが表示されますleaveDate
。ユーザーが別の日付を選択すると、フォーム コントローラーは を使用してモデルを更新しますsetValue:forKey:
。
プロパティはおよび( を使用して)のisValid
影響を受けると宣言され、フォーム コントローラーはこのプロパティの変更を監視するために登録され、その場で送信ボタンを有効/無効にします。leaveDate
returnDate
+keyPathsForValuesAffectingIsValid
この時点まで、すべてが魔法のように機能します。さて、ねじれた部分について:
モデルが開いているときに、フォーム コントローラーがモデルの変更を処理できるようにしたいと考えています。例: モデルに「欠席は少なくとも過去 3 日間は必要」というルールがあります。ユーザーが休暇日を変更すると、合計期間が 3 日を超えない場合、復帰日が自動的に調整されます。
したがって、私のフォーム コントローラーは、すべてのプロパティの変更をリッスンするためにも登録する必要があります。問題は、プロパティを変更し、変更をリッスンすることです。
こうすることで、ユーザーが変更するleaveTime
と、フォーム コントローラーは を使用setValue:forKey:
してモデルを更新しますが、直前に行ったまさにこの変更についての KVO 通知を即座に受け取ります。これは不必要であり、潜在的に有害です (私は自分で変更を行っただけです。変更したばかりであると言われる必要はありません)。
私が今まで見つけた唯一の方法は、新しい値を設定する直前に登録を解除し、次のように直後に再登録することです:
[self.model removeObserver:self forKeyPath:self.currentField.key];
[self.model setValue:newValue forKey:self.currentField.key];
[self.model addObserver:self forKeyPath:self.currentField.key options:NSKeyValueObservingOptionNew context:nil];
それは機能していますが、醜く、パフォーマンスに関しては、それが素晴らしいとは思えません。
誰かがそれをより良くする方法について説明していますか?
TL;DR
ControllerA
の登録済み KVO オブザーバーですModel
。
ControllerB
更新Model
==> ControllerA
KVO 通知を受け取ります。それはいいです。
ControllerA
更新Model
==> ControllerA
KVO 通知を受け取ります。これはいらない。