UISlider и UIScrollView - PullRequest
       23

UISlider и UIScrollView

15 голосов
/ 05 января 2011

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

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

Ответы [ 7 ]

44 голосов
/ 23 июня 2011

тест на попадание со стороны 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 голосов
/ 05 января 2011

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

2 голосов
/ 18 января 2017

наиболее одобренный код комментария в 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)
        }
    }
}
2 голосов
/ 22 января 2014

Моя проблема была надмножеством этой проблемы - у меня есть 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 (обратите внимание, что табличное представление находится в 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;
}

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

2 голосов
/ 19 ноября 2013

При создании аудиоплеера у меня была точная проблема с 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 ;

}
1 голос
/ 07 октября 2015

Я хотел опубликовать это как комментарий, но в моем аккаунте нет ни одного представителя, поэтому мне пришлось опубликовать целый ответ. Ответ 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)
}
0 голосов
/ 22 мая 2013

Вы можете создать подкласс 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; для scrollview.

...