Ich habe eine Reihe von Elementen, die ich animieren möchte. Ich füge eine Verzögerung pro Inkrement in den Index ein. Einige Elemente werden jedoch gleichzeitig animiert (normalerweise zwei gleichzeitig).

Ich bin mir nicht sicher, ob es daran liegt, dass die for-Schleife nicht mit konstanter Geschwindigkeit iteriert oder ob der Hauptthread zu viele Aufrufe hat.

* Hinweis - Die FlipCard ist nicht das Problem. Wenn ich nur cell.alpha = 0 setze, bleibt das Problem weiterhin bestehen.

Gif mit Problem - https://imgur.com/a/0e8JXyw

private func animateCells() {
    if collectionView.visibleCells.count < indexToFlip { return }
    let numberOfCards = collectionView.visibleCells.count
    var currentIndex = 1
    var delayNextFlipMiliSeconds = 0
    for section in 0 ..< collectionView.numberOfSections {
        for row in 0 ..< collectionView.numberOfItems(inSection: section) {
            guard let cell = collectionView.cellForItem(at: IndexPath(row: row, section: section)) as? CardCell else { continue }
            print(delayNextFlipMiliSeconds)
            print(NSDate().timeIntervalSince1970)
            DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(delayNextFlipMiliSeconds)) {
                cell.flipCard() {
                    currentIndex += 1
                    if currentIndex == numberOfCards {
                        UIView.animate(withDuration: 0.5, animations: {
                            self.playButton.alpha = 1
                        })
                    }
                }
            }
            delayNextFlipMiliSeconds += 200
        }
    }
}

Debug-Drucke ..

0
1547749398.849406
200
1547749398.850255
400
1547749398.850312
600
1547749398.850347
800
1547749398.8503752
1000
1547749398.850401
1200
1547749398.850429
1400
1547749398.850459
1600
1547749398.850482
1800
1547749398.850504
2000
1547749398.850525
2200
1547749398.850559
2400
1547749398.850597
2600
1547749398.85063
2800
1547749398.850657
3000
1547749398.850702
0
Mocha 17 Jän. 2019 im 21:27

3 Antworten

Beste Antwort

DispatchQueue.main ist eine serielle Warteschlange, die sicherstellt, dass die Aufgaben in der Reihenfolge ausgeführt werden, in der sie der Warteschlange hinzugefügt werden. Dispatch_async kehrt jedoch zurück, nachdem es zur Warteschlange hinzugefügt wurde und möglicherweise noch nicht fertig ist, und der Rest des Codes (for-Schleife) wird ausgeführt.

Was passiert ist, dass "for" -Schleifen mehrere Codeblöcke (Tasks) in die Hauptwarteschlange stellen, die in der Reihenfolge ausgeführt werden, aber der Ausführungszeitpunkt kann von der Verfügbarkeit des Prozessors abhängen. Einige Aufgaben werden möglicherweise schneller geplant als andere. Der Scheduler plant die Aufgabe zur Laufzeit und kann nicht vorhergesagt werden. Aus diesem Grund wird es nicht in regelmäßigen Abständen ausgeführt.

Sie können so etwas versuchen

Timer.scheduledTimer(withTimeInterval: delayNextFlipMiliSeconds, repeats: false) { (timer) in
         DispatchQueue.main.sync {
                //Your Code
            }
    }.fire()

Durch die Verwendung von DispatchQueue.main.sync wird sichergestellt, dass "Ihr Code" zuerst ausgeführt wird und dann zum Rest des Codes übergeht. Die Verwendung von DispatchQueue.main.sync verlangsamt jedoch auch Ihre Benutzeroberfläche aus Sicht des Benutzers, aber ich denke, dass dies vernachlässigbar sein wird.

1
Abhisheks 17 Jän. 2019 im 20:15

Können Sie UIView.animate () nicht mit einem Completion-Handler verwenden? Auf diese Weise können Sie die nächste Animation direkt übergeben, wenn die erste fertig ist.

0
Johannes Schidlowski 17 Jän. 2019 im 18:48

Ich habe beschlossen, mich nicht um GCD oder die for-Schleifenzeit zu kümmern und stattdessen einen Timer zu verwenden. Es funktioniert perfekt.

private func animateCells() {
    var cells: [CardCell] = []
    for section in 0 ..< collectionView.numberOfSections {
        for row in 0 ..< collectionView.numberOfItems(inSection: section) {
            guard let cell = collectionView.cellForItem(at: IndexPath(row: row, section: section)) as? CardCell else { continue }
            cells.append(cell)
        }
    }
    var animationIndex = 0
    Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (timer) in
        cells[animationIndex].flipCard()
        animationIndex += 1
        if animationIndex ==  cells.count {
            UIView.animate(withDuration: 0.5, animations: {
                self.playButton.alpha = 1
            })
            timer.invalidate()
        }
    }.fire()
}
0
Mocha 17 Jän. 2019 im 19:02