LoginSignup
11
13

More than 5 years have passed since last update.

その瞬間タッチされているかを判定する方法

Last updated at Posted at 2016-11-04

はじめに

他所から何らかの通知を受けて画面を更新しようとしたのですが、ユーザーが編集作業をしているときに勝手に更新すると事故りそうな内容だったため、「ユーザーがタッチしていない瞬間」を見計らって更新を掛けたらどうかと考えました。
ただ、その「タッチしているかどうかの判定方法」が調べても出てこなかったので作ってみました。

  • Xcode8, Swift3.0

タッチイベントの監視

まず、UIWindowのサブクラスを作り、イベントを監視します。
その中でタッチイベントのみをさばき、タッチ情報を更新するだけです。

MyWindow.swift
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) }
    }
}

ちなみに、UITouchphaseプロパティは、UIScrollViewだかジェスチャーだかどこかで処理がコケるとphase.endedに変わる現象を確認しています(滅多に起きないんですが)。そのために、sendEvent後半でわざわざphaseを見て間引いています(この間引く処理が無いとゴミ情報が残り、誤判定します)。

UIWindowを置き換える方法

Storyboardを使っている場合で、既存のUIWindowを自前のクラスに置き換える方法は
subclassing UIWindow while using storyboards
を参考にしました。

AppDelegate.swift
@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 {}
    }

    
}

使い方の例

ViewController.swift
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

11
13
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
13