2011-01-05 2 views
13

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

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

ответ

3

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

+0

Спасибо, в конечном итоге с этим. Однако, еще одна странность ... это поведение проявлялось только на симуляторе. Как только я получил его на устройстве, он отлично работал без исправления. – MarkPowell

+0

Интересно, еще одна причина не полагаться только на симулятор для тестирования ... – BoltClock

+2

FWIW, я вижу точно такое же поведение (как описано в исходном вопросе) как на симуляторе (iOS 6), так и на устройстве (iOS 5.1). Я должен нажать и удерживать минутку, прежде чем перетаскивать слайдер, иначе я получаю вместо этого прокрутку. –

40

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

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

трюк:

yourScrollView.delaysContentTouches = NO; 

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

пройти другие, чем те, которые находятся в UISliders пальца touchevents Прямоугольник вы должны создать подкласс 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]; 
    } 
} 
+0

Спасибо, Себастьян, это именно то, что я искал! –

+0

добро пожаловать, алекс. –

+0

Я искал точно такую ​​же вещь, спасибо! –

0

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

1

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

Моя проблема была надстройкой этого вопроса - у меня 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]; 

Для целей данного руководства, мы заботимся только о приземлении и 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 (такой же, как указано выше, заключенный в ВК):

- (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 остаются неподвижными, и когда вы нажимаете за пределы ползунков, таблицы и прокрутки просматриваются как ожидалось. Также - обратите внимание, что я мог бы использовать не-подклассы экземпляров всех трех компонентов в этом решении. Надеюсь, это поможет кому-то!

1

Я хочу опубликовать это как комментарий, но у моей учетной записи нет репутации, поэтому мне пришлось опубликовать целый ответ. Ответ пользователя762391, включающий отмену 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) 
} 
1

самый upvoted комментарий код in 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) 
     } 
    } 
} 
Смежные вопросы