CALayer委托仅在使用Swift时偶尔被调用

时间:2014-11-21 05:05:45

标签: ios swift uiview calayer

我是IOS和Swift的新手,所以我开始将Apple的Accelerometer示例代码移植到Swift。

这一切都很简单。由于Accelerometer API已被弃用,我使用的是Core Motion,它运行得很好。我也转到故事板。

我遇到的问题是我的图层委托很少被调用。它将持续几分钟而且永远不会被调用,然后它将被调用40次,然后返回到不被调用。如果我上下文切换,代理将被调用,并且将显示其中一个子图层,但是有32个子图层,我还没有看到它们全部被绘制。绘制的内容似乎很好 - 问题是当我调用setNeedsDisplay()并让所有子图层被绘制时,让委托实际被调用。

我已经检查过每个子图层是否具有正确的边界和框架尺寸,并且我已经检查过以确保在获取每个加速度计点后调用setNeedsDisplay()。

如果我附加一个乐器,我看到帧速率通常为零,但偶尔会有更高的数字。

我的猜测是跑步循环不循环。在跑步循环中实际上什么也没有,我不知道在哪里放一个。在ViewDidLoad委托中,我设置了加速度计的更新速率,并调用更新视图中子图层的函数。其他一切都是事件驱动的,所以我不知道我对运行循环做了什么。

我尝试过创建CALayers,并将它们添加为子图层。我还尝试将GraphViewSegment类设为UIView,因此它拥有自己的图层。

在Objective C中编写的版本可靠地运行。

此应用程序的工作方式是,加速度值显示在屏幕左侧,并向右滚动。为了使其高效,新的加速度值被写入一个小的子层,该子子层保存32个时间值的图形。当它完整时,整个子图层一次只向右移动一个像素,新的(或回收的)片段在屏幕的左侧占据一席之地。

这里是将未更改的段向右移动一个像素的代码:

    for s: GraphViewSegment in self.segments {
         var position = s.layer.position
        position.x += 1.0;
        s.layer.position = position;
        //s.layer.hidden = false
        s.layer.setNeedsDisplay()
    }

我认为setNeedsDisplay在这里是非常必要的,因为当左边的线段获得一个新的线段时,它会调用该层。

此处了解如何添加新图层:

public func addSegment() -> GraphViewSegment {
    // Create a new segment and add it to the segments array.
    var segment = GraphViewSegment(coder: self.coder)
    // We add it at the front of the array because -recycleSegment expects the oldest segment
    // to be at the end of the array. As long as we always insert the youngest segment at the front
    // this will be true.
    self.segments.insert(segment, atIndex: 0)
 // this is now a weak reference

    // Ensure that newly added segment layers are placed after the text view's layer so that the text view
    // always renders above the segment layer.
    self.layer.insertSublayer(segment.layer, below: self.text.layer)
    // Position it properly (see the comment for kSegmentInitialPosition)
    segment.layer.position = kSegmentInitialPosition;
    //println("New segment added")
    self.layer.setNeedsDisplay()
    segment.layer.setNeedsDisplay()
    return segment;

}

此时我很困惑。我试过在整个地方调用setNeedsDisplay,包括拥有UIView。我试过制作子层UIViews,我试过让它们不是UIViews。无论我做什么,行为总是一样的。

所有内容都在viewDidLoad中设置:

override func viewDidLoad() {
    super.viewDidLoad()

    pause.possibleTitles?.setByAddingObjectsFromArray([kLocalizedPause, kLocalizedResume])
    isPaused = false
    useAdaptive = false
    self.changeFilter(LowpassFilter)

    var accelerometerQueue = NSOperationQueue()
    motionManager.accelerometerUpdateInterval = 1.0 / kUpdateFrequency 
    motionManager.startAccelerometerUpdatesToQueue(accelerometerQueue,
        withHandler:
            {(accelerometerData: CMAccelerometerData!, error: NSError!) -> Void in
                self.accelerometer(accelerometerData)})

    unfiltered.isAccessibilityElement = true
    unfiltered.accessibilityLabel = "unfiltered graph"
    filtered.isAccessibilityElement = true
    filtered.accessibilityLabel = "filtered graph"
}

func accelerometer (accelerometerData: CMAccelerometerData!) {
    if (!isPaused) {
        let acceleration: CMAcceleration = accelerometerData.acceleration
        filter.addAcceleration(acceleration)
        unfiltered!.addPoint(acceleration.x, y: acceleration.y, z: acceleration.z)
        filtered!.addPoint(filter.x, y: filter.y, z: filter.z)
        //unfiltered.setNeedsDisplay()
    }
}

有什么想法吗?

我非常喜欢Swift作为一种语言 - 它需要Java和C#的最佳部分,并添加了一些很好的语法糖。但这让我很开心!我确定这是一件我忽略的小事,但我无法弄清楚是什么。

1 个答案:

答案 0 :(得分:1)

由于您为加速计更新处理程序创建了新的NSOperationQueue,因此处理程序调用的所有内容也在一个单独的队列中运行,并与主运行循环隔离。我建议在主线程NSOperationQueue.mainQueue()上运行该处理程序,或者通过主队列上的块将任何可以更新UI的东西移回主线程:

NSOperationQueue.mainQueue().addOperationWithBlock {
    // do UI stuff here
}