Здравствуйте, меня зовут Виктор, я работаю в Exyte. Недавно мы выпустили в открытый исходный код нашу внутреннюю разработку — библиотеку для работы с векторной графикой и ее анимацией.
Ара .
Хочу поделиться впечатлениями от использования его в реальном проекте и рассказать о его преимуществах перед нативным API. Нам, разработчикам, часто приходится создавать нестандартные элементы управления и повторять одни и те же рутинные действия даже для простых эффектов:
- Наследовать от UIView, чтобы переопределить drawRect
- Опишите «сцену», используя устаревший API Core Graphics.
Мы решили избавиться от этой рутины и создали Macaw. С его помощью мы можем описать приведенную выше сцену в простом и функциональном стиле:let circlePath1 = UIBezierPath( arcCenter: center, radius: r, startAngle: offset, endAngle: offset + CGFloat(angle), clockwise: true) let circlePath2 = UIBezierPath( arcCenter: center, radius: r, startAngle: offset, endAngle: offset + CGFloat(angle), clockwise: false) UIColor.init(colorLiteralRed: 0.784, green: 0.784, blue: 0.784, alpha: 1.0).
setStroke() UIColor.clear.setFill() circlePath1.lineWidth = 10.0 circlePath1.stroke() UIColor.white.setStroke() circlePath2.lineWidth = 10.0 circlePath2.stroke()
let circle = Circle(cx: Double(center.x), cy: Double(center.y), r: r)
self.node = [
circle.arc(shift: -1.0 * M_PI_2, extent: angle)
.
stroke(fill: Color.rgb(r: 200, g: 200, b: 200), width: 10.0),
circle.arc(shift: -1.0 * M_PI_2 + angle, extent: 2 * M_PI - angle)
.
stroke(fill: Color.white, width: 10.0)
].
group()
Слева: основная графика, справа: Ара Как видите, сцена представлена в виде модели, которую можно легко использовать повторно или модифицировать.
Например, с помощью узла Группа можно создавать большие иерархии объектов с относительными характеристиками (положением, прозрачностью и т. д.).
let sceneNode = [
shape1,
shape2,
.
[ subshape1, subshape2 .
[.
].
group() ].
group(place: Transform.move(dx: 10.0, dy: 10.0).
scale(sx: 0.5, sy: 0.5), opacity: 0.9) ].
group()
Трудно даже представить, сколько усилий потребуется для создания такой сцены с использованием «чистого» Core Graphics API. Чтобы использовать эту красоту, вам просто нужно наследовать от MacawView или использовать MacawView в качестве контейнера.
После этого из коробки начнется «волшебство», например, изменения в модели автоматически вызовут перерисовку контента.
Но для создания красивого эффекта нужна еще одна вещь – анимация.
Используя Core Graphics, у нас есть два варианта:
- CAShapeLayer в сочетании с CABasicAnimation. Это достаточно просто, но код по-прежнему выглядит устаревшим:
let scaleTransform = CGAffineTransform.init(scaleX: 0.1, y: 0.1)
let scaleAnimation = CABasicAnimation(keyPath: "transform")
scaleAnimation.toValue = CATransform3DMakeAffineTransform(scaleTransform)
scaleAnimation.duration = 1.0
scaleAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
scaleAnimation.autoreverses = true
self.layer.add(scaleAnimation, forKey: "scale_animation")
- Используйте обратный вызов CADisplayLink, чтобы вручную перерисовать содержимое UIView. Но как разработчик я не хочу впадать в такие крайности ради простого эффекта.
let scaleAnimation = self.node.placeVar.animation(to: GeomUtils.centerScale(node: self.node, sx: 0.1, sy: 0.1), during: 2.0).
easing(.
easeOut)
scaleAnimation.autoreversed().
play()
Да, это весь код. Есть ли накладные расходы по сравнению с «чистой» основной графикой? Под капотом Macaw используется CAKeyframeAnimation, и для расчета кадров анимации требуется некоторое время (в зависимости от сложности модели).
В противном случае это те же вызовы Core Graphics. Хорошо, а как насчет анимации содержимого сцены? Можно ли анимировать состояние объектов модели или анимировать все дерево модели? К сожалению, нет возможности как-то оптимизировать этот процесс; единственное решение — перерисовать всю модель вручную.
Хорошая новость в том, что у Macaw для этого есть очень удобный API. Проведем рефакторинг кода нашего элемента управления, чтобы было проще создавать дерево объектов: contentNode(angle: Double) -> [Node] {
.
let circle = Circle(cx: Double(center.x), cy: Double(center.y), r: r) let text = Text(text: "\(value)", font: Font(name: "System", size: 38), fill: Color.white) let textCenter = GeomUtils.center(node: text) text.place = Transform.move(dx: Double(center.x) - textCenter.x, dy: Double(center.y) - textCenter.y) return [ text, circle.arc(shift: -1.0 * M_PI_2, extent: angle) .
stroke(fill: Color.rgb(r: 200, g: 200, b: 200), width: 10.0), circle.arc(shift: -1.0 * M_PI_2 + angle, extent: 2 * M_PI - angle) .
stroke(fill: Color.white, width: 10.0) ] } .
self.node = contentNode(angle: angle).
group()
Теперь мы можем создавать анимацию контента, заменяя содержимое поддерева внутри модели в каждом кадре: guard let rootNode = self.node as? Group else {
return
}
rootNode.contentsVar.animate({ t -> [Node] in
return self.contentNode(angle: 2 * M_PI * t)
}, during: 2.0)
Даже если сцена сложная, во время анимации будет перерисовываться только область поддерева, которое мы меняем.
Как видите, с помощью Macaw мы можем добиться производительности «чистой» базовой графики, но с более читаемым и простым в обслуживании кодом.
Многое осталось необъяснимым, но я надеюсь, что этот обзор вдохновит вас использовать нашу библиотеку в своем проекте.
Мы постоянно работаем над улучшениями и будем рады вашим советам и помощи.
Теги: #разработка для iOS #Swift #Core Graphics #macaw #с открытым исходным кодом #Разработка под iOS #Cocoa #Swift
-
Яндекс.такси Вводит Новые Тарифы
19 Oct, 24 -
Mylifeorganized: Папки Против Контекстов
19 Oct, 24 -
Garanthost.ru Не Будет Принимать Грузин
19 Oct, 24 -
Снова Прогнозирование, Часть 1
19 Oct, 24 -
Amp-Сайты – Попадитесь В Ловушку И Выиграйте
19 Oct, 24 -
Матричная Революция…
19 Oct, 24