简体   繁体   English

计时器到期时快速删除表视图单元格

[英]Swift deleting table view cell when timer expires

I have tried hard to find a solution but I'm stuck. 我已经尽力寻找解决方案,但是我陷入了困境。 have a custom table view with timer in each cell. 在每个单元格中都有一个带有计时器的自定义表格视图。 when the timer expires the cell should get deleted even if the cell is Offscreen it should get deleted and should not be displayed. 当计时器到期时,即使该单元处于屏幕外,该单元也应被删除,并且不应显示。

 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! iDealCell
    //        cell.SponsorLogo.image = UIImage(named: "back.png")!
    cell.SponsorName.text = iDeals[indexPath.row].SponsorName;
    cell.Distance.text = iDeals[indexPath.row].Distance;
    cell.Type.text = iDeals[indexPath.row].Type;
    cell.iDealTimer.font = UIFont(name: "DBLCDTempBlack", size: 18.0)

    onHourFromNow = NSDate(timeInterval: 10, sinceDate: timeNow)
    let TimeDiffInSec = NSCalendar.currentCalendar().components(.Second, fromDate: timeNow, toDate: onHourFromNow, options: []).second
    cell.TimeDiffInSec = TimeDiffInSec
    cell.kickOffCountdown()
    cell.backgroundColor = UIColor.clearColor()
    cell.delegate = self
    return cell
}

in cell class, three functions to initialise and run the timer 在单元类中,有三个函数来初始化和运行计时器

func kickOffCountdown(){
    self.setCountDown()
    Timer.invalidate()
    Timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(iDealCell.setCountDown), userInfo: nil, repeats: true)                     
}

func secondsToHoursMinutesSeconds (seconds : Int) -> (String, String, String) {
    let hours = seconds / 3600
    let minutes = (seconds % 3600) / 60
    let seconds = (seconds % 3600) % 60

    let hoursString = hours < 10 ? "0\(hours)" : "\(hours)"
    let minutesString = minutes < 10 ? "0\(minutes)" : "\(minutes)"
    let secondsString = seconds < 10 ? "0\(seconds)" : "\(seconds)"
    return (hoursString, minutesString, secondsString)
}

func setCountDown() {
    if(TimeDiffInSec > 0) {
        let (h,m,s) = secondsToHoursMinutesSeconds(TimeDiffInSec)
        self.iDealTimer.text = "\(h):\(m):\(s)"
        TimeDiffInSec = TimeDiffInSec - 1
    }
    else{
        self.iDealTimer.text = "EXPIRED"
        if let delegate = self.delegate {
            delegate.DeleteiDealID(1)
        }
        Timer.invalidate()
    }
}

Any help will be greatly appreaciated 任何帮助将不胜感激

I think, this will be better way: 我认为,这将是更好的方法:

  1. Inside tableView:cellForRowAtIndexPath calculate (or fetch) time and add it's value to label in cell. tableView:cellForRowAtIndexPath内部,计算(或获取)时间并将其值添加到单元格中的标签上。
  2. Remove timer from cell. 从单元格中删除计时器。
  3. Add timer in main class (where tableView placed) in viewDidAppear (or inside block where you fetch data), that will every second call method, that check and remove expired objects (or you can apply filter) and fire tableView.reloadData() (or delete needed rows animated). viewDidAppear中的主类(放置tableView的位置)中(或在获取数据的内部块中)添加计时器,这将每隔第二个调用方法一次,该方法将检查并删除过期的对象(或您可以应用过滤器)并触发tableView.reloadData() (或删除所需的动画行)。
  4. In viewDidDisappear invalidate timer. viewDidDisappear计时器失效。

You can do simple thing whenever your time have been expire you can remove those values from your array of dictionary whatever you used for row count. 只要您的时间到期,您都可以做简单的事情,无论您使用什么行计数,都可以从字典数组中删除这些值。

Simple thing here is your all table cell depends on your row count remover particular cell with by removing particular array object. 简单的事情是您的所有表单元格取决于您通过删除特定数组对象而获得的行数去除器特定单元格。

example : 例如:

