Перетаскивание UIView внутри UIScrollView
Я пытаюсь решить основную проблему с перетаскиванием на iPhone. Здесь моя настройка:
- У меня есть UIScrollView, у которого есть один большой просмотр содержимого (я могу прокручивать и масштабировать его).
- В subview для контента есть несколько небольших фрагментов, которые должны быть перемещены внутри него.
Мой подкласс UIScrollView имеет этот метод:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *tile = [contentView pointInsideTiles:[self convertPoint:point toView:contentView] withEvent:event];
if (tile) {
return tile;
} else {
return [super hitTest:point withEvent:event];
}
}
Подпоследовательность содержимого имеет этот метод:
- (UIView *)pointInsideTiles:(CGPoint)point withEvent:(UIEvent *)event {
for (TileView *tile in tiles) {
if ([tile pointInside:[self convertPoint:point toView:tile] withEvent:event])
return tile;
}
return nil;
}
И в режиме просмотра плитки этот метод:
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self.superview];
self.center = location;
}
Это работает, но не полностью корректно: плитка иногда "падает" во время процесса перетаскивания. Точнее, он перестает получать штрихи. Вместо этого запускается прокрутка: вызовы и прокрутка. Я заметил, что это зависит от скорости перетаскивания: чем быстрее я перетаскиваю, тем быстрее "падает".
Любые идеи о том, как сохранить плиту, приклеенную к перетаскивающему пальцу?
Ответы
Ответ 1
Решено: выяснилось, что также должны быть затронутыBegan: и touchesEnded: реализации (в моем случае с пустым методом помогли) в плитке, иначе жест начал распространяться на родительские представления, и они каким-то образом перехватили жест. Зависимость от скорости перемещения была мнимой.
Ответ 2
Я боролся с этой же проблемой - я пытался сделать интерфейс с большим количеством "карточек" (подклассы UIView) на пробковой доске и иметь прокручиваемую область пробки, но все же способную перетаскивать мышью, отбросьте карты. Я делал решение hitTest() выше, но один из инженеров Apple спросил меня, почему я так делаю. Более простое решение, которое они предложили, было следующим:
1) В классе UIScrollView задайте значение canCancelContentTouches в НЕТ - это указывает классу UIScrollView разрешить касания в пределах subviews (или, в данном случае, в subviews subviews).
2) В моем классе "card" установите для параметра exclusiveTouch значение "ДА" - это говорит подчиненному, что он владеет штрихами внутри него.
После этого мне удалось перетащить карты и еще прокрутить подвью. Это намного проще и чище, чем решение hitTest() выше.
(BTW, для дополнительного кредита, если вы используете iOS 3.2 или 4.0 или новее, используйте класс UIPanGestureRecognizer для обработки логики перетаскивания - движение перетаскивания намного более плавное, чем переопределение касанийBegan()/touchhesMoved ( )/touchesEnded().)
Ответ 3
На основе кода, который вы поделили, похоже, что ваш метод touchesMoved:
будет вызываться только для жестов внутри плитки. При каждом касании вы перемещаете плитку так, чтобы она была сосредоточена на этом касании, поэтому медленные движения будут давать обновление внутри плитки - и плитка "догонит" кончиком пальца - до того, как жест выйдет из плитки. Однако, когда жест выполняется быстрее, (x, y) касается событий, связанных друг с другом, и вы потеряете жест, когда одна (x, y) точка находится достаточно далеко от последней, которая находится за пределами плитка уже.
Вы можете обойти это, захватив движения в супервиде, достаточно большие, чтобы покрыть всю область перетаскивания, и контролировать движение плитки изнутри этого супервизора.
Кстати, есть ли причина, по которой вы переопределяете метод hitTest:? Может быть проще (и, возможно, более эффективно?) Использовать встроенную реализацию.
Ответ 4
Сначала установите:
scrollview.canCancelContentTouches = NO;
yourSubView. exclusiveTouch = YES;
Затем в вашей функции обработки жестов subview,
- (void)handleSubviewMove:(UIPanGestureRecognizer *)gesture {
if(gesture.state == UIGestureRecognizerStateBegan) {
if(_parentScrollView != nil) {
_parentScrollView.scrollEnabled = NO;
}
}
if(gesture.state == UIGestureRecognizerStateEnded) {
if(_parentScrollView != nil) {
_parentScrollView.scrollEnabled = YES;
}
}
CGPoint translation = [gesture translationInView:[self superview]];
/* handle your view movement here. */
[gesture setTranslation:CGPointZero inView:[self superview]];
}