简体   繁体   English

计时器在 UItableview 单元格中重用

[英]Timer being reused in UItableview cell

I am working on my UITableviewcell where I have a UIView for user to "tap" and start timer.我正在处理我的 UITableviewcell,在那里我有一个 UIView 供用户“点击”并启动计时器。 I am creating my UItableview cell components completely programmatically and adding timer components within UITableviewcell class instead of the parent view controller.我正在完全以编程方式创建我的 UItableview 单元格组件,并在 UITableviewcell 类而不是父视图控制器中添加计时器组件。

The problem I am having is that my timer is supposed to be distinct for each UITableviewcell.我遇到的问题是我的计时器对于每个 UITableviewcell 应该是不同的。 User should be able to start timer in one cell and scroll through other cells to start/stop another.用户应该能够在一个单元格中启动计时器并滚动其他单元格以启动/停止另一个单元格。 However, when I "tap" on my first cell, the timer gets started not only on the first one, but I can also see that it is starting on the 3rd cell.但是,当我“点击”我的第一个单元格时,计时器不仅在第一个单元格上启动,而且我还可以看到它从第三个单元格开始。

I tried to rectify this issue by using PrepareForReuse and invalidate timer, but it also result in the 1st cell's timer to be invalidated.我试图通过使用 PrepareForReuse 和使计时器失效来纠正这个问题,但这也导致第一个单元格的计时器失效。

I tried to research on this issue, but have found no exact match.我试图研究这个问题,但没有发现完全匹配。 Could anyone kindly advise how I can resolve this issue?任何人都可以建议我如何解决这个问题?

class ActiveExerciseTableViewCell: UITableViewCell, UITextFieldDelegate {
    
    static let tableviewidentifier = "activeExerciseTableViewCell"
    
    var tableviewContentViewTabBarHeight = CGFloat()
    
    var exerciseCellKeyboardHeight = CGFloat()
    
    var restTimer = Timer()
    var restTimeRemaining: Int = 180
    var isRestTimerRunning: Bool = false

    let activeExerciseTimerUIView: UIView = {
        let activeExerciseTimerUIView = UIView()
        activeExerciseTimerUIView.backgroundColor = .darkGray
        return activeExerciseTimerUIView
    }()
    
    let timerLabel: UILabel = {
        let timerLabel = UILabel()
        timerLabel.text = "180"
        timerLabel.textColor = .black
        timerLabel.adjustsFontSizeToFitWidth = true
        return timerLabel
    }()


    override func prepareForReuse() {
        super.prepareForReuse()
        
        if self.isRestTimerRunning == false {
            restTimer.invalidate()
            restTimeRemaining = 180
            timerLabel.text = prodTimeString(time: TimeInterval(restTimeRemaining))
        }

    }


func setUpActiveExerciseUIViewLayout(){
        
        timerLabel.translatesAutoresizingMaskIntoConstraints = false
        timerLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        timerLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: (contentView.frame.height-tableviewContentViewTabBarHeight)*0.55).isActive = true
        timerLabel.widthAnchor.constraint(equalToConstant: contentView.frame.width * 0.7).isActive = true
        timerLabel.heightAnchor.constraint(equalToConstant: 80).isActive = true
        timerLabel.font = .boldSystemFont(ofSize: 64)
        
        
        activeExerciseTimerUIView.translatesAutoresizingMaskIntoConstraints = false
        activeExerciseTimerUIView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        activeExerciseTimerUIView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: (contentView.frame.height-tableviewContentViewTabBarHeight)*0.25).isActive = true
        activeExerciseTimerUIView.widthAnchor.constraint(equalToConstant: 225).isActive = true
        activeExerciseTimerUIView.heightAnchor.constraint(equalToConstant: 225).isActive = true

        let timerStartGesture = UITapGestureRecognizer(target: self, action: #selector(playTapped))
        timerStartGesture.numberOfTapsRequired = 1
        
        activeExerciseTimerUIView.addGestureRecognizer(timerStartGesture)
        activeExerciseTimerUIView.isUserInteractionEnabled = true


    }


 @objc func playTapped(_ sender: Any) {
        
        if isRestTimerRunning == false {
            restTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(step), userInfo: nil, repeats: true)
            self.isRestTimerRunning = true
        }
      
    }
    
    
    @IBAction func pauseTapped(_ sender: Any) {
        restTimer.invalidate()
    }
    

    @IBAction func resetTapped(_ sender: Any) {
        restTimer.invalidate()
        restTimeRemaining = 180
        timerLabel.text = prodTimeString(time: TimeInterval(restTimeRemaining))
    }
    
    
    @objc func step() {
        if restTimeRemaining > 0 {
            restTimeRemaining -= 1
        } else {
            restTimer.invalidate()
            restTimeRemaining = 180
        }
        timerLabel.text = prodTimeString(time: TimeInterval(restTimeRemaining))
    }
    
    public func prodTimeString(time: TimeInterval) -> String {
        let Minutes = Int(time) / 60 % 60
        let Seconds = Int(time) % 60

        return String(format: "%02d:%02d", Minutes, Seconds)
    }

Iam posting my own answer, issue had been resolved the issue by我发布了我自己的答案,问题已解决

  1. Creating a separate view model that stores timer.创建一个单独的视图模型来存储计时器。
  2. Initializing UITableView cell by using unique "reuseID" from tableview delegate.使用来自 tableview 委托的唯一“reuseID”初始化 UITableView 单元格。
SetViewModel

class SetViewModel {
    
    init(set: SetInformation) {
        self.set = set
        self.secondsRemaining = set.restTime ?? 0
        self.elapsedSeconds = 0
        self.isTimerRunning = false
        self.currentBackgroundDate = nil
        self.buttonPressCount = 0

    }
    
    var set: SetInformation
    var secondsRemaining: Int
    var elapsedSeconds: Int
    var isTimerRunning: Bool
    var currentBackgroundDate: Date?
    var buttonPressCount: Int
    
    weak var delegate: SetViewModelDelegate?
    
    // MARK: - Timer
    private var timer: Timer?
    
    func startTimer() {
        //stopTimer()
        isTimerRunning = true
        timer = .scheduledTimer(withTimeInterval: 1, repeats: true, block: { [weak self] timer in
            guard let self = self else {
                // If the model was discarded during the runtime of the timer
                timer.invalidate()
                return
            }
            
            if self.secondsRemaining > 0 && self.elapsedSeconds < (self.secondsRemaining + self.elapsedSeconds) {
                self.secondsRemaining -= 1
                self.elapsedSeconds += 1
            } else {
                timer.invalidate()
            }
            
            self.delegate?.setTimerUpdated(model: self)
            
        })
    }
    
    func stopTimer() {
        timer?.invalidate()
        isTimerRunning = false
        self.delegate?.setTimerUpdated(model: self)
    }

UItableView Delegate UItableView 委托


 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let reuseID = String(indexPath.row) + ActiveExerciseTableViewCell.tableviewidentifier
        
        activeExerciseTableView.register(ActiveExerciseTableViewCell.self, forCellReuseIdentifier: reuseID)
        
        let cell = activeExerciseTableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath) as! ActiveExerciseTableViewCell
        
        //configure set information for the tableview cell based on setviewmodel
        cell.configure(setViewModel: setViewModels[indexPath.row], currentHeartRate: userCurrentHeartRate ?? 00)
        
        cell.setDescriptionLabel.text = "Set \(String(indexPath.row + 1)) of \(activeExerciseList[activeDisplayedExericseRow].setInformation!.count)"
        
        cell.tableviewContentViewTabBarHeight = contentViewTabBarHeight
        cell.delegate = self

        return cell
    }

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

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