UIControlでの状態プログラミング

制御要素を実装するときにプログラマが直面する主な問題は、この要素が機能するための正しいロジックを構築することです。



研究課題



ドキュメントで定義されているように、UIControlは、ユーザーアクションに特定の方法で応答できる視覚要素の一般的な動作を実装するクラスです。 これは、視覚的な表示、動作、開始プロセスなどを変更することを意味します。 これには何が必要で、どのように実装するのですか? 最初の質問に対する明確な答えがあります-状態、およびそれらの間の遷移の論理。 2番目の質問ではもう少し複雑です...



問題解決



単純な開発者の多くは、通常update()と呼ばれるメソッドを作成し、その中に仮定法で叙事詩を書くことになります。



if ... { element1.property = value1 ... } else if ... { element1.property = value2 ... } ...
      
      





それはまだ行かず、コードは一貫していて読みやすいです。 しかし、 サルがファッショナブルな手ren弾に出くわしても、それはすべて嘆かわしいものになります。



  RAC(element1, hidden) = [RACSignal combineLatest:@[ self.textField1.rac_textSignal ] reduce:^(NSString *password) { return @((!password.length >= 1)); }]; RAC(element2, hidden) = [RACSignal combineLatest:@[ self.textField1.rac_textSignal ] reduce:^(NSString *password) { return @(!(password.length >= 2)); }]; RAC(element3, hidden) = [RACSignal combineLatest:@[ self.textField1.rac_textSignal ] reduce:^(NSString *password) { return @(!(password.length >= 3)); }]; RAC(element4, hidden) = [RACSignal combineLatest:@[ self.textField1.rac_textSignal ] reduce:^(NSString *password) { if(password.length == PIN_LENGTH) { [self activateNextField]; return @(NO); } else return @(YES); }];
      
      





そして州が多ければ多いほど、それはすべて地獄の無限の輪のように見えます。



すぐに使えるソリューション



UIControlおよびその子孫は、それぞれ次の状態更新メカニズムを使用します。









updateメソッドが最初に行うことは、現在の状態、より具体的にはUIControlState状態プロパティを読み取ることです。 これは、UIControlStateの列挙に記述された状態単位のビットマスクです。 このプロパティは、保存するのではなく計算する必要があることに注意してください。 つまり オブジェクトの実際の状態は、この状態の説明を形成するものであり、その逆ではありません。



さらに、受信状態に関連付けられた値がコンテナから引き出されて適用されます。



状態を更新するプロセスは、状態係数の変更後に開始されます。 たとえば、ブール変数の場合:



 open var isEnabled: Bool { didSet { if oldValue != isEnabled { //    } } }
      
      





UIControlStateには、追加の状態を作成するためのビットマスクの一部、UIControlStateApplicationがあります。 システム制御の状態を追加する場合は、この間隔から任意の値を選択できます。



 extension UIControlState { static let custom = UIControlState(rawValue: 1 << 16) } let button = UIButton(type: .custom) let title = "Title for custom state" button.setTitle(title, for: .custom) button.title(for: .custom) == title // true
      
      





しかし、何らかの理由で、独自の状態を作成する機会を与えてくれたApple開発者は、それらを管理するAPIを提供しませんでした。



私の場合



私の仕事は、ピンコードを入力するための制御を実装することでした。 タスクは非常に簡単なので、あなたはそれを複雑にしようとしています。



上記の問題を考慮して、私はAppleがパブリックインターフェイスで宣言しなかったものを書くことにしました。



そこで、UIControl-QUIckControlを介してクラスアドオンを作成しました。 特定のオブジェクトの特定の状態(または状態のセット)に値を設定する機能を提供します。



 func setValue(_ value: Any?, forTarget: NSObject, forKeyPath: String, for: QUICState)
      
      





メソッドのセマンティクスからわかるように、KVCが基本です。 swift 3の重要な検証の問題はすでに解決されています。ObjCでは、定義マクロを追加することで簡単に解決できます。



ユーザー状態の値を設定する前に、次のメソッドを使用してこの状態を登録する必要があります。



 func register(_ state: UIControlState, forBoolKeyPath keyPath: String, inverted: Bool)
      
      





コントロールが値を設定していない状態になった場合、デフォルト値が適用されます。 デフォルト値は、特定のキーに値が最初に設定されるときに決定されます。 正式には、部分一致モードで.normal状態を使用してオーバーライドできます(以下を参照)。 .normalは、絶対にあらゆる条件に含まれます。



状態設定を簡素化し、値を重複させないために、QUICState状態記述構造が作成されました。 現在では、現在の状態への準拠を評価する6つのモードが含まれています。





各モードには独自の優先度があり、複数の一致がある場合の最重要度を決定します。



状態の実現は状態係数(ブール変数)の変更直後に発生するため、変更をすぐに適用せずに複数の遷移を実行する可能性が作成されます。



 func beginTransition() //    func endTransition() //      func commitTransition() //      func performTransition(withCommit commit: Bool = default, transition: () -> Void) //     
      
      





おわりに



このAPIを使用すると、次の操作だけでなく、状態をすばやく構成し、コントロール間の依存関係を作成できます。



 control.setValue(true, forTarget: otherControl, forKeyPath: "enabled", forAllStatesContained: [.filled, .valid])
      
      





これは、たとえば、入力フォームの頻繁な使用例です。









その結果、私は自分が望むものを手に入れましたが、反応主義の要素も得ました。 自動プログラミングは非常に不便ですが、有能なアプローチでは、非常に信頼できます。 このプログラミングスタイルが、ゲームやコントローラーから、あらゆる種類のアナライザーやAIにアプリケーションを見つけるのも不思議ではありません。



→PinCodeControlの実装とすべてのコードは、 ここで表示できます



All Articles