Как выполнить несколько пользовательских анимационных функций синхронно в Swift 4 - PullRequest
0 голосов
/ 21 мая 2018

Я работаю над приложением IOS, которое должно помочь людям в туристическом регионе разобраться в транспортных средствах.Чтобы им было кристально ясно, как добраться от А до Б, маршруты анимированы с помощью объектов аннотаций.Например, когда пользователь выбирает просмотр маршрута от A до D, объект канатной дороги скользит от A к B. После завершения объект Shuttle Bus движется по дороге от B до C, а затем катер, скользящий от C доД.

Итак, я написал следующие функции.Этот объект позволяет объекту transportMode (маленькому изображению лодки, канатной дороги и т. Д.) Скользить по прямой линии от A до B или от B до A.

func animateLinearRoute(transportMode: TransportAnnot, startcoor: 
    CLLocationCoordinate2D, destcoor: CLLocationCoordinate2D){

    UIView.animate(withDuration: 3, animations:
        {
            if (transportMode.coordinate.latitude == startcoor.latitude && transportMode.coordinate.longitude == startcoor.longitude) {
            transportMode.coordinate = destcoor
        } else {
            transportMode.coordinate = startcoor
        }

    })
}

Для перемещения объекта по нелинейному маршруту(обычно дорога), нарисованная на карте, я использую следующую функцию:

// pass mode of transport and coordinates along the travel route
func animateNonLinearRoute(transportMode: TransportAnnot, animroute: [CLLocationCoordinate2D]){
    let path = UIBezierPath()
    // get start point of route from coordinates and start drawing route
    let point = self.mapView.convert(animroute[0], toPointTo: self.mapView)
    path.move(to: point)
    // translate each coordinate along the route into a point in the view for drawing
    for coor in animroute {
        let point = self.mapView.convert(coor, toPointTo: self.mapView)
        path.addLine(to: point)
    }

    // create keyframe animation to move annotation along the previously drawn path
    let animation = CAKeyframeAnimation(keyPath: "position")
    animation.path = path.cgPath
    animation.duration = 5.0
    animation.isRemovedOnCompletion = false
    let transportview = self.mapView.view(for: transportMode)
    transportview?.layer.add(animation, forKey: "animate position along path")
    transportMode.coordinate = animroute[animroute.count - 1]
    CATransaction.commit()

}

Теперь полный маршрут может состоять из произвольной цепочки этих методов.Например, пользователь может выбрать точку, которая требует линейного маршрута -> нелинейного маршрута -> линейного маршрута -> нелинейного -> нелинейного.

В конечном итоге анимации должны выполняться строго последовательно, поэтомупользователь не будет сбит с толку (вторая анимация не должна запускаться, пока не завершится первая и т. д.).

Одним из соображений будет анимация ключевого кадра, подобная этой:

    UIView.animateKeyframes(withDuration: 8, delay: 0, options: .calculationModeLinear, animations: {

        UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 5/8, animations: {
            self.animateNonLinearRoute(transportMode: self.bus, animroute: self.br.coordinates)
        })

        UIView.addKeyframe(withRelativeStartTime: 5/8, relativeDuration: 3/8, animations: {
            self.animateLinearRoute(transportMode: self.cableCar, startcoor: self.lowerstation, destcoor: self.midstation)
        })

        // dynamically fill up as needed using appropriate relative start times and durations

    }, completion: nil)

Это не выполняет код синхронно, хотя.Я предполагаю, что это конфликтует с таймингами и ключевыми кадрами, определенными в функциях.

Я возился с пользовательскими закрытиями завершения и затем помещал каждый метод в закрытие завершения предыдущего, а также с очередями отправки.Но я не совсем понимаю их, потому что не смог достичь желаемого эффекта.И поскольку маршруты становятся длиннее, вложенные замыкания завершения не кажутся идеальным вариантом, поскольку они делают программу излишне сложной.Любые предложения высоко ценятся.

1 Ответ

0 голосов
/ 21 мая 2018

Полагаю, я нашел проблему.Так как animateNonLinearRoute запускает анимацию на CALayer, функция возвращается после запуска анимации, не ожидая ее завершения.Поэтому все функции в обработчике завершения методов будут выполняться после того, как анимация CALayer БЫЛА ПРОВЕДЕНА, НЕ ПОСЛЕ ТОГО, КАК ЭТО ЗАВЕРШЕНО.Простым хакерским решением было бы обернуть функции, которые должны быть выполнены после завершения анимации CALayer, в блок CATransaction:

    CATransaction.begin()
    CATransaction.setCompletionBlock({
        self.animateLinearRoute(transportMode: self.cableCar, startcoor: self.lowerstation, destcoor: self.midstation)
    })

    self.animateNonLinearRoute(transportMode: self.bus, animroute: self.br.coordinates)
    CATransaction.commit()

Мне бы хотелось услышать лучшее объяснение от того, кто понимает многопоточность и параллелизмв Swift и предложение о чистом способе связать несколько из этих вызовов функций.

...