2016-07-01 2 views
0

Dans mon implémentation je courais plusieurs minuteries à la fois sur un UITableView et quand on termine la minuterie, j'annule et lui permettre d'être retiré de ladite vue. Actuellement, le problème que je rencontre est que quand une minuterie est supprimée, toutes les autres après l'arrêt de l'exécution.Tous NSTimers courir après une minuterie précédente qui a été invalidée l'exécution du cessez au lieu de continuer à courir

public class TimerManager{ 
    static let instance = TimerManager() 
    private var delegates = [TimerDelegate]() 
    var currentTimers = [TimerObject]() 

    public func timerAdded(timer: TimerObject){ 
     self.currentTimers.append(timer) 
     timer.timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(persistTimer(_:)), userInfo: "\(TimerManager.instance.currentTimers.count-1)", repeats: false) 
     timer.isRunning = true 
     for delegate in delegates{ 
      delegate.startTimer(self.currentTimers[self.currentTimers.count-1]) 
     } 
    } 

    @objc public func persistTimer(timer: NSTimer){ 
     if let indexString = timer.userInfo as? String{ 
      if let index = Int(indexString){ 
       if let currentTimer = self.currentTimers[safe: index]{ 
        if currentTimer.isRunning{ 
         currentTimer.timeDuration -= 1 
         for delegate in delegates{ 
          delegate.timerStarted(index) 
         } 
        } 
       } 
      } 
     } 

    } 

    public func addTime(timerId: Int, amount: Int){ 
     for delegate in delegates{ 
      delegate.addTimeToTimer(timerId, amount: amount) 
      UIApplication.sharedApplication().cancelLocalNotification(currentTimers[timerId].notification) 
      currentTimers[timerId].notification.fireDate = NSDate(timeIntervalSinceNow: Double(currentTimers[timerId].timeDuration)) 
     } 
    } 

    public func subtractTime(timerId: Int, amount: Int){ 
     for delegate in delegates{ 
      delegate.subtractTimeFromTimer(timerId, amount: amount) 
      UIApplication.sharedApplication().cancelLocalNotification(currentTimers[timerId].notification) 
      currentTimers[timerId].notification.fireDate = NSDate(timeIntervalSinceNow: Double(currentTimers[timerId].timeDuration)) 
     } 
    } 

    public func removeDelegate(removedDelegate: TimerDelegate){ 
     for i in 0..<delegates.count{ 
      if delegates[i] === removedDelegate{ 
       delegates.removeAtIndex(i) 
      } 
     } 
    } 

    public func cancelTimer(timerId: Int){ 
     let currentTimer = currentTimers[timerId] 
     currentTimer.timer.invalidate() 
     UIApplication.sharedApplication().cancelLocalNotification(currentTimer.notification) 
     currentTimers.removeAtIndex(timerId) 
    } 

    private init() {} 
} 

C'est mon singleton TimerManager ci-dessus. ci-dessous est ma logique de suppression:

//Delete cell functionality 
    public override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { 
     if(editingStyle == .Delete){ 
      let screenSize: CGRect = UIScreen.mainScreen().bounds 
      let screenWidth = screenSize.width 
      let screenHeight = screenSize.height 
      let deletedTimer = TimerManager.instance.currentTimers[indexPath.row] 
      if (deletedTimer.isRunning){ 
       deletedTimer.timer.invalidate()// <-- invalidates all timers for some reason 
       UIApplication.sharedApplication().cancelLocalNotification(deletedTimer.notification) 
      } 
      TimerManager.instance.currentTimers.removeAtIndex(indexPath.row) 
      self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) 
      self.tableView.frame = CGRect.init(x: 0, y: 100, width: screenWidth, height: screenHeight * 0.0625 * CGFloat(TimerManager.instance.currentTimers.count)) 
     } 
    } 

Voici comment je commence une minuterie:

public func startTimer(timer: TimerObject) { 
     let newIndexPath = NSIndexPath(forRow: TimerManager.instance.currentTimers.count-1, inSection: 0) 
     self.tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom) 
     let screenSize: CGRect = UIScreen.mainScreen().bounds 
     let screenWidth = screenSize.width 
     let screenHeight = screenSize.height 
     self.view.frame = CGRectMake(0, 100, screenWidth, screenHeight * 0.0625 * CGFloat(TimerManager.instance.currentTimers.count)) 
     self.view.alpha = 0.95 

    } 

C'est ce que mon TimerObject se compose de:

public class TimerObject{ 
    var alreadySet = false 
    var isRunning = false 
    var timer = NSTimer() 
    var timeDuration = Int() 
    var timerString = String() 
    var doneString = String() 
    let notification = UILocalNotification() 

    init(timeDuration: Int, timerString: String, doneString: String){ 
     self.timeDuration = timeDuration 
     self.timerString = timerString 
     self.doneString = doneString 
     notification.alertBody = doneString 
    } 
} 

instanciation d'un TimerObject:

private func setTextLinks(string: String){ 
     if let timersForInstruction = parsedOutTimers{ 
      stepTextView.linkTextAttributes = [NSForegroundColorAttributeName : UIColor.redColor()] 
      for timer in timersForInstruction{ 
       stepTextView.addLink(string.substringFromRange(timer.range)) { 
        let newTimer = TimerObject.init(timeDuration: timer.lowerBound * 60, timerString: self.stepTitle 
        newTimer.notification.fireDate = NSDate(timeIntervalSinceNow: Double(newTimer.timeDuration)) 
        TimerManager.instance.timerAdded(newTimer) 
       } 
      } 
      stepTextView.processLinks() 
     } 
    } 

Je dirais que c'est là où il est utilisé:

//where we update the cells 
    public func timerIsRunning(timerIndex: Int){ 
     if let currentTimer = TimerManager.instance.currentTimers[safe: timerIndex]{ 
      guard let cell = self.tableView.cellForRowAtIndexPath(NSIndexPath.init(
       forRow: timerIndex, 
       inSection: 0)) as? TimerCell else { return } 
      if currentTimer.timeDuration > 0{ 
       currentTimer.timeDuration -= 1 
       cell.timerDisplay.text = "\(currentTimer.timerString) \t\t\t - \(currentTimer.timeDuration/60):\((currentTimer.timeDuration % 60).format("02")) + " 
       cell.timerDisplay.processLinks() 
      }else if(currentTimer.timeDuration <= 0){ 
       currentTimer.isRunning = false 
       cell.timerDisplay.text = "Finished" 
       currentTimer.timer.invalidate() 
       currentTimer.notification.fireDate = NSDate(timeIntervalSinceNow: 0) 
       currentTimer.notification.alertBody = currentTimer.doneString 
       currentTimer.notification.alertAction = "Ok" 
       if(self.isBeingPresented()){ 
        NSNotificationCenter.defaultCenter().postNotificationName("timerDidFinish", object: currentTimer.notification.alertBody) 
       } 
       if(UIApplicationState.Background == UIApplication.sharedApplication().applicationState){ 
        UIApplication.sharedApplication().scheduleLocalNotification(currentTimer.notification) 
       } 
      }else{ 
       //handle pause 
      } 
     } 

    } 

J'espère sincèrement que cette information est suffisante pour travailler avec. Toutes les solutions ou alternatives aideraient grandement.

+0

Ne faites jamais 'minuterie var = NSTimer()'. Vous ne montrez aucun exemple de création et d'utilisation d'un 'TimerObject'. Votre question est mieux qu'hier, mais vous devriez vraiment expliquer à la fois la façon dont vous utilisez cette classe et ce que votre objectif utilisateur réel est - pourquoi avez-vous beaucoup de minuteries, pourquoi ne pas 1 minuterie et objets qui tiennent un « feu jusqu'à expiration » ou simplement un NSDate quand ils expirent? – Wain

+0

Désolé, je ne suis pas sûr de la façon dont montrer l'utilisation de l'objet de la minuterie sans une capture d'écran de l'application. Je vais expliquer du mieux que je peux. L'utilisateur clique sur un lien qui est mappé à une action qui notifie au singleton de démarrer une temporisation pendant X nombre de minutes, la fonction de temporisateur de début crée ensuite une nouvelle cellule dans la vue de table affichant la temporisation en cours. J'utilise un NSTimer pour avertir persistTimer de dire à la table de mettre à jour la cellule avec le temps nouvellement décrémenté. L'utilisateur peut démarrer n'importe quel nombre de minuteries et ils peuvent activer une minuterie n'importe quel nombre de fois, ce sont les contraintes. –

+0

L'utilisation d'un NSDate ne me permettrait pas de mettre à jour le tableView chaque seconde. Aussi, pouvez-vous s'il vous plaît entrer dans plus de détails pour savoir pourquoi 'var timer = NSTimer()' est tabou? –

Répondre

0

j'ai pu corriger mon problème en instancier à la place une nouvelle minuterie pour chaque objet, il y a juste une minuterie qui est mise à jour de la vue pour tous les objets.

private var timer = NSTimer() 
    var isFirstTimer = false 


    public func timerAdded(newTimerObject: TimerObject){ 
     if(isFirstTimer){ 
      self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(persistTimers(_:)), userInfo: nil, repeats: true) 
     } 
     self.currentTimers.append(newTimerObject) 
     newTimerObject.isRunning = true 
     for delegate in delegates{ 
      delegate.startTimer(self.currentTimers[self.currentTimers.count-1]) 
     } 
    } 

    @objc public func persistTimers(timer: NSTimer){ 
     for i in 0..<self.currentTimers.count{ 
      if let currentTimer = currentTimers[safe: i]{ 
       if(currentTimer.isRunning){ 
        currentTimer.timeDuration -= 1 
        for delegate in delegates{ 
         delegate.timerStarted(i) 
        } 
       } 
      } 
     } 
    } 

C'est finalement ce que je fini par changer dans ma déclaration singleton et J'ai fini par enlever le

currentTimer.timeDuration -= 1 

ligne de la fonction timerStarted dans la fonction persistTime ... Je réalise ma mise en œuvre et le style a beaucoup à désirer mais c'est un début. Seulement 2 semaines dans la programmation iOS après tout.

EDIT:

l'extension "[safe: ]" I ajouté est d'accès à l'index sûr:

public extension CollectionType{ 
    subscript (safe index: Index) -> Generator.Element? { 
     return indices.contains(index) ? self[index] : nil 
    } 
}