Изменение масштаба путем ответвления

В то время как основное UIScrollView класс поддерживает повышение - в и жесты повышения с очень мелкой суммой кода, поддерживание более богатого опыта изменения масштаба с помощью обнаружения касания требует большего количества работы со стороны приложения.

Инструкции по Интерфейсу пользователя iOS определяют двойное касание, чтобы увеличить масштаб и уменьшить масштаб. Это, однако, принимает некоторые определенные ограничения: то, что представление имеет единственный уровень изменения масштаба, такой как в фото приложении, или что последовательные двойные касания будут масштабировать к максимальной сумме и, когда-то достиг следующих изменений масштаба двойного касания назад к полноэкранному представлению. Но некоторые приложения требуют более гибкого поведения при контакте с функциональностью касания к изменению масштаба, примером этого является приложение Карт. Карты поддерживают двойное касание для увеличивания масштаб с дополнительными двойными касаниями, увеличивающими масштаб далее. Для уменьшения масштаб в последовательных суммах Карты используют касание с двумя пальцами, с пальцами близко друг к другу, для уменьшения масштаб шаг за шагом. В то время как этот жест не определяется в Инструкциях по Интерфейсу пользователя iOS, приложения могут принять решение принять его для имитации приложения Карт, когда требуется функциональность.

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

Реализация обрабатывающего касание кода

При поддержке касания, удваивают касание и касание с двумя пальцами в сенсорном коде подкласса UIView (или потомок), требует реализации трех методов: touchesBegan:withEvent:, touchesEnded:withEvent:, и touchesCanceled:withEvent:. Кроме того, инициализация взаимодействия, многократных касаний и переменных отслеживания может требоваться. Следующие фрагменты кода от примера TapToZoom в проекте примера кода ScrollViewSuite, в TapDetectingImageView класс, который является подклассом UIImageView.

Инициализация

Жесты, которые желаемы для реализации касания к изменению масштаба, требуют, чтобы взаимодействие с пользователем и многократные касания были включены для представления, и методы для добавления той функциональности вызывают от initWithImage: метод. Этот метод также инициализирует две переменные экземпляра, использующиеся для отслеживания состояния в сенсорных методах. twoFingerTapIsPossible свойство является булевской переменной, которая является YES если больше чем два пальца не находятся в контакте с экраном устройства. multipleTouches свойство имеет значение NO если их не больше чем одно сенсорное обнаруженное событие. Третье свойство, tapLocation, CGPoint, использующийся для отслеживания расположения двойного касания или средней точки между двумя пальцами, когда обнаруживается двойное касание. Эта точка тогда используется в качестве центральной точки для того, чтобы увеличить масштаб или использовать программируемые методы изменения масштаба, описанные в Изменении масштаба Программно.

- (id)initWithImage:(UIImage *)image {
    self = [super initWithImage:image];
    if (self) {
        [self setUserInteractionEnabled:YES];
        [self setMultipleTouchEnabled:YES];
        twoFingerTapIsPossible = YES;
        multipleTouches = NO;
    }
    return self;
}

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

touchesBegan:withEvent: Реализация

touchesBegan:withEvent: метод сначала отменяет любые выдающиеся попытки инициировать обработку единственного касания пальца, handleSingleTap сообщение. Сообщение отменяется, потому что, если оно было отправлено, это недопустимо, поскольку это - дополнительное сенсорное событие, исключая единственное касание. Если сообщение не было отправлено, потому что это - первое касание, отменение выполнения, проигнорирован.

Метод тогда обновляет состояние переменных отслеживания. Если больше, чем единственное сенсорное событие были получены, то multipleTouches свойство установлено в YES, потому что это может быть двумя касаниями пальца. Если больше чем два сенсорных события имели место, то twoFingerTapIsPossible свойство установлено в NO, касания больше чем двумя пальцами за один раз являются проигнорированным жестом.

Код для этого метода следующие:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // Cancel any pending handleSingleTap messages.
    [NSObject cancelPreviousPerformRequestsWithTarget:self
                                             selector:@selector(handleSingleTap)
                                               object:nil];
 
    // Update the touch state.
    if ([[event touchesForView:self] count] > 1)
        multipleTouches = YES;
    if ([[event touchesForView:self] count] > 2)
        twoFingerTapIsPossible = NO;
 
}

