2014-11-21 3 views
2

Я новичок в IOS и Swift, поэтому я начал с портирования примерного кода акселерометра Apple на Swift.Делегат CALayer вызывается только иногда, когда используется Swift

Все это было довольно просто. Поскольку API Accelerometer устарел, я вместо этого использовал 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

Поскольку вы создали новый NSOperationQueue для вашего обработчика обновлений акселерометра, все вызовы обработчика также выполняются в отдельной очереди, секвестрированной из основного цикла запуска. Я хотел бы предложить, либо работает этот обработчик на главном потоке NSOperationQueue.mainQueue() или переместить все, что может обновить интерфейс обратно в основной поток через блок на главной очереди:

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

Большое спасибо! – Andromeda

+0

Я потратил пару дней, разрывая волосы на это, но я неправильно предположил, что мой обработчик выполняет в mainThread. Вы сохранили то, что у меня осталось. Теперь я получаю согласованные 60 кадров в секунду. – Andromeda

Смежные вопросы