Этот пост является переводом статьи Swift: around and around!, автором которой является молодой инженер Michael Teeuw.
Сам автор стал немного известнее в Рунете после создания «умного» зеркала
В этой же статье Майкл поделился впечатлениями о новом языке программирования Swift и выложил исходный код своих экспериментов, посвященных распознаванию жестов
Хотя большинство постов в моём блоге посвящены всему, что связано с электроникой, этот пост будет о такой стороне технологии, как разработка программного обеспечения. Кроме недавно снова проснувшейся любви к электронике у меня есть огромная страсть к разработке программного обеспечения и всему «яблочному». Эти две страсти послужили причиной для того, чтобы начать разрабатывать приложения для iPhone, изучая программирование на языке Objective-C в прошлом году. И несмотря на то, что два моих приложения QuickHue и Bermuda в конечном счете принесли мне некоторый доход, который я мог использовать для любимых занятий, мне до сих пор нравится тратить свободное время на разработку приложений.
Недавно компания «Apple» объявила о новом языке программирования «Swift». И хотя моей первой реакцией было: «Что? Я зря учил Objective-C?!», вскоре я увидел красоту этого нового языка: он спасает от печати БОЛЬШОГО количества текста и, таким образом, позволяет вам за короткое время превращать идеи в рабочие продукты.
Несмотря на то, что он находится в стадии бета-тестирования, и я все еще должен освоить новый синтаксис, уже сейчас разработка приложений этим новым способом приносит массу удовольствия. Одним из моих самых больших экспериментов было создание своего собственного Распознавателя Жестов. В этом посте я поделюсь процессом и концепцией Распознавателя Жестов. Так как написано много постов, где сравнивается Objective-C и Swift, я не стану углубляться в синтаксис Swift. Приготовьтесь главным образом к математической абракадабре.
Распознаватель Жестов
Перед погружением в процесс давайте займем немного времени, чтобы объяснить, как используется Распознавателя Жестов, заглянув в документацию «Apple»:
Распознаватели жестов преобразовывают низкоуровневый код обработки событий в высокоуровневые действия. Они являются объектами, прикрепленными к видимой области, что позволяет видимой области реагировать на действия так, как это делает контроллер. Распознаватели жестов интерпретируют касания, чтобы определить, соответствуют ли они определенному жесту, такому как свайп, пинч (сближение пальцев) или вращение.
Другими словами, устройства распознавания жестов позволяют вам преобразовывать действия пользователя в информацию, которую можно использовать для взаимодействия с вашим приложением.
И хотя в большинстве случаев достаточно жестов, присутствующих по умолчанию (касание, сближение и разведение пальцев, свайп и вращение), возможно, было бы полезным создать свой собственный распознаватель жестов. Это можно сделать, используя подклассы UIGestureRecognizer.
Использование подклассов:
Подкласс, «производный класс», унаследованный класс или дочерний класс – это модульный, производный класс, который наследует одно или несколько языковых свойств одного или нескольких других классов (называемых суперклассами, базовыми классами или родительскими классами).
Жест, который я создам, является распознавателем вращения одним пальцем. Он ведет себя как кнопка громкости на усилителе и преобразует круговое движение вокруг средней точки в значение, применимое для приложения.
Разделение на подклассы в Swift
Я уже создавал свой собственный Распознаватель Жестов на Objective-C, для этого мне нужно было включить UIKit/UIGestureRecognizerSubclass.h в проект, чтобы перезаписать существующие методы.
В Swift это выполняется добавлением следующего кода:
import UIKit.UIGestureRecognizerSubclass
Но несмотря на то, что редактор не выдавал ошибку и код методов подклассов компилировался, само приложение не компилировалось. После получения отчета об ошибках я должен был найти альтернативное решение. К счастью, оно было найдено, когда я узнал о возможности использования Bridging Header (перекрывающего заголовка).
Математическое волшебство
Вращение моего Распознавателя Жестов будет измеряться путем вычисления изменения угла относительно центральной точки распознавателя жестов. Немного поискав в памяти школьные уроки по математике (которые были много-много лун назад), я вспомнил про функцию арктангенса для вычисления угла. Поскольку эта функция возвращает значение между -π/2 и π/2, я должен был добавить 2*π, когда угол был меньше нуля. В общем, функция получилась следующей:
func angleForPoint(point:CGPoint) -> CGFloat {
var angle = -atan2f(point.x - midPoint.x, point.y - midPoint.y) + π/2
if (angle < 0) {
angle += π*2;
}
return angle
}
Обратите внимание на то, что π (еще) не является переменной по умолчанию. Но так как Swift позволяет использовать любой символ юникода в качестве имени переменной, то легко присвоить переменной Pi значение π:
let π = Float(M_PI)
Теперь, когда я могу вычислить угол определенной точки, довольно легко вычислить разницу между углами двух точек:
func angleBetween(pointA:CGPoint, andPointB pointB:CGPoint) -> CGFloat {
return angleForPoint(pointA) - angleForPoint(pointB)
}
Эти две функции являются основой моего Распознавателя Жестов и в конечном счете сделают большую часть волшебства.
Дополнительно мы хотим знать расстояние от касания до центральной точки. Это расстояние будет использоваться для проверки, находится ли касание в желаемых границах Распознавателя Жестов. Это расстояние рассчитывается при помощи Теоремы Пифагора и приводит к следующей функции:
func distanceBetween(pointA:CGPoint, andPointB pointB:CGPoint) -> CGFloat {
let dx = pointA.x - pointB.x
let dy = pointA.y - pointB.y
return sqrtf(dx*dx + dy*dy)
}
Разделение методов суперкласса на подклассы
Одна функция, которую мы определенно хотим создать, это определяемый инициализатор. Так как мы хотим вычислить угол на основе средней точки, то эту среднюю точку требуется задать. Кроме того, это позволяет вам при желании установить минимальный внутренний радиус и максимальный внешний радиус жеста:
init(midPoint:CGPoint, innerRadius:CGFloat?, outerRadius:CGFloat?, target:AnyObject?, action:Selector) {
super.init(target: target, action: action)
self.midPoint = midPoint
self.innerRadius = innerRadius
self.outerRadius = outerRadius
}
Конечно же вы хотите, чтобы ваш подкласс сотворил все волшебство при распознавании касания. Это осуществляется путем перезаписи следующих функций:
touchesBegan(touches: NSSet!, withEvent event: UIEvent!)
В этой функции мы проверяем, находится ли касание в желаемом диапазоне, путем проверки расстояния от центра, используя предыдущую заявленную функцию, и, если так, устанавливаем состояние жеста в .Began
touchesMoved(touches: NSSet!, withEvent event: UIEvent!)
В этой функции мы установим предыдущую и текущую точку касания и установим состояние жеста в .Changed
touchesEnded(touches: NSSet!, withEvent event: UIEvent!)
В этом классе мы сбросим предыдущую и текущую точки касания и установим состояние жеста в .Ended
На основе информации вышеупомянутых функций, значения для угла можно вычислить с помощью ранее заявленных функций.
Да, да … Покажите мне результат!
Ладно, ладно, я понимаю, что все вышеизложенное может показаться немного абстрактным и скучным, поэтому позвольте продемонстрировать результат, используя небольшую анимацию:
Как видите, возможно:
- Считывать данные относительного вращения и (как в этом примере) использовать это относительное значение для изменения значения в вашем приложении. (Например, громкость звука.)
- Считывать абсолютный угол относительно центральной точки. Обратите внимание на то, что 0 ° и 360 ° будут находиться на три часа.
- Считывать расстояние от центральной точки.
Есть много вариантов использования для этого жеста. Так как Распознавателем Жестов можно пользоваться всего с несколькими строками кода, то этот код является удивительным (разве вам не нравится моя скромность?). Но более важна простота, с которой Swift позволил мне написать этот код после нескольких дней экспериментирования с ним.
Не могу дождаться, чтобы увидеть, что будет в будущем Swift.
Как всегда, если вас интересует полный исходный код, или вы просто хотите использовать Распознаватель Жестов в своем собственном проекте, отправляйтесь на GitHub и начинайте создавать ветки!
Ветки, патчи и другая обратная связь приветствуются. Комментарии ниже этого сообщения приветствуются еще больше!
Автор: Beltoev