带计时器的绘图应用程序。绘图时间少于 20 秒后计时器开始滞后

Posted

技术标签:

【中文标题】带计时器的绘图应用程序。绘图时间少于 20 秒后计时器开始滞后【英文标题】:Drawing app with timer. Timers begins to lag after drawing for less than 20 seconds 【发布时间】:2019-09-05 08:24:09 【问题描述】:

我在一些朋友的帮助下构建了这个应用程序。我真的不知道代码是如何工作的。

它基本上使用苹果铅笔记录数据(平板电脑上的时间、苹果铅笔的速度、笔划数等)。但是,随着时间的流逝和绘制次数的增加,计时器会与实时不同步。

这个应用程序的目的是用于痴呆症研究,我让患者在平板电脑上画画,并收集相关信息。如果计时器发臭,我就无法进行研究。

我已尝试禁用所有计时器,但延迟保持不变。我有一种感觉,这与笔画的采样方式有关。我只需要一个笔画数,我不需要它来显示每分钟的笔画数(这是它目前正在做的事情)。我认为中风计数器可能是原因???

这是程序: https://drive.google.com/open?id=1lwzKwG7NLcX1qmE5yoxsdq5HICV2TNHm

class StrokeSegment 
    var sampleBefore: StrokeSample?
    var fromSample: StrokeSample!
    var toSample: StrokeSample!
    var sampleAfter: StrokeSample?
    var fromSampleIndex: Int

    var segmentUnitNormal: CGVector 
        return segmentStrokeVector.normal!.normalized!
    

    var fromSampleUnitNormal: CGVector 
        return interpolatedNormalUnitVector(between: previousSegmentStrokeVector, and: segmentStrokeVector)
    

    var toSampleUnitNormal: CGVector 
        return interpolatedNormalUnitVector(between: segmentStrokeVector, and: nextSegmentStrokeVector)
    

    var previousSegmentStrokeVector: CGVector 
        if let sampleBefore = self.sampleBefore 
            return fromSample.location - sampleBefore.location
         else 
            return segmentStrokeVector
        
    

    var segmentStrokeVector: CGVector 
        return toSample.location - fromSample.location
    

    var nextSegmentStrokeVector: CGVector 
        if let sampleAfter = self.sampleAfter 
            return sampleAfter.location - toSample.location
         else 
            return segmentStrokeVector
        
    

    init(sample: StrokeSample) 
        self.sampleAfter = sample
        self.fromSampleIndex = -2
    

    @discardableResult
    func advanceWithSample(incomingSample: StrokeSample?) -> Bool 
        if let sampleAfter = self.sampleAfter 
            self.sampleBefore = fromSample
            self.fromSample = toSample
            self.toSample = sampleAfter
            self.sampleAfter = incomingSample
            self.fromSampleIndex += 1
            return true
        
        return false
    


class StrokeSegmentIterator: IteratorProtocol 
    private let stroke: Stroke
    private var nextIndex: Int
    private let sampleCount: Int
    private let predictedSampleCount: Int
    private var segment: StrokeSegment!

    init(stroke: Stroke) 
        self.stroke = stroke
        nextIndex = 1
        sampleCount = stroke.samples.count
        predictedSampleCount = stroke.predictedSamples.count
        if (predictedSampleCount + sampleCount) > 1 
            segment = StrokeSegment(sample: sampleAt(0)!)
            segment.advanceWithSample(incomingSample: sampleAt(1))
        
    

    func sampleAt(_ index: Int) -> StrokeSample? 
        if index < sampleCount 
            return stroke.samples[index]
        
        let predictedIndex = index - sampleCount
        if predictedIndex < predictedSampleCount 
            return stroke.predictedSamples[predictedIndex]
         else 
            return nil
        
    

    func next() -> StrokeSegment? 
        nextIndex += 1
        if let segment = self.segment 
            if segment.advanceWithSample(incomingSample: sampleAt(nextIndex)) 
                return segment
            
        
        return nil
    

例如在真正的 25 秒时,应用程序会在 20 秒时显示总时间。

【问题讨论】:

【参考方案1】:

Timer 不是计算经过时间的东西。它是一种用于在经过一段时间后触发执行的工具。但是只是“在”一段时间过去之后,而不是“恰好在”一段时间过去之后。因此,例如执行以下操作:

var secondsElapsed: TimeInterval = 0.0
let timeInitiated = Date()

Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true)  _ in
    secondsElapsed += 1
    print("\(secondsElapsed) seconds should have passed but in reality \(Date().timeIntervalSince(timeInitiated)) second have passed")

您会发现两者并不相同,但非常接近。但是一旦我添加了一些额外的工作,就像这样:

var secondsElapsed: TimeInterval = 0.0 让 timeInitiated = Date()

func countTo(_ end: Int) 
    var string = ""
    for i in 1...end 
        string += String(i)
    
    print("Just counted to string of lenght \(string.count)")


Timer.scheduledTimer(withTimeInterval: 1.0/60.0, repeats: true)  _ in
    countTo(100000)


Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true)  _ in
    secondsElapsed += 1
    print("\(secondsElapsed) seconds should have passed but in reality \(Date().timeIntervalSince(timeInitiated)) second have passed")

我们会遇到类似“应该已经过了 14.0 秒,但实际上已经过了 19.17617702484131 秒”这样的情况。

我们让应用程序繁忙,所以它没有时间正确计数。

在您的情况下,您将需要使用以下两种解决方案之一:

    如果您对经过的时间感兴趣,只需使用timeIntervalSince,如第一个代码 sn-p 所示。 如果您需要确保每N 秒触发一次,您应该优化您的代码,考虑使用多线程...但请记住,您只能接近“每N 秒”,这应该是不可能的保证每N 秒执行一次。

【讨论】:

以上是关于带计时器的绘图应用程序。绘图时间少于 20 秒后计时器开始滞后的主要内容,如果未能解决你的问题,请参考以下文章

AVAudioPlayer 可以在预定义的时间间隔触发事件吗?

如何在绘图中创建按组着色的带注释的条

canvas绘图详解-03-绚丽的倒计时效果

Android 按键 每次调节亮度时亮度框消失时间需要重新倒计时

C# 版本的 计时器类:精确到微秒 秒后保留一位小数 支持年月日时分秒带单位的输出

炫丽的倒计时效果Canvas绘图与动画基础