Статьи

Захватить подпись на iOS

Первоначально автор Джейсон Харвиг

В блоге Square Engineering есть отличная статья о Smoother Signatures для Android, но я ничего не нашел конкретно об iOS. Итак, каков наилучший способ получить подпись пользователя на устройстве iOS?

Хотя я не нашел ни одной статьи о захвате подписи, в App Store есть хорошие реализации. Моим целевым пользовательским опытом было приложение для iPad Paper by 53 , приложение для рисования с красивыми и отзывчивыми кистями.

Весь код доступен в репозитории Github: SignatureDemo .

Соединение точек

Самый простой подход — захватить прикосновения и соединить их прямыми линиями.

В инициализаторе UIViewподкласса создайте средство распознавания пути и жеста для захвата событий касания.

// Create a path to connect lines
path = [UIBezierPath bezierPath];

// Capture touches
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
pan.maximumNumberOfTouches = pan.minimumNumberOfTouches = 1;
[self addGestureRecognizer:pan];

Захватите события панорамирования в путь Безье, соединив точки с линиями.

- (void)pan:(UIPanGestureRecognizer *)pan {
    CGPoint currentPoint = [pan locationInView:self];

    if (pan.state == UIGestureRecognizerStateBegan) {
        [path moveToPoint:currentPoint];
    } else if (pan.state == UIGestureRecognizerStateChanged)
        [path addLineToPoint:currentPoint];

    [self setNeedsDisplay];
}

Инсульт путь

- (void)drawRect:(CGRect)rect
{
    [[UIColor blackColor] setStroke];
    [path stroke];
}

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

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

Квадратичные кривые Безье

Вместо соединительных линий между точками касания квадратичные кривые Безье соединяют точки, используя технику, рассмотренную в вышеупомянутом сеансе WWDC (см. 42:15). Соедините точки касания с квадратичной кривой, используя точки касания в качестве контрольных точек и средние точки как начало и конец.

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

CGPoint previousPoint;

Создайте функцию для вычисления средней точки двух точек.

static CGPoint midpoint(CGPoint p0, CGPoint p1) {
    return (CGPoint) {
        (p0.x + p1.x) / 2.0,
        (p0.y + p1.y) / 2.0
    };
}

Обновлен обработчик жестов панорамирования, чтобы добавить квадратные кривые вместо прямых

- (void)pan:(UIPanGestureRecognizer *)pan {
    CGPoint currentPoint = [pan locationInView:self];
    CGPoint midPoint = midpoint(previousPoint, currentPoint);

    if (pan.state == UIGestureRecognizerStateBegan) {
        [path moveToPoint:currentPoint];
    } else if (pan.state == UIGestureRecognizerStateChanged) {
        [path addQuadCurveToPoint:midPoint controlPoint:previousPoint];
    }

    previousPoint = currentPoint;

    [self setNeedsDisplay];
}

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

Переменная ширина хода

Ширина может варьироваться в зависимости от скорости касания, чтобы создать более естественный ход. В UIPanGestureRecognizerуже включенный метод, velocityInView:который возвращает текущую скорость касания как CGPoint.

Чтобы визуализировать обводку различной ширины, я переключился на OpenGL ES и метод, называемый тесселяцией, для преобразования обводки в треугольники — в частности, в треугольные полосы (OpenGL поддерживает рисование линий, но iOS не поддерживает переменную ширину линий при сглаживании). Квадратичные точки вдоль кривой также должны быть рассчитаны, но это выходит за рамки данной статьи. Проверьте источник на github для деталей.

Учитывая две точки, вычисляется перпендикулярный вектор и его величина устанавливается равной половине текущей толщины. Учитывая природу, GL_TRIANGLE_STRIPтолько две точки необходимы для создания следующего сегмента прямоугольника с двумя треугольниками.

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