it-swarm.com.ru

UITableView внутри UIScrollView не получает первое касание после прокрутки

Краткое

У меня проблема с UITableView внутри UIScrollView. Когда я прокручиваю внешнее scrollView, table не получает событие willSelect/didSelect при первом касании, а при втором. Что еще более странно, сама ячейка получает прикосновения и подсвеченное состояние, даже когда delegate нет.

Детальное объяснение

Моя иерархия представлений:

UIView
  - UIScrollView  (outerscroll)
      - Some other views and buttons
      - UITableView (tableView)

Внутри прокрутки у меня есть несколько дополнительных видов, которые расширяются/закрываются динамически. Табличное представление должно быть «зафиксировано» сверху вместе с некоторыми другими элементами представления, поэтому я создал этот макет, который позволяет мне легко перемещать элементы аналогично тому, как Apple рекомендует использовать преобразования, когда свиток бывает.

Вид таблицы преобразуется с эффектом перевода, когда прокрутка перемещается следующим образом:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (scrollView == self.outerScrollView) {

        CGFloat tableOffset = scrollView.contentOffset.y - self.fixedHeaderFrame.Origin.y;
        if (tableOffset > 0) {
            self.tableView.contentOffset = CGPointMake(0, tableOffset);
            self.tableView.transform = CGAffineTransformMakeTranslation(0, tableOffset);
        }
        else {
            self.tableView.contentOffset = CGPointMake(0, 0);
            self.tableView.transform = CGAffineTransformIdentity;
        }

        // Other similar transformations are done here, but not involving the table

}

В моей камере, если я реализую эти методы:

- (void)setSelected:(BOOL)selected {
    [super setSelected:selected];
    if (selected) {
        NSLog(@"selected");
    }
}

- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
    [super setHighlighted:highlighted animated:animated];
    if (highlighted) {
        NSLog(@"highlighted");
    }
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];
    NSLog(@"touchesBegan");
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    NSLog(@"touchesEnded");
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesCancelled:touches withEvent:event];
    NSLog(@"touchesCancelled");
}

Y может увидеть этот вывод при сбое (первое нажатие):

2014-02-10 13:04:40.940 MyOrderApp[5588:70b] highlighted 
2014-02-10 13:04:40.940 MyOrderApp[5588:70b] touchesBegan 
2014-02-10 13:04:40.978 MyOrderApp[5588:70b] touchesEnded

И этот, когда работает (второй кран):

2014-02-10 13:05:30.359 MyOrderApp[5588:70b] highlighted 
2014-02-10 13:05:30.360 MyOrderApp[5588:70b] touchesBegan 
2014-02-10 13:05:30.487 MyOrderApp[5588:70b] touchesEnded 
2014-02-10 13:05:30.498 MyOrderApp[5588:70b] expanded

Никакое другое изменение кадра, анимация или любое другое взаимодействие просмотра не выполняется между первым и вторым касанием. Кроме того, только при прокрутке больших объемов появляется ошибка, но при прокрутке всего в несколько пикселей все продолжает работать, как и ожидалось.

Я тоже экспериментировал с изменением некоторых свойств, но безуспешно. Некоторые из вещей, которые я сделал:

  • Удалить userInteractionEnabled из представлений, отличных от прокрутки и таблицы
  • Добавьте вызов setNeedsLayout для таблицы, прокрутки и основного представления при возникновении scrollViewDidScroll.
  • Удалить преобразования из таблицы (все еще происходит)

Я видел некоторые комментарии о неожиданном поведении встраивания UITableViews в UIScrollViews, но я не вижу такого предупреждения в официальной документации Apple, поэтому я ожидаю, что это сработает. 

Приложение только для iOS7 +.

Вопросы

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

Обновления

  • Я попытался отключить повторное использование ячеек, как это было предложено в комментарии, но все равно происходит так же.
30
Angel G. Olloqui

Из Документация Apple , вы не должны встраивать UITableViewinside в UIScrollView.

Важное замечание: Вы не должны встраивать объекты UIWebView или UITableView в UIScrollView объекты. Если вы это сделаете, это может привести к неожиданному поведению потому что события касания для двух объектов могут быть перепутаны и ошибочно обрабатываются.

Ваша проблема действительно связана с тем, что делает ваш UIScrollView.

