はじめに
他所から何らかの通知を受けて画面を更新しようとしたのですが、ユーザーが編集作業をしているときに勝手に更新すると事故りそうな内容だったため、「ユーザーがタッチしていない瞬間」を見計らって更新を掛けたらどうかと考えました。
ただ、その「タッチしているかどうかの判定方法」が調べても出てこなかったので作ってみました。
- Xcode8, Swift3.0
タッチイベントの監視
まず、UIWindow
のサブクラスを作り、イベントを監視します。
その中でタッチイベントのみをさばき、タッチ情報を更新するだけです。
class MyWindow: UIWindow {
var allTouches = Set<UITouch>()
override func sendEvent(_ event: UIEvent) {
if event.type == .touches {
if let allTouches = event.allTouches {
for touch in allTouches {
switch touch.phase {
case .began:
self.allTouches.insert(touch)
case .ended, .cancelled:
self.allTouches.remove(touch)
default:
break
}
}
}
}
super.sendEvent(event)
//allTouches = Set<UITouch>(allTouches.filter({ $0.phase != .ended && $0.phase != .cancelled })) これだとメモリ効率悪いかな
allTouches.filter { $0.phase == .ended || $0.phase == .cancelled }.forEach { allTouches.remove($0) }
}
}
ちなみに、UITouch
のphase
プロパティは、UIScrollView
だかジェスチャーだかどこかで処理がコケるとphase
が.ended
に変わる現象を確認しています(滅多に起きないんですが)。そのために、sendEvent
後半でわざわざphase
を見て間引いています(この間引く処理が無いとゴミ情報が残り、誤判定します)。
UIWindowを置き換える方法
Storyboardを使っている場合で、既存のUIWindow
を自前のクラスに置き換える方法は
subclassing UIWindow while using storyboards
を参考にしました。
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
// http://stackoverflow.com/questions/10403137/subclassing-uiwindow-while-using-storyboards
var myWindow: MyWindow?
var window: UIWindow? {
get {
if myWindow == nil {
myWindow = MyWindow(frame: UIScreen.main.bounds)
}
return myWindow
}
set {}
}
略
}
使い方の例
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.didTimer), userInfo: nil, repeats: true)
}
@objc private func didTimer() {
if let myWindow = (UIApplication.shared.delegate as? AppDelegate)?.myWindow {
print(myWindow.allTouches.isEmpty ? "タッチ無し" : "タッチ有り")
}
}
}
タイマーで定期的にカスタムウィンドウのallTouches.isEmpty
を見ているだけのサンプルコードです。
所感
実際には、他所から通知を受けてタッチ状態だったら、1秒後にもう一回タッチされていないか確認する(繰り返す)...といった処理を書きました。
今回はそれで十分でしたが、もう少しレスポンスが欲しければタッチが終わった瞬間にイベントを通知するなどという方法も実現できそうです。
また、タッチの監視なんてViewController
のtouches系を処理すりゃええやん!とツッコミがありそうですが、UIWindow
系でやれば、どのビューコントローラーからでもタッチの有無情報が見られるというお手軽さがあるかと思われます。
いろいろ書きましたが、他にもっとスマートな判定方法が有ったら教えてくださいm(_ _)m