if timerExpire == true {
        array.removeAtIndex(5)
        self.table.reloadData()
}

This is a tricky problem, because you want: 这是一个棘手的问题,因为您想要:

  1. Table rows to be deleted even if they are offscreen at the time the timer pops. 即使计时器弹出时屏幕外的表行也要删除。
  2. New rows can be added while the old rows are "ticking". 可以在旧行“滴答”时添加新行。

The first point means that you do not want the timer to be kept in the cell . 第一点意味着您不希望将计时器保留在单元格中 It is the wrong place anyway, because cells get reused and you'd have a nightmare invalidating and restarting timers. 无论如何,这是错误的地方,因为单元会被重用,并且您将面临噩梦,使计时器无效并重新启动。

The second point means that the row number you want to delete at the time the timer is started could be different than the row number you delete when the timer pops. 第二点意味着在计时器启动时要删除的行号可能不同于在弹出计时器时要删除的行号。 You may start your timer for row 5 to be deleted in 5 seconds, but in the meantime row 4 gets deleted, making the former row 5 now row 4 . 您可能会启动定时器行5中被删除5秒,但在此期间连续4被删除,使得前行5现在排4 When the former row 5 's timer pops, row 4 needs to be deleted from the table. 当前第5行的计时器弹出时,需要从表中删除第4行。

