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;
для прокрутки.