Подтвердить что ты не робот

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

Краткая

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

Подробное объяснение

Моя иерархия просмотров:

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

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

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

- (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 получает как ожидалось.

Обновления

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

Ответ 1

Из Apple Documentation вы не должны вставлять UITableView внутри UIScrollView.

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

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

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

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

Ответ 2

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

Ответ 3

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

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

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

Когда значение свойства равно YES, окно приостанавливает доставку коснитесь объектов в фазе UITouchPhaseBegan для представления. Если распознаватель жестов впоследствии распознает его жест, эти прикосновения объекты отбрасываются. Если распознаватель жеста, однако, не распознать его жест, окно доставляет эти объекты к виду в контактахBegan: 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, когда настройка этого свойства не работает (спасибо яблоко). Однако есть простой способ: установить свойство непосредственно на жесте панорамы scrollview. Конечно, это отлично сработало для меня:

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

Ответ 4

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

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

Примечание от яблока:

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

Примечание: возврат ДА гарантированно позволяет одновременное распознавание. возврат НЕТ не гарантируется, чтобы предотвратить одновременное распознавание, так как другой делегат может передать ДА

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

Ответ 5

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

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

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

  • Добавьте эту кнопку в качестве свойства вашей пользовательской ячейки.

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

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

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

Ответ 6

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

- (void)scrollViewDidScroll:(UIScrollView *)scrollView 

Ответ 7

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

Ответ 8

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

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

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

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

Ответ 9

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

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

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

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

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

Ответ 10

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

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

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

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

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

4) Установите headView в качестве губей UIView

self.tableView.headView = myHeadViewGubbins.

5) В методе tableViewController

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

делать:

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

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

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

Ответ 11

Я столкнулся с UITableView с scrollEnabled, являющимся NO в 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 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

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

Ответ 12

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

tableview.scrollEnabled = true;

Ответ 13

У меня такая же проблема. Затем обратитесь к разделу "Прокрутка прокрутки", как сказал lxx.

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

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

Это работа

Ответ 14

  • Отбросьте UIButton над вашим UITableViewCell и создайте выход как "btnRowSelect".
  • В вашем представлении контроллер помещает этот код в cellForRowAtIndexPath

    cell.btnRowSelect.tag = indexPath.row
    cell.btnRowSelect.addTarget(self, action: Selector("rowSelect:"), forControlEvents: .TouchUpInside)
    
  • Добавьте эту функцию в свой 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"