Here is the approach I suggest: 这是我建议的方法:

  1. Give each row in your table a unique ID. 为表格中的每一行分配唯一的ID。 This will just be a simple count that is maintained by your UITableViewController class. 这只是UITableViewController类维护的简单计数。

     var nextID = 0 
  2. Maintain a list of active ID's that correspond to the rows that are currently in your table. 维护与表中当前行相对应的活动ID的列表。 Add this property to your UITableViewController : 将此属性添加到您的UITableViewController

     var activeIDs = [Int]() 
  3. Add a dictionary to your table that maps a NSTimer to an ID. 将字典添加到表中,该字典将NSTimer映射到ID。 Add this to your UITableViewController : 将此添加到您的UITableViewController

     var timerIDmap: [NSTimer: Int]() 
  4. When you create a new row in your table: 在表格中创建新行时:

     let newID = nextID activeIDs.append(newID) nextID += 1 
  5. In cellForRowAtIndexPath , be sure to store the ID in a property of the cell. cellForRowAtIndexPath ,确保将ID存储在单元格的属性中。

     cell.cellID = activeIDs[indexPath.row] 
  6. When you create a timer, you need to store the timer and its corresponding cell ID in the timerIDmap . 创建计时器时,需要将计时器及其对应的单元格ID存储在timerIDmap Since you'll do this in the custom cell, the cell needs to have a weak reference to the tableViewController that holds it: 由于您将在自定义单元中执行此操作,因此该单元需要对保存它的tableViewController进行弱引用:

     // add this property to your cell weak var myTableVC: UITableViewController? 

    and assign that property in cellForRowAtIndexPath : 并在cellForRowAtIndexPath分配该属性:

     cell.myTableVC = self 

    so that when you create the timer: 这样,当您创建计时器时:

     let timer = NSTimer.scheduledTimerWithTimerInterval(... myTableVC?.timerIDmap[timer] = cellID 
  7. When your timer ticks, you need to decrement the time left on that timer. 计时器计时时,您需要减少该计时器上剩余的时间。 That means the time left should also be kept in your model. 这意味着剩余的时间也应保留在模型中。 Add this dictionary to your UITableViewController : 将此字典添加到您的UITableViewController

     var timeLeft = [Int: Int]() // maps CellID to time left 

    that means that when you create the timer in the first place, you will store timeLeft in this dictionary 这意味着当您首先创建计时器时,会将timeLeft存储在此字典中

     myTableVC?.timeLeft[cellID] = 50 // some appropriate value 

    OK, so now in your handleCountdown routine which should be implemented in your UITableViewController : 好的,现在在您的handleCountdown例程中,应该在UITableViewController实现该例程:

     func handleCountdown(timer: NSTimer) { let cellID = timerIDMap[timer] // find the current row corresponding to the cellID let row = activeIDs.indexOf(cellID) // decrement time left let timeRemaining = timeLeft[cellID] - 1 timeLeft[cellID] = timeRemaining if timeRemaining == 0 { timer.invalidate timerIDmap[timer] = nil activeIDs.removeAtIndex(row) tableView.deleteRowsAtIndexPaths(NSIndexPath(row: row, section: 0), withRowAnimation: ... } else { tableView.reloadRowsAtIndexPaths(NSIndexPath(row: row, section: 0), withRowAnimation: ... } } 
  8. This leaves very little work for your custom cell. 这为您的自定义单元留下了很少的工作。 It should merely take the time left on the timer and format it for display. 它仅应占用计时器上剩余的时间并对其进行格式化以进行显示。 In cellForRowAtIndexPath , tell the cell how much time is left on the timer: cellForRowAtIndexPath ,告诉单元计时器还剩多少时间:

     cell.timeLeft = timeLeft[activeIDs[indexPath.row]] 
  9. The number of items in your table is the same as the number of items in activeIDs , so in tableView:numberOfRowsInSection return 表中的项目数与activeIDs的项目数相同,因此在tableView:numberOfRowsInSection返回

     return activeIDs.count 

I have been trying these solutions but I dont think they are viable towards the goal. 我一直在尝试这些解决方案,但我认为它们对实现该目标不可行。 Just wanted to let people know. 只是想让人们知道。 If you have found something that works or have found the same can you please let us know 如果您发现了有用的东西或找到了相同的东西,请告诉我们

This is the solution I have come up with. 这是我想出的解决方案。 Its not a perfect solution by any means however, it does solve the problem. 无论如何,它都不是完美的解决方案,它确实解决了问题。

In the viewcontroller: 在视图控制器中:

    func handleDate(timer: Timer) {


    DispatchQueue.main.async() {

        if self.posts.count < 1 {
            print("Empty")
            timer.invalidate()

        } else {

            let calendar = Calendar.current
            let date = Date()

            let componentsCurrent = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date)
            var components = DateComponents()

            components.hour = componentsCurrent.hour
            components.minute = componentsCurrent.minute
            components.second = componentsCurrent.second
            components.year = componentsCurrent.year
            components.month = componentsCurrent.month
            components.day = componentsCurrent.day
            let currentTime = calendar.date(from: components)!

            for post in self.posts {



                let cellID = post.postID
                let row = self.postsInFeed.index(of: cellID)

                let endDate = TimeInterval(post.time)



                if currentTime.timeIntervalSince1970 >= endDate {



                        self.tableView.beginUpdates()
                        timer.invalidate()

                        print(post.postID)
                        print("Deleting tableview row")

                        self.postsInFeed.removeFirst()
                        self.posts.removeFirst()

                        let store: Dictionary<String, Any> = ["caption": post.caption, "mediaURL": post.imageUrl as Any, "likes": post.likes, "user_ID": FriendSystem.system.CURRENT_USER_ID, "time": post.time]

                        let firebasePost = FriendSystem().GROUP_REF.child(self.group.groupID).child("storage").child(post.postID)
                        firebasePost.setValue(store)

                        FriendSystem().GROUP_REF.child(self.group.groupID).child("posts").child(cellID).removeValue()

                        self.tableView.deleteRows(at: [IndexPath(row: row!, section: 0)] , with: UITableViewRowAnimation.fade)
                        self.tableView.endUpdates()
                        self.tableView.reloadData()

                        }
                    }
                }
            }
    }

In the tableviewcell: 在tableviewcell中:

func tick(timer: Timer) {

    guard let expiresAt = endDate else {
        return
    }
    let calendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)
    if let components = calendar?.components([.hour, .minute, .second], from: NSDate() as Date, to: expiresAt, options: []) {
        currentTime = formatDateComponents(components: components as NSDateComponents)
        self.timerLbl.text = currentTime

        if Date() >= endDate! {
            timer.invalidate()
        }

    }
}

func formatDateComponents(components: NSDateComponents) -> String {
    let hours = components.hour
    let minutes = components.minute
    let seconds = components.second

    return "\(hours):\(minutes):\(seconds)"
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM