Вопрос

У меня есть UIScrollView с 2 страницами, и я могу прокручивать их по горизонтали.Однако на одной из моих страниц у меня есть UIDatePicker, и вид прокрутки перехватывает события вертикального касания, поэтому я больше не могу манипулировать средством выбора даты (кроме как щелчком мыши).Есть ли какой-нибудь способ указать ScrollView отправлять события вертикального касания в средство выбора даты, но отправлять события горизонтального касания в режим прокрутки для переключения страниц?

Это было полезно?

Решение

Я думаю, что в этой проблеме есть две части.Первый - это определение намерения пользователя, а второй - получение правильного элемента управления для реагирования на это намерение.

Определение намерения

Я думаю, важно четко представлять, что намеревается пользователь.Представьте себе такой сценарий:Пользователь начинает прикасаться к экрану и перемещает палец далеко влево, но также немного вверх.Пользователь, вероятно, намеревался прокрутить представление и вообще не собирался менять дату.Было бы плохо одновременно прокручивать вид и изменять дату, особенно когда она перемещается за пределы экрана.Итак, чтобы определить, что намеревается сделать пользователь, я предлагаю следующий алгоритм:

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

Реализация

Чтобы реализовать это, вам необходимо обработать события до того, как это сделает UIScrollView или средство выбора даты.Вероятно, есть несколько способов сделать это, но на ум приходит один, в частности:Создайте пользовательский UIView под названием ScrollingDateMediatorView.Установите UIScrollView в качестве дочернего элемента этого представления.Переопределите hitTest для ScrollingDateMediatorView:withEvent:и точка зрения: с событием:методы.Эти методы должны выполнять тот же тип проверки совпадений, который обычно выполняется, но если результатом является средство выбора даты, верните вместо этого self .Это эффективно перехватывает любые события касания, которые были предназначены для средства выбора даты, позволяя ScrollingDateMediatorView обрабатывать их первым.Затем вы реализуете алгоритм, описанный выше, в различных методах touches*.В частности:

В методе touchesBegan:withEvent сохраните исходное положение.

В touchesMoved: с помощью Event, если намерение пользователя еще не известно, определите, переместился ли объект касания достаточно далеко от исходного положения.Если это так, определите, намеревается ли пользователь прокрутить или изменить дату, и сохраните это намерение.Если намерение пользователя уже известно и оно заключается в изменении даты, отправьте средству выбора даты сообщение touchedMoved:withEvent, в противном случае отправьте UIScrollView сообщение touchesMoved: withEvent.Вам нужно будет выполнить некоторую аналогичную работу в рамках touchesEnded: withEvent и touchesCancelled: withEvent, чтобы убедиться, что другие представления получают соответствующие сообщения.Оба этих метода должны сбросить сохраненные значения.

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

Другие советы

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

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView* result = [super hitTest:point withEvent:event];

    if ([result.superview isKindOfClass:[UIPickerView class]])
    {
         self.canCancelContentTouches = NO;  
         self.delaysContentTouches = NO;
    }
    else 
    {
         self.canCancelContentTouches = YES; // (or restore bool from prev value if needed)
         self.delaysContentTouches = YES;    // (same as above)
    }
    return result;
}

Причина, по которой я использую result.superview заключается в том, что представление, которое получает штрихи, на самом деле будет UIPickerTable, который является частным API.

Ваше здоровье

Потрясающая помощь Сэму!Я использовал это, чтобы создать простую категорию, которая перелистывает метод (потому что я делал это в UITableViewController и, следовательно, должен был бы сделать некоторые действительно грязные вещи, чтобы подклассировать вид прокрутки).

#import <UIKit/UIKit.h>

@interface UIScrollView (withControls)

+ (void) swizzle;

@end

И основной код:

#import </usr/include/objc/objc-class.h>
#import "UIScrollView+withControls.h"

#define kUIViewBackgroundImageTag 6183746
static BOOL swizzled = NO;

@implementation UIScrollView (withControls)

+ (void)swizzleSelector:(SEL)orig ofClass:(Class)c withSelector:(SEL)new;
{
    Method origMethod = class_getInstanceMethod(c, orig);
    Method newMethod = class_getInstanceMethod(c, new);

    if (class_addMethod(c, orig, method_getImplementation(newMethod),
                        method_getTypeEncoding(newMethod))) {
        class_replaceMethod(c, new, method_getImplementation(origMethod),
                            method_getTypeEncoding(origMethod));
    } else {
        method_exchangeImplementations(origMethod, newMethod);
    }
}

+ (void) swizzle {
    @synchronized(self) {
        if (!swizzled) {

            [UIScrollView swizzleSelector:@selector(hitTest:withEvent:)
                                  ofClass:[UIScrollView class]
                             withSelector:@selector(swizzledHitTest:withEvent:)];
            swizzled = YES;
        }
    }
}

- (UIView*)swizzledHitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView* result = [self swizzledHitTest:point withEvent:event]; // actually calling the original hitTest method

    if ([result.superview isKindOfClass:[UIPickerView class]]) {
        self.canCancelContentTouches = NO;  
        self.delaysContentTouches = NO;
    } else {
        self.canCancelContentTouches = YES; // (or restore bool from prev value if needed)
        self.delaysContentTouches = YES;    // (same as above)
    }
    return result;
}

@end

Затем, в моем методе viewDidLoad, я просто вызвал

[UIScrollView swizzle];
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top