Но если нужно просто скрыть табличное представление, когда это необходимо (это был мой случай), вы можете просто переместить UITableView в его суперпредставление.

Я написал небольшой пример здесь: https://github.com/rvirin/SoundCloud/

22
Rémy Virin

оставьте внутреннее свойство scrollEnabled UITableView равным YES. это позволяет внутренней UITableView знать, как правильно обрабатывать касания, связанные с прокруткой UIScrollView.

19
Tom Abraham

Я столкнулся с этой же проблемой и нашел решение !! 

Вам необходимо установить для delaysTouchesBegan значение true в вашем просмотре прокрутки, чтобы представление прокрутки отправляло свой неудачный жест прокрутки (то есть касание) своим дочерним элементам. 

var delaysTouchesBegan: Bool -Булево значение, определяющее, задерживает ли приемник отправку касаний в начальной фазе для его просмотра.

Когда значение свойства равно YES, окно приостанавливает доставку коснитесь объектов в фазе UITouchPhaseBegan к представлению. Если распознаватель жестов впоследствии распознает его жест, эти прикосновения объекты отбрасываются. Если распознаватель жестов, однако, не При распознавании его жеста окно доставляет эти объекты в вид в сообщении touchesBegan: withEvent: (и, возможно, в последующем touchesMoved: withEvent: сообщение, информирующее его о текущем местоположении касаний).

https://developer.Apple.com/library/ios/documentation/UIKit/Reference/UIGestureRecognizer_Class/index.html#//Apple_ref/occ/instp/UIGestureRecognizer/delaysTouchesBegan

Но есть одна загвоздка ... это не сработает, если вы сделаете это прямо в scrollview!

// Does NOT work
self.myScrollview.delaysTouchesBegan = true

По-видимому, это ошибка iOS, когда установка этого свойства не работает (спасибо Apple). Однако есть простой обходной путь: установите свойство непосредственно в жесте панорамирования скроллвью. Конечно же, это сработало для меня отлично:

// This works!!
self.myScrollview.panGestureRecognizer.delaysTouchesBegan = true
13
Oren

Кажется, что ваш UiTableView не распознает ваш кран. Вы пытались использовать это:

- (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UISwipeGestureRecognizer             *)otherGestureRecognizer
{
    if ([otherGestureRecognizer.view isKindOfClass:[UITableView class]]) {
        return YES;
    }
    return NO;
}

Примечание от Apple: 

вызывается, когда распознавание одного из gestRecognizer или otherGestureRecognizer будет заблокировано другим. верните ДА, чтобы позволить обоим распознавать одновременно. реализация по умолчанию возвращает NO (по умолчанию два жеста не могут быть распознаны одновременно)

примечание: возвращение YES гарантирует одновременное распознавание. возврат NO не гарантирует предотвращения одновременного распознавания, так как делегат другого жеста может вернуть YES

Надеюсь, что это поможет.

3
Nicolas Bonnet

Распознаватели жестов не будут работать правильно для двух встроенных видов прокрутки или подклассов.

Попробуйте обходной путь:

  1. Используйте прозрачное, пользовательское и наложение всего в ячейке UIButton с соответствующим тегом или подклассом UIButton и добавьте свойство пути индекса и перезаписывайте каждый раз в повторно используемой ячейке. 

  2. Добавьте эту кнопку как свойство в свою ячейку.

  3. Добавьте цель для желаемого UIControlEvent (одного или нескольких), который указывает на ваш класс принятия протокола UITableViewDelegate.

  4. Отключить выбор в IB и вручную управлять выбором из кода.

Это решение требует внимания для случаев одиночного/множественного выбора.

3
Wladek Surala

Я встречал UITableView с scrollEnabled, являющимся НЕТ в UIScrollView в некотором устаревшем коде. Я не смог ни изменить существующую иерархию, ни включить прокрутку, но нашел следующий обходной путь для решения проблемы первого касания:

@interface YourOwnTableView : UITableView
@end

@implementation YourOwnTableView

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {

    [super touchesCancelled:touches withEvent:event];

    // Note that this is a hack and it can stop working at some point.
    // Looks like a table view with scrollEnabled being NO does not handle cancellation cleanly,
    // so let's repeat begin/end touch sequence here hoping it'll reset its own internal state properly
    // but won't trigger cell selection (the touch passed is in its cancelled phase, perhaps there is a part
    // of code inside which actually checks it)
    [super touchesBegan:touches withEvent:event];
    [super touchesEnded:touches withEvent:event];
}

@end

Опять же, это просто обходной путь, работающий в моем конкретном случае. Наличие табличного представления в представлении прокрутки - все еще неправильная вещь.

2
aleh

У меня та же проблема с вложенным UITableView, и я нашел обходной путь для этого:

innerTableView.scrollEnabled = YES;
innerTableView.alwaysBounceVertical = NO;

Вам нужно будет установить высоту внутреннего представления таблицы, чтобы она соответствовала общей высоте его ячеек, чтобы она не прокручивалась при прокрутке пользователем внешнего вида.

Надеюсь это поможет.

1
Duc Minh Trinh

Моя ошибка заключалась в реализации метода делегата:

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath

вместо того, что я хотел реализовать: 

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

Следовательно, вызывается только во второй подключаемой ячейке, потому что тогда была выбрана первая ячейка de. Глупая ошибка, допущенная с помощью автозаполнения. Просто мысль для тех из вас, кто может бродить здесь, не осознавая, что вы совершили ту же ошибку.

1
LunaCodeGirl

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

- (void)scrollViewDidScroll:(UIScrollView *)scrollView 
1
Rajesh

Scrollview пытается выяснить, собирается ли пользователь прокрутить или нет, поэтому он намеренно задерживает первоначальное прикосновение. Вы можете отключить это, установив delaysContentTouches в NO.

1
Stepan Hruda
  1. Перетащите UIButton поверх вашей UITableViewCell и создайте розетку как «btnRowSelect».
  2. По вашему мнению, контроллер поместите этот код в cellForRowAtIndexPath

    cell.btnRowSelect.tag = indexPath.row
    cell.btnRowSelect.addTarget(self, action: Selector("rowSelect:"), forControlEvents: .TouchUpInside)
    
  3. Добавьте эту функцию в ваш viewController, а также

    func rowSelect (sender:UIButton) {
    // "sendet.tag" give you the selected row 
    // do whatever you want to do in didSelectRowAtIndexPath
    }
    

Эта функция "rowSelect" будет работать как didSelectRowAtIndexPath где вы получаете строку "indexPath.row" как "sender.tag"

0
shubham

Что это, если сенсорный просмотр таблицы будет работать правильно. также с видом прокрутки в том же контроллере представления также.

tableview.scrollEnabled = true;
0
Ravi H Malviya

Как говорят другие ответы, вы не должны помещать табличное представление в прокручиваемое. UITableView в любом случае наследуется от UIScrollView, так что я думаю, что здесь все запутано. Что я всегда делаю в этой ситуации:

1) Подкласс UITableViewController и включает свойство UIView * headView.

2) В родительском UIViewController создайте все верхнее содержимое контейнера UIView.

3) Инициализируйте свой пользовательский UITableView и добавьте представление tableView в полноразмерный контроллер представления.

[self.view addSubview: self.myTableView.view];

4) Установите headView, чтобы быть вашими UIView Gubbins

self.tableView.headView = myHeadViewGubbins.

5) В методе tableViewController

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger *)section;

Делать:

if ( section == 0 ) {
    return self.headView;
}

Теперь у вас есть вид таблицы с кучей других шизлов наверху.

Наслаждайтесь!

0
Martin

У меня та же проблема, затем обратитесь к «Вложенным представлениям прокрутки», как сказал lxx.

https://developer.Apple.com/library/ios/documentation/WindowsViews/Conceptual/UIScrollView_pg/NestedScrollViews/NestedScrollViews.html

Пример поперечной прокрутки можно найти в приложении Stocks. Вид сверху - это вид таблицы, но вид снизу - вид горизонтальной прокрутки, настроенный с использованием режима подкачки. В то время как два из трех его подпредставлений являются пользовательскими представлениями, третье представление (которое содержит новостные статьи) представляет собой UITableView (подкласс UIScrollView), которое является подпредставлением представления горизонтальной прокрутки. После горизонтальной прокрутки к просмотру новостей вы можете прокрутить его содержимое по вертикали.

Это работа

0
Davin Kao