isExclusiveTouchで同時タップを禁止
UIButton
を同時にタップすると思わぬ不具合が発生する場合があります。
それを防止するために、UIButtonの同時タップを禁止するisExclusiveTouchという設定があります。 exclusive
は 排他的な
という意味なので、isExclusiveTouch
はタップを排他的にする設定
という意味です(デフォルトは false
です)。
下記のプログラムのように、同時タップしてほしくないボタンの isExclusiveTouch
を true
にすると、ボタンの同時タップができなくなるので安心です。
buttonA.isExclusiveTouch = true
buttonB.isExclusiveTouch = true
Outlet Collectionでほぼ自動
しかし、ボタンが増えると、すべてのボタンに isExclusiveTouch
を設定するのは大変です。そんなときに便利なのが Outlet Collection
です。
Outlet Collection
をつくる方法は簡単です。
通常の Outlet
をつくるように、controlキーを押しながらボタンをクリックして、ストーリーボードからソースファイルにぐいっとドラッグします。するとポップアップ画面が表示されるので、 connection
を Outlet Collection
に変更して name
に名前を入力して、 connect
ボタンをクリックします。

あとは、 Outlet Collection
に追加したいボタンをcontrolキーを押しながらクリックして、さきほど作成した Outlet Collection
のところまでドラッグします。そうすると、ひとつの Outlet Collection
に複数のボタンが登録されます。

ソースファイルでは、 Outlet Collection
は下記のようになります。
@IBOutlet var exclusiveButtons: [UIButton]!
UIButton
の部分が [UIButton]
になっているところからわかるように、 Outlet Collection
はアウトレットの配列です。配列であれば for-in
がつかえるので、下記のようなコードでいっきに isExclusiveTouch
を true
に変更できます。
for button in exclusiveButtons {
button.isExclusiveTouch = true
}

viewDidLoad
などで上の処理を実行すれば、 Outlet Collection
に追加しているボタンの同時タップを禁止することができます。
デフォルト設定すれば全自動
shtnkgmさんにコメントをいただいて追記しました。ありがとうございます!
以下のように書くと UIButton
全体にデフォルト値として適用が可能だそうです。つぎに紹介する exclusiveAllTouches
メソッドも便利ですが、たとえば viewDidLoad
よりあとに動的に追加されたボタンへも反映できるそうです。
なにこれ素敵!
UIButton.appearance().isExclusiveTouch = true
再帰メソッドをつかえば全自動
Outlet Collection
も便利ですが、ボタンを追加したときに設定し忘れるかもしれません。そんなときに便利な方法をこちらの記事で紹介していました。
viewDidLoad
などで exclusiveAllTouches
メソッドを呼び出せば、再帰的にすべてのボタンを検索して isExclusiveTouch
を true
にしてくれます。とてもいいですね。
extension UIViewController {
func exclusiveAllTouches() {
self.applyAllViews { $0.isExclusiveTouch = true }
}
func applyAllViews(apply: (UIView) -> ()) {
apply(self.view)
self.applyAllSubviews(self.view, apply: apply)
}
private func applyAllSubviews(view: UIView, apply: (UIView) -> ()) {
let subviews = view.subviews as [UIView]
subviews.map { (view: UIView) -> () in // Swiftのmapって戻り値なくても大丈夫なのね(ちょっと不気味
apply(view)
self.applyAllSubviews(view, apply: apply)
}
}
}