プロパティを利用して処理を簡易的に記述する【Swift 4】
Swiftでは、プロパティの値が変化した際に呼び出されるwillSet/didSetという関数があります。これらの関数はプロパティを監視する目的で定義されていますが、うまく使うことでプロパティの値の変化をUIに反映することも可能です。ここからは、プロパティの値の変化とUIへの反映について具体的なサンプルを挙げながら説明します。
プロパティの値が変化した際に処理を行う
プロパティの値が変化した際に処理を行うwillSet/didSet関数について、両者の違いは次の通りです。
| 関数名 | 呼ばれるタイミング | 得られる値 |
|---|---|---|
| willSet | 値が変更される直前 | 変更後の値 |
| didSet | 値が変更された後 | 変更前の値 |
呼び出されるタイミングと得られる値が両者で逆になっていることに気をつけてください。willSet/didSet関数を利用する際には、プロパティを定義する際に次の書式で関数を記述します。
var プロパティ名 [:型] = 初期値 {
willSet[(newValue)]{
// 処理
}
didSet[(oldValue)]{
// 処理
}
}
willSet/didSetメソッドの引数は使わない場合に省略可能で、関数自体も利用しない場合は記述しなくて構いません。また、関数の処理に関しては仕様上は処理内容に規制はありません。
didSet関数を利用する簡単な例を次に挙げます。
class PropertyViewController: UIViewController {
var num: Int = 0 {
didSet(oldValue){
if num > oldValue { return } // 元の値より大きければ何もしない
print("元の値より大きい値を入力してください。")
num = oldValue // 前の値のまま
}
}
override func viewDidLoad() {
num = 3
print("代入後: \(num)") // 結果:代入後: 3
num = 2
print("代入後: \(num)") // 結果:元の値より大きい値を入力してください。代入後: 3
num = 4
print("代入後: \(num)") // 結果:代入後: 4
}
}
リスト5の例では、Int型のプロパティnumの値を代入した後に、代入前の値と比べて大きい値でなければ代入できないようにしています。didsetメソッドを利用すると、こうした値を参照しての制限を容易にかけることが可能です。サンプルの通り、ビューコントローラー内に特別な処理を記述することなく、プロパティの値に関しての処理を行うことができます。
UIの入力制限を行う
didSet関数を利用し、会員登録の画面などでよく利用される入力内容によってボタンの有効化を行うサンプルを作成してみます。
2つの入力欄に入力がある場合のみ、ナビゲーションバーの保存ボタンが押せるようになるサンプルです。ゲームやSNSのアプリでの会員登録時によく見かける機能ですが、didSet関数を利用するとこうした入力制限も比較的容易に作成できます。
class InputViewController: UIViewController {
@IBOutlet weak var saveButton: UIBarButtonItem! // ------(1)
@IBOutlet weak var addressTextField: UITextField!
@IBOutlet weak var nameTextField: UITextField!
var isValidated = false { // ------(2)
didSet {
self.saveButton.isEnabled = self.isValidated
}
}
最初にプログラム内で利用するUIをIBOutletで接続しておきます(1)。その後、保存ボタンの有効化を制御するプロパティをBool型の変数isValidatedで定義します。有効化を管理する変数を独立させておくことで処理をわかりやすくできます。プロパティを定義する定義の際に、didSetメソッドのブロック内で保存ボタンを押せる/押せないをisValidated変数の値で変更できるようにします。
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.isValidated = false // ------(3)
}
@IBAction func textChanged(_ sender: Any) { // ------(4)
// 入力内容のある入力欄のみを取得
let array = [self.nameTextField.text, self.addressTextField.text].filter{ ($0?.count ?? 0) > 0 }
// 入力内容のある入力欄が2つならば true
self.isValidated = array.count == 2 ? true : false
}
画面が表示された際には、保存ボタンは無効化しておきます(3)。
次に、画面上に配置した2つのテキストフィールドの入力内容が変更された際に発生するUIControlEvents.editingChangedオブジェクトと入力チェックの処理をIBActionで結合させます(4)。これによって入力欄2つのうち、どちらの入力内容が変更された場合にも(4)の入力チェックのメソッドが呼び出されます。
入力内容のチェックは、配列の条件に合う要素のみを取得するfilter(_:)メソッドを利用して作成しています。入力欄の入力内容を参照するtextプロパティを参照して、2つの入力欄の入力内容を配列にします。その配列でfilter(_:)メソッドを実行し、入力内容の文字数が0より大きいもののみを取得します。その結果が2つであれば2つの入力欄に内容があるものと判断しています。
判断した結果は、最初に定義した保存ボタンの有効化を制御するisValidated変数に代入します。isValidated変数の値が変更された際には、didSet関数が呼ばれ、保存ボタンの有効/無効を切り替えます。
サンプルの実行結果は次の通りです。
2つの入力欄の内容をチェックする処理にも関わらず、delegateや別々のメソッドを利用することなく、1つのプロパティと1つのメソッドで処理を完結することができます。
プロパティをオーバーライドして処理を行う
UI部品を管理するクラスで、IBInspectableでプロパティを宣言すると、そのプロパティはストーリーボードから制御することができます。UIButtonクラスを継承して次のようにUIColor型のプロパティを宣言すると、ストーリーボードでは次のようにプロパティの内容を参照/指定できるようになります。
import UIKit
class ColorButton: UIButton {
@IBInspectable var hightlightedBackgroundColor :UIColor?
@IBInspectable var hightlightedselectedBackgroundColor :UIColor?
@IBInspectable var selectedBackgroundColor :UIColor?
...略...
UIButtonクラスのハイライト状態を制御するisHighlightedプロパティは、UIButtonクラスを継承したクラスではオーバーライドすることが可能です。これを利用して、ハイライト時や選択時の処理をdidSet関数で行うことも可能です。
import UIKit
class ColorButton: UIButton {
@IBInspectable var hightlightedBackgroundColor :UIColor?
@IBInspectable var hightlightedselectedBackgroundColor :UIColor?
@IBInspectable var selectedBackgroundColor :UIColor?
// isHighlightedをオーバーライドしてハイライト状態が変わった際の処理を行う
override var isHighlighted: Bool {
didSet {
// 状態によってボタンの背景色を変える
switch (isHighlighted, isSelected) {
case (true, false):
self.backgroundColor = hightlightedBackgroundColor
case (true, true):
self.backgroundColor = hightlightedselectedBackgroundColor
case (false, true):
self.backgroundColor = selectedBackgroundColor
default:
backgroundColor = .white
}
}
}
}
ハイライト状態はisHighlightedプロパティ、選択状態はisSelectedプロパティで参照できます。サンプルの実行結果は次の通りです。
この図のように、UIButtonクラスが本来持っているプロパティの値が変化した際の処理もオーバーライドできます。またdidSetの中では、通常のSwiftでの処理をそのまま行えるため、サンプルの通り他のプロパティも参照し、状態に応じた処理を行うことも可能です。UI部品の状態がいくつかに分かれた場合の処理も、didSet関数を利用すると比較的容易に行うことができます。
まとめ
アプリの画面を操作した際の処理について、今回はプロパティからUIを制御する方法について説明しました。プロパティの値が変化した際の処理に関しては、デリゲートやNotificationを利用しなくても実行できるため、ぜひ使ってみてください。
次回はライブラリを利用して、アプリの機能を拡充する基本的な方法について説明します。
