Как да открием всички докосвания в Swift 2

Опитвам се да създам функция за изчакване за приложение, което разработвам с помощта на 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?

Някой знае ли начин за засичане на всяко докосване в приложението?


person FireDragonMule    schedule 26.07.2015    source източник
comment
Има нещо общо с iOS 9 вместо Swift 2. Имам подобно решение в моя персонализиран UIWindow и то също спира да работи. Вероятно заради новия разделен изглед на приложения на iOS 9 и т.н. Така че те го преработиха, за да поддържа тези нови функции - две отворени приложения, клавиатура, ... Не съм 100% сигурен, просто си мисля на глас ...   -  person zrzka    schedule 28.07.2015


Отговори (3)


Тъй като имам нещо подобно в приложението си, просто се опитах да го поправя:

  • замени sendEvent в UIWindow - не работи
  • замени 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.

въведете описание на изображението тук

person zrzka    schedule 28.07.2015
comment
Имайте предвид едно нещо - това не улавя външни събития от клавиатурата. Ако имате BT клавиатура ... Само тази на екрана/софтуера. - person zrzka; 29.07.2015
comment
Благодаря за подкрепата. Това приложение, което създавам, ще има външни аксесоари за въвеждане, така че ще трябва да разгледам това. - person FireDragonMule; 29.07.2015
comment
не забравяйте да импортирате UIKit в горната част. Това е страхотно, точно това, от което имах нужда. - person mondousage; 22.06.2016
comment
Какво сте внедрили в appdelegate? Бихте ли споделили целия си код? - person Jamshed Alam; 09.10.2019

UIWindow също има sendEvent метод, който можете да замените. Това ще ви позволи да проследите времето от последното докосване на екрана. Swift 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
}
person markiv    schedule 05.02.2018
comment
Хубаво решение, но за съжаление: „keyWindow“ беше отхвърлено в iOS 13.0: Не трябва да се използва за приложения, които поддържат множество сцени, тъй като връща ключов прозорец във всички свързани сцени - person wider-spider; 13.09.2019

@markiv отговорът работи като чар, с два проблема:

  1. keyWindow
    • "'keyWindow' was deprecated in iOS 13.0: Should not be used for applications that support multiple scenes as it returns a key window across all connected scenes"

Може да се реши така:

let kw = UIApplication.shared.windows.filter {$0.isKeyWindow}.first намерен тук

вижте отговора на @matt

  1. AppDelegate - Получавам това съобщение:

    • The app delegate must implement the window property if it wants to use a main storyboard file

Човек може да подкласифицира UIWindow – намери отговора тук. Комбинирайте това с класа IdlingWindow.

Съобщението го няма.

var customWindow: IdlingWindow?    
var window: UIWindow? {
    get {
        customWindow = customWindow ?? IdlingWindow(frame: UIScreen.mainScreen().bounds)
        return customWindow
    }
    set { }
}
person wider-spider    schedule 13.09.2019