Статьи

Создание и анимация пользовательских интерфейсов с помощью фигур на iOS

Со времени выхода iOS 7 дизайны приложений для iOS кардинально изменились. С прекращением скеоморфизма пришли сплошные цвета. Значки и кнопки не пытаются воссоздать внешний вид реальных объектов и предпочитают быть схематичными.

Тени, градиенты, сложные элементы управления исчезли. Простота, ясность, эффективность — это новые цели для iOS 7, iOS 8 и iOS 9. Мы видим все больше и больше очень упрощенных интерфейсов, и вместо использования сложных изображений они часто используют простые геометрические формы. Круги. Прямоугольники. Треугольники. Как мы реализуем эти виды конструкций? Мы попытаемся ответить на этот вопрос с помощью новой платформы под названием  Shapes .

Представляем фигуры 

Shapes — это набор обёрток, который позволяет простые, но мощные способы рисования и изменения форм на iOS CAShapeLayer и UIBezierPath — это два основных ключевых элемента, на которых он построен.

CAShapeLayer является одним из подклассов CALayer, который позволяет рисовать кубический сплайн Безье в его координатном пространстве. А UIBezierPath — это класс, который определяет геометрический путь Безье. Эти два класса очень похожи, но служат совершенно разным целям. UIBezierPath используется для определения пути, а CAShapeLayer отвечает за его рисование. Оба класса имеют очень похожие свойства, но их не так просто использовать вместе, так как один использует платформу QuartzCore и CoreFoundation, а другой — из мира UIKit. С Shapes мы сокращаем разрыв между этими двумя.

DTShapeView

DTShapeView — это UIView, поддерживаемый CAShapeLayer вместо CALayer. Чтобы настроить его форму, просто создайте UIBezierPath, передайте его этому классу, и его свойства будут автоматически преобразованы в базовые свойства CAShapeLayer. Мало того, он также предоставляет набор свойств, которые позволяют использовать свойства и перечисления CoreGraphics, такие как CGLineCap и CGLineJoin, полностью обходя QuartzCore и CoreFoundation. 

Теперь иметь геометрическую форму — это круто, но что мы можем с этим сделать? Первое, что приходит на ум, — это взгляд на прогресс.

Прогресс просмотров

В двух словах, что такое прогресс? Это какая-то геометрическая фигура, скорее всего, бар, который заполняется от начала до конца. Если вы знакомы с CAShapeLayer, он должен немедленно позвонить в колокол, потому что он имеет свойства strokeStart и strokeEnd, которые точно определяют это. Познакомьтесь с DTProgressView, подклассом DTShapeView, который основывается на них.

По умолчанию DTProgressView заполняет все границы представления, заполняя представление слева направо. И все, что вам нужно сделать, чтобы создать представление прогресса, это перетащить его на ваш вид, установить штрих-цвет, и ваш обзор прогресса готов!

self.simpleProgressView.strokeColor = [UIColor greenColor];

Всякий раз, когда ваш прогресс меняется, просто позвоните 

[self.simpleProgressView setProgress:progress animated:YES];

И изменение прогресса будет анимированным. Конечно, продолжительность и функции анимации настраиваются. Но давайте не будем останавливаться на достигнутом! DTProgressView является подклассом DTShapeView, что означает, что он допускает любую геометрическую форму, которая может быть определена UIBezierPath. И вы можете сойти с ума и сделать что-то вроде этого: 

Что если мы хотим вырезать одну фигуру из другой? Иногда вам нужно руководство в вашем приложении, и вы хотите затемнить некоторые части интерфейса, чтобы переключить внимание пользователя на какой-то элемент пользовательского интерфейса. Иногда вы можете создать сборщик фотографий, который выбирает округленную фотографию пользователя, и вы хотите скрыть все остальное, кроме этого круга, который будет выбран. Это где DTDimmingView приходит.

Тусклые виды

По умолчанию DTDimmingView затемняет все границы. Поэтому все, что вам нужно сделать, это создать UIBezierPath, который определит, какая часть представления должна быть видимой, и установите для него свойство visiblePath.

UIBezierPath * roundedPath = [UIBezierPath bezierPathWithOvalInRect: CGRectInset (self.view.bounds, 100, 100)]; self.dimmingDynamicView.visiblePath = roundedPath;

Хорошо, рисование фигур — это весело, но как насчет взаимодействия с ними?

Фасонные кнопки

DTShapeButton — это UIButton с добавленным DTShapeView в качестве подпредставления. По умолчанию этот DTShapeView заполняет весь кадр кнопки. А поскольку любое свойство DTShapeView является анимируемым, изменение его пути можно использовать для анимации изменения геометрической формы кнопки. Но мы должны быть осторожны, потому что изменение формы не так просто, как может показаться. Почему это?

Кнопка загрузки AppStore

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

Если два пути имеют разное количество контрольных точек или сегментов, результаты не определены .

Вот как UIBezierPath рисует скругленный прямоугольник:

Он имеет гораздо больше контрольных точек и сегментов, чем круг. При изменении путей CAShapeLayer пытается интерполировать между этими линиями и, очевидно, терпит неудачу, потому что число линий и контрольных точек различается. Вы можете столкнуться с совершенно неожиданными анимациями, такими как завихрение или частичное рисование видов Это не приемлемо.

Одним из очевидных решений было бы рисовать фигуры с одинаковым количеством контрольных точек. По своей сути круг можно нарисовать в виде закругленного квадрата с угловым радиусом, равным половине его стороны. И этот подход прекрасно работает, но только на симуляторе iOS, а не на реальном устройстве. Это происходит из-за оптимизации устройства; Похоже, iOS автоматически оценивает, как можно рисовать фигуры. И даже если вы создали фигуру в виде скругленного прямоугольника, если ее можно нарисовать как простой круг, она будет нарисована как круг с таким же количеством контрольных точек, что и круг.

Здесь есть разные решения, которые могут вам помочь. 

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

Начальное состояние:

Среднее состояние:

Законченное состояние:

 

И вот результат в движении:

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

Кнопка записи голосовых заметок

Решение здесь немного другое. Вместо того, чтобы изменять саму форму кнопки, мы решили анимировать маску CAShapeLayer. Это возможно, потому что маска CALayer сама может быть CAShapeLayer. Для достижения неявной анимации мы написали класс DTAnimatableShapeLayer. После этого анимация изменения состояния кнопки заключается в простом уменьшении или увеличении маски вокруг кнопки. 

Вывод

Фигуры — это мощный инструмент для рисования фигур и элементов управления на iOS. И это стало еще более мощным с XCode 6 и Swift, потому что на игровых площадках Swift вы можете наблюдать, как UIBezierPath строит себя, шаг за шагом, строку за строкой. Это только начало для Shapes, и мы не можем дождаться, чтобы увидеть, что вы сможете с ним построить!

PS Здесь вы можете найти все, о чем мы говорили,  например, проект Shapes . И, конечно же, установка через  CocoaPods  поддерживается.

Ссылки:
Фигуры на GitHub
CKShapeView
Анимация рисования CGPath с помощью CAShapeLayer