touchesEnded:withEvent: Реализация

Этот метод является рабочей лошадью обработки касания и несколько сложен. Однако код хорошо документируется и просто показан ниже. midPointBetweenPoints функция используется для определения точки, которая двойное касание будет центрироваться на когда handleTwoFingerTap метод вызывают, который приводит к представлению, уменьшающему уровень.

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    BOOL allTouchesEnded = ([touches count] == [[event touchesForView:self] count]);
 
    // first check for plain single/double tap, which is only possible if we haven't seen multiple touches
    if (!multipleTouches) {
        UITouch *touch = [touches anyObject];
        tapLocation = [touch locationInView:self];
 
        if ([touch tapCount] == 1) {
            [self performSelector:@selector(handleSingleTap)
                       withObject:nil
                       afterDelay:DOUBLE_TAP_DELAY];
        } else if([touch tapCount] == 2) {
            [self handleDoubleTap];
        }
    }
 
    // Check for a 2-finger tap if there have been multiple touches
    // and haven't that situation has not been ruled out
    else if (multipleTouches && twoFingerTapIsPossible) {
 
        // case 1: this is the end of both touches at once
        if ([touches count] == 2 && allTouchesEnded) {
            int i = 0;
            int tapCounts[2];
            CGPoint tapLocations[2];
            for (UITouch *touch in touches) {
                tapCounts[i] = [touch tapCount];
                tapLocations[i] = [touch locationInView:self];
                i++;
            }
            if (tapCounts[0] == 1 && tapCounts[1] == 1) {
                // it's a two-finger tap if they're both single taps
                tapLocation = midpointBetweenPoints(tapLocations[0],
                                                    tapLocations[1]);
                [self handleTwoFingerTap];
            }
        }
 
        // Case 2: this is the end of one touch, and the other hasn't ended yet
        else if ([touches count] == 1 && !allTouchesEnded) {
            UITouch *touch = [touches anyObject];
            if ([touch tapCount] == 1) {
                // If touch is a single tap, store its location
                // so it can be averaged with the second touch location
                tapLocation = [touch locationInView:self];
            } else {
                twoFingerTapIsPossible = NO;
            }
        }
 
        // Case 3: this is the end of the second of the two touches
        else if ([touches count] == 1 && allTouchesEnded) {
            UITouch *touch = [touches anyObject];
            if ([touch tapCount] == 1) {
                // if the last touch up is a single tap, this was a 2-finger tap
                tapLocation = midpointBetweenPoints(tapLocation,
                                                    [touch locationInView:self]);
                [self handleTwoFingerTap];
            }
        }
    }
 
    // if all touches are up, reset touch monitoring state
    if (allTouchesEnded) {
        twoFingerTapIsPossible = YES;
        multipleTouches = NO;
    }
}

touchesCancelled:withEvent: Реализация

Представление получает a touchesCancelled:withEvent: обменивайтесь сообщениями, если представление прокрутки обнаруживает, что обработка касания больше не релевантна, потому что палец переместился, который вызывает прокрутку к тому, чтобы быть. Этот метод просто сбрасывает переменные состояния.

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    twoFingerTapIsPossible = YES;
    multipleTouches = NO;
}

Пример комплекта ScrollView

Проект примера кода ScrollViewSuite имеет превосходные примеры реализации изменения масштаба с помощью жестов касания. TapToZoom пример в комплекте реализует подкласс UIImageView это поддерживает изменение масштаба с помощью касающегося поведения, как выведено на экран в приложении Карт. Реализация достаточно универсальна, через это - использование делегата (обычно контроллер, управляющий представлением прокрутки) реализовать фактическую обработку касания, двойного касания, или дважды затроньте, что необходимо быть в состоянии легко адаптировать код и проект, к собственным представлениям.

TapDetectingImageView класс является подклассом UIImageView это реализует сенсорную обработку, с помощью RootViewController класс как делегат, обрабатывающий фактическое касание и сенсорные ответы, а также контроллер, первоначально конфигурирующий UIScrollView.