我是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#的最佳部分,并添加了一些很好的语法糖。但这让我很开心!我确定这是一件我忽略的小事,但我无法弄清楚是什么。
答案 0 :(得分:1)
由于您为加速计更新处理程序创建了新的NSOperationQueue
,因此处理程序调用的所有内容也在一个单独的队列中运行,并与主运行循环隔离。我建议在主线程NSOperationQueue.mainQueue()
上运行该处理程序,或者通过主队列上的块将任何可以更新UI的东西移回主线程:
NSOperationQueue.mainQueue().addOperationWithBlock {
// do UI stuff here
}