UIBezierPath
и его базовый базовый класс CGPath
действительно используют переменное число контрольных точек при создании дуги в зависимости от угла дуги.
Таким образом, как вы обнаружили, если вы пытаетесь анимировать траекторию, которая включает в себя дуги с разными углами, вы получаете очень странные артефакты рисования при изменении количества контрольных точек на траектории.
Я предлагаю использовать сплайны Катмулла-Рома для создания ваших дуг. Сплайн Кэтмулла-Рома - это другой вид кубической кривой, где все контрольные точки находятся на кривой. Я узнал о сплайнах Catmull-Rom из выдающейся серии поваренных книг разработчиков iOS Эрики Садун (очень рекомендую к прочтению).
Если вы создадите сплайн Catmull-Rom с примерно 10 точками, равномерно распределенными по кругу, полученная в результате кривая будет очень близко приближаться к кругу. (Сначала я рекомендовал 8 контрольных точек, но я только что попробовал, и кривая, описывающая полный круг, не выглядит идеально круглой, пока у вас не будет хотя бы 10 контрольных точек.) Если вы хотите сгенерировать CGPath, который содержит дугу с меньший угол, создайте 10 контрольных точек, которые покрывают меньше или больше вашей дуги.
У меня есть проект на Github под названием TrochoidDemo , в котором используются сплайны Catmull Rom для создания реалистично выглядящих волн на воде. Вы можете игнорировать большинство из них, но он включает в себя файл SmoothCGPointsArray.swift, который будет принимать массив контрольных точек и использовать сплайны Catmull-Rom для возврата нового массива с промежуточными точками, которые описывают плавную кривую между вашими контрольными точками.
Функция, которую вы хотите в этом файле: smoothPointsInArray(_:granularity:adjustGranularity:)
Параметр adjustGranularity
по умолчанию имеет значение true. Этот параметр указывает функции изменять количество создаваемых точек сглаживания в зависимости от расстояния между контрольными точками. Вы захотите переопределить значение по умолчанию и вызвать функцию с помощью adjustGranularity=false
, поскольку для анимации всегда требуется одинаковое количество точек.
Вы бы просто вызвали эту функцию с массивом контрольных точек, который включает 10 точек для каждой дуги, которую вы хотите нарисовать, независимо от угла. Он будет возвращать массив CGPoints, а затем вы будете использовать результирующий массив GCPoints
для генерации UIBezierPath с помощью команд lineTo()
, а затем подать CGPath
из вашего UIBezierPath
в анимацию.
В исходном массиве точек, если вы хотите, чтобы заданная точка была острым углом, добавьте эту точку дважды в ряд. Если вы хотите отрезок, добавьте обе конечные точки отрезка дважды.
Вот как выглядит 8-точечная аппроксимация круга:
А вот как выглядит 10-точечная аппроксимация круга:
(На каждом изображении выше аппроксимация Catmull-Rom изображена черным, контрольные точки показаны синим, а нарисованный Какао круг нарисован синим. Обратите внимание, что Какао использует кривые Безье для создания своих кругов, поэтому Какао круги также являются приблизительными.)