UISlider и UIScrollView

У меня есть UISlider как часть представления, которое загружается в UIScrollView с включенным пейджингом. Я заметил неожиданное поведение. Если пользователь быстро использует слайдер (т.е. Нажимает и перемещает), он "активирует" вид прокрутки, заставляя страницу переключаться. Однако, если вы нажмете и удерживаете в течение секунды, ползунок "активируется", и вы можете отрегулировать значение ползунка. Это поведение нежелательно.

Каков наилучший способ реагирования UISlider при загрузке в UIScrollView? Я думал о добавлении "блокатора", который просто ест сенсорные события, которые помещаются под ползунок, но не уверен, что это лучший способ сделать это.

Ответы

Ответ 1

Просматривает ли ваш профиль прокрутки область, занятую вашим слайдером (переопределить hitTest:withEvent:, вам может потребоваться подкласс UIScrollView). Если обнаружено касание указанной области, скажите, что ваш вид прокрутки немедленно передает касание к ползунку.

Ответ 2

нет необходимости в тесте на бит UIScrollView. так как задержка устанавливается самой UIScrollView. подклассов и реализации пользовательского hitTest:withEvent: не поможет, поскольку он все еще запускается с задержкой.

Я искал часы для изящного решения этого, так как я хотел имитировать собственный лог файл apple в iOS-приложении.

трюк:

yourScrollView.delaysContentTouches = NO;

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

для передачи косвенностей, отличных от тех, которые находятся в прямоугольнике UISliders, вы должны подкласса UISlider и добавить следующее:

// get the location of the thumb
- (CGRect)thumbRect 
{
   CGRect trackRect = [self trackRectForBounds:self.bounds];
   CGRect thumbRect = [self thumbRectForBounds:self.bounds
                                  trackRect:trackRect
                                      value:self.value];
   return thumbRect;
}

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{   
   CGRect thumbFrame = [self thumbRect];

      // check if the point is within the thumb
   if (CGRectContainsPoint(thumbFrame, point))
   {
      // if so trigger the method of the super class
      NSLog(@"inside thumb");
      return [super hitTest:point withEvent:event];
   }
   else
   {
      // if not just pass the event on to your superview
      NSLog(@"outside thumb");
      return [[self superview] hitTest:point withEvent:event];
   }
}

Ответ 3

При создании аудиопроигрывателя у меня была точная проблема с sliderView, и он добавляется к scrollView, и всякий раз, когда я касался sliderView, scrollView использовался для прикосновения, а вместо sliderView, перемещение scrollView. Чтобы этого избежать, я отключил srcolling scrollView, когда пользователь коснулся кнопки sliderView, и в противном случае прокрутка будет включена.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

    UIView *result = [super hitTest:point withEvent:event] ;
    if (result == sliderView)
    {
        scrollView.scrollEnabled = NO ;
    }
    else
    {
        scrollView.scrollEnabled = YES ;
    }

    return result ;

}

Ответ 4

Моя проблема была надмножеством этой проблемы - у меня UISliders внутри UITableViewCells, а весь UITableView - это страница внутри UIScrollView. Ползунки разрушали взаимодействие с другими двумя, и подклассические решения не работали. Вот что я придумал, что отлично работает: отправлять уведомления, когда ползунки перемещаются, и в это время UITableView и UIScrollView disableScrolling. Обратите внимание на рисунке ниже: мои ползунки горизонтальные, мой столбец вертикальный, а мой UIScrollView имеет горизонтальные страницы.

UISlider in a UITableView in a UIScrollView

UITableViewCell собирает события для программно созданного слайдера:

self.numberSlider = [[UISlider alloc] init];
[self.numberSlider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged];
[self.numberSlider addTarget:self action:@selector(sliderTouchDown:) forControlEvents:UIControlEventTouchDown];
[self.numberSlider addTarget:self action:@selector(sliderTouchUp:) forControlEvents:UIControlEventTouchUpInside];
[self.numberSlider addTarget:self action:@selector(sliderTouchUp:) forControlEvents:UIControlEventTouchUpOutside];

Для целей этого руководства мы заботимся только о touchDown и Up:

- (void)sliderTouchDown:(UISlider *)sender
{
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFY_SLIDER_TOUCH_BEGAN object:nil];
}

- (void)sliderTouchUp:(UISlider *)sender
{
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFY_SLIDER_TOUCH_ENDED object:nil];
}

Теперь мы поймаем эти уведомления как в UITableView (обратите внимание, что tableview находится в VC, но я уверен, что это сработает, если вы подклассы):

- (void)viewDidLoad
{
    // other stuff
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sliderTouchDown:) name:NOTIFY_SLIDER_TOUCH_BEGAN object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sliderTouchUp:) name:NOTIFY_SLIDER_TOUCH_ENDED object:nil];
}

- (void)sliderTouchDown:(NSNotification *)notify
{
    self.treatmentTableView.scrollEnabled = NO;
}

- (void)sliderTouchUp:(NSNotification *)notify
{
    self.treatmentTableView.scrollEnabled = YES;
}

и UIScrollView (как указано выше, заключено в VC):

- (void)viewDidLoad
{
    // other stuff

    // Register for slider notifications
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(disableScrolling:) name:NOTIFY_SLIDER_TOUCH_BEGAN object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enableScrolling:) name:NOTIFY_SLIDER_TOUCH_ENDED object:nil];
}

- (void)disableScrolling:(NSNotification *)notify
{
    self.scrollView.scrollEnabled = NO;
}

- (void)enableScrolling:(NSNotification *)notify
{
    self.scrollView.scrollEnabled = YES;
}

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

Ответ 5

Я хотел опубликовать это как комментарий, но у моей учетной записи нет репутации, поэтому мне пришлось опубликовать целый ответ. user762391 ответ, включая отмену hitTest, отлично работает. Я просто хочу добавить, что вместо того, чтобы переопределять hitTest, вы можете вместо этого переопределить pointInside: withEvent: вроде этого (сохраняя метод thumbRect):

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent * _Nullable)event {
    return CGRectContainsPoint([self thumbRect], point)
}

или в Swift:

var thumbRect: CGRect {
    return thumbRectForBounds(bounds, trackRect: trackRectForBounds(bounds), value: value)
}

override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
    return thumbRect.contains(point)
}

Ответ 6

наиболее распространенный код комментария в Swift 3:)

import Foundation

class UISliderForScrollView:UISlider {
    var thumbRect:CGRect {
        let trackRect = self.trackRect(forBounds: bounds)
        return thumbRect(forBounds: bounds, trackRect: trackRect, value: value)
    }

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if thumbRect.contains(point) {
            return super.hitTest(point, with: event)
        } else {
            return superview?.hitTest(point, with: event)
        }
    }
}

Ответ 7

Вы можете подклассифицировать UIScrollView и переопределить метод - (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view следующим образом:

- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view {
    if ([view isKindOfClass:[UISlider class]]) {
        UITouch *touch = [[event allTouches] anyObject];
        CGPoint location = [touch locationInView:view];
        CGRect thumbRect;
        UISlider *mySlide = (UISlider*) view;
        CGRect trackRect = [mySlide trackRectForBounds:mySlide.bounds];
        thumbRect = [mySlide thumbRectForBounds:mySlide.bounds trackRect:trackRect value:mySlide.value];
        if (CGRectContainsPoint(thumbRect, location))
            return YES;
    }
    return NO;
}

Также установите свойство yourScrollView.delaysContentTouches = NO; для прокрутки.