如何检测 Swift 2 中的所有触摸

Posted

技术标签:

【中文标题】如何检测 Swift 2 中的所有触摸【英文标题】:How to detect all touches in Swift 2 【发布时间】:2015-07-26 23:22:05 【问题描述】:

我正在尝试为我正在使用 Swift 2 开发的应用程序创建超时功能,但在 swift 2 中,您可以将此代码放在应用程序委托中,它可以工作,但它没有检测到任何键盘按下、按钮按下,文本字段印刷机等:

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) 
    super.touchesBegan(touches, withEvent: event);
    let allTouches = event!.allTouches();

    if(allTouches?.count > 0) 
        let phase = (allTouches!.first as UITouch!).phase;
        if(phase == UITouchPhase.Began || phase == UITouchPhase.Ended) 
            //Stuff
            timeoutModel.actionPerformed();
        
    

在 swift 2 之前,我能够拥有 AppDelegate 子类 UIApplication 并覆盖 sendEvent: 如下所示:

-(void)sendEvent:(UIEvent *)event

    [super sendEvent:event];

    // Only want to reset the timer on a Began touch or an Ended touch, to reduce the number of timer resets.
    NSSet *allTouches = [event allTouches];
    if ([allTouches count] > 0) 
        // allTouches count only ever seems to be 1, so anyObject works here.
        UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase;
        if (phase == UITouchPhaseBegan || phase == UITouchPhaseEnded)
            [[InactivityModel instance] actionPerformed];
    

上面的代码适用于每次触摸,但 swift 等效代码仅在 UIWindow 的层次结构之上不存在视图时才有效?

有谁知道检测应用程序中每一次触摸的方法吗?

【问题讨论】:

它与 ios 9 而不是 Swift 2 有关。我在我的自定义 UIWindow 中有类似的解决方案,它也停止工作。可能是因为新的 iOS 9 拆分应用程序视图等。所以他们重做了它以支持这些新功能 - 两个打开的应用程序、键盘......不是 100% 确定,只是大声思考...... 【参考方案1】:

由于我的应用程序中有类似的东西,我只是尝试修复它:

UIWindow 中覆盖sendEvent - 不起作用 在委托中覆盖 sendEvent - 不起作用

所以唯一的方法是提供自定义UIApplication 子类。到目前为止,我的代码(适用于 iOS 9)是:

@objc(MyApplication) class MyApplication: UIApplication 

  override func sendEvent(event: UIEvent) 
    //
    // Ignore .Motion and .RemoteControl event
    // simply everything else then .Touches
    //
    if event.type != .Touches 
      super.sendEvent(event)
      return
    

    //
    // .Touches only
    //
    var restartTimer = true

    if let touches = event.allTouches() 
      //
      // At least one touch in progress?
      // Do not restart auto lock timer, just invalidate it
      //
      for touch in touches.enumerate() 
        if touch.element.phase != .Cancelled && touch.element.phase != .Ended 
          restartTimer = false
          break
        
      
    

    if restartTimer 
      // Touches ended || cancelled, restart auto lock timer
      print("Restart auto lock timer")
     else 
      // Touch in progress - !ended, !cancelled, just invalidate it
      print("Invalidate auto lock timer")
    

    super.sendEvent(event)
  


为什么会有@objc(MyApplication)。那是因为 Swift 对名称的处理方式与 Objective-C 不同,它只是说 - 我在 Objective-C 中的类名是 MyApplication

要使其正常工作,请打开您的 info.plist 并使用 Principal class 键和 MyApplication 值添加行(MyApplication@objc(...) 内部的内容,而不是您的 Swift 类名称)。原始密钥是NSPrincipalClass

【讨论】:

注意一件事 - 这不会捕获外部键盘事件。如果你有 BT 键盘......只是屏幕/软件之一。 感谢您的提醒。我正在构建的这个应用程序需要输入外部配件,所以我必须研究一下。 别忘了在顶部导入 UIKit。这太棒了,正是我需要的。 你在 appdelegate 中实现了什么?你能分享你的整个代码吗?【参考方案2】:

UIWindow 也有一个可以覆盖的sendEvent 方法。这将允许您跟踪自上次触摸屏幕以来的时间。斯威夫特 4:

class IdlingWindow: UIWindow 
    /// Tracks the last time this window was interacted with
    var lastInteraction = Date.distantPast

    override func sendEvent(_ event: UIEvent) 
        super.sendEvent(event)
        lastInteraction = Date()
    

如果您使用的是故事板,您可以在didFinishLaunchingWithOptions 中加载它:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate 
    var window: IdlingWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool 
        window = IdlingWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = UIStoryboard.init(name: "Main", bundle: nil).instantiateInitialViewController()
        window?.makeKeyAndVisible()
        return true
    
    :


extension UIApplication 
    /// Conveniently gets the last interaction time
    var lastInteraction: Date 
        return (keyWindow as? IdlingWindow)?.lastInteraction ?? .distantPast
    

现在在您的应用程序的其他地方,您可以像这样检查不活动:

if UIApplication.shared.lastInteraction.timeIntervalSinceNow < -2 
    // the window has been idle over 2 seconds

【讨论】:

不错的解决方案,但不幸的是:“'keyWindow' 在 iOS 13.0 中已弃用:不应用于支持多个场景的应用程序,因为它会在所有连接的场景中返回一个关键窗口”【参考方案3】:

@markiv 的回答就像一个魅力,有两个问题:

    键窗口 “'keyWindow' 在 iOS 13.0 中已被弃用:不应用于支持多个场景的应用程序,因为它会在所有连接的场景中返回一个关键窗口”

可以这样解决:

let kw = UIApplication.shared.windows.filter $0.isKeyWindow.first 找到here

查看@matt 的回答

    AppDelegate - 我收到这条消息:

    如果要使用主故事板文件,应用代理必须实现 window 属性

可以继承 UIWindow - 找到答案here。将此与 IdlingWindow 类结合使用。

消息消失了。

var customWindow: IdlingWindow?    
var window: UIWindow? 
    get 
        customWindow = customWindow ?? IdlingWindow(frame: UIScreen.mainScreen().bounds)
        return customWindow
    
    set  

【讨论】:

以上是关于如何检测 Swift 2 中的所有触摸的主要内容,如果未能解决你的问题,请参考以下文章

如何在我的触摸位置获取层次结构中的所有视图 - Swift

在 swift 中,如何在 AVPlayerViewController 中播放视频时检测触摸

使用 Swift/SKScene 检测触摸

在 Swift 中检测 UIImageView 触摸

如何防止意外触摸触发 swift 4.2 中的 touchesBegan?

我们如何检测精灵的触摸?