Насколько Удобным Может Быть Api Рисования Ios?

Здравствуйте, меня зовут Виктор, я работаю в Exyte. Недавно мы выпустили в открытый исходный код нашу внутреннюю разработку — библиотеку для работы с векторной графикой и ее анимацией.

Ара .

Хочу поделиться впечатлениями от использования его в реальном проекте и рассказать о его преимуществах перед нативным API. Нам, разработчикам, часто приходится создавать нестандартные элементы управления и повторять одни и те же рутинные действия даже для простых эффектов:

  • Наследовать от UIView, чтобы переопределить drawRect
  • Опишите «сцену», используя устаревший API Core Graphics.
Попробуем создать нестандартный элемент управления и использовать его в качестве примера:

Насколько удобным может быть API рисования iOS?

  
  
  
  
  
  
   

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()

Мы решили избавиться от этой рутины и создали Macaw. С его помощью мы можем описать приведенную выше сцену в простом и функциональном стиле:

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()



Насколько удобным может быть API рисования iOS?

Слева: основная графика, справа: Ара Как видите, сцена представлена в виде модели, которую можно легко использовать повторно или модифицировать.

Например, с помощью узла Группа можно создавать большие иерархии объектов с относительными характеристиками (положением, прозрачностью и т. д.).



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. Но как разработчик я не хочу впадать в такие крайности ради простого эффекта.

Используя Macaw, такую анимацию можно создать гораздо приятнее:

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()



Насколько удобным может быть API рисования iOS?

Да, это весь код. Есть ли накладные расходы по сравнению с «чистой» основной графикой? Под капотом 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)



Насколько удобным может быть API рисования iOS?

Даже если сцена сложная, во время анимации будет перерисовываться только область поддерева, которое мы меняем.

Как видите, с помощью Macaw мы можем добиться производительности «чистой» базовой графики, но с более читаемым и простым в обслуживании кодом.

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

Мы постоянно работаем над улучшениями и будем рады вашим советам и помощи.

Теги: #разработка для iOS #Swift #Core Graphics #macaw #с открытым исходным кодом #Разработка под iOS #Cocoa #Swift

Вместе с данным постом часто просматривают: