UITableViewCell не отменяется при быстром обращении

Теперь я обновил три моих приложения до iOS 7, но во всех трех случаях, несмотря на то, что они не используют какой-либо код, у меня есть проблема, когда пользователь перебирается, чтобы вернуться в контроллер навигации (вместо того, ) быстро, ячейка останется в выбранном состоянии.

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

Это происходит только в моих приложениях iOS 7, собственные приложения Apple и сторонние приложения, обновленные для iOS 7, все, похоже, ведут себя нормально (хотя и с небольшими различиями в том, как быстро ячейки отключаются).

Должно быть, я что-то делаю неправильно, но я не уверен, что?

Ответ 1

Я имею дело с той же проблемой прямо сейчас. UICatalog - пример от Apple, похоже, приносит грязное решение.

Это действительно не радует меня. Как упоминалось ранее, он использует [self.tableView deselectRowAtIndexPath:tableSelection animated:NO]; для отмены выбранной текущей строки.

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    // this UIViewController is about to re-appear, make sure we remove the current selection in our table view
    NSIndexPath *tableSelection = [self.tableView indexPathForSelectedRow];
    [self.tableView deselectRowAtIndexPath:tableSelection animated:NO];

    // some over view controller could have changed our nav bar tint color, so reset it here
    self.navigationController.navigationBar.tintColor = [UIColor darkGrayColor];
}

Я должен упомянуть, что пример кода может не быть iOS 7 iOS 8 iOS 9 iOS 10-ready


Что-то, что меня действительно смущает, это Ссылка на класс UITableViewController:

Когда представление таблицы будет отображаться при первом загрузке, контроллер табличного представления перезагружает данные табличных представлений. Он также очищает его выбор (с анимацией или без нее, в зависимости от запроса) каждый раз, когда отображается представление таблицы. UITableViewControllerкласс реализует это в методе суперкласса viewWillAppear:. Вы может отключить это поведение, изменив значение в clearsSelectionOnViewWillAppear.

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

Ответ 2

Это сработало лучше для меня:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.clearsSelectionOnViewWillAppear = NO;
}

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:animated];
}

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

Ответ 3

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

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    self.savedSelectedIndexPath = nil;
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    if (self.savedSelectedIndexPath) {
        [self.tableView selectRowAtIndexPath:self.savedSelectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
    }
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    self.savedSelectedIndexPath = self.tableView.indexPathForSelectedRow;

    if (self.savedSelectedIndexPath) {
        [self.tableView deselectRowAtIndexPath:self.savedSelectedIndexPath animated:YES];
    }
}

Если вы используете UITableViewController, обязательно отключите встроенную очистку:

self.clearsSelectionOnViewWillAppear = NO;

и добавьте свойство для savedSelectedIndexPath:

@property(strong, nonatomic) NSIndexPath *savedSelectedIndexPath;

Если вам нужно сделать это в нескольких разных классах, это может иметь смысл разделить его в помощнике, например, как и в этом контексте: https://gist.github.com/rhult/46ee6c4e8a862a8e66d4

Ответ 4

Это решение анимирует выделение строки вместе с координатором перехода (для отклонения от VC, управляемого пользователем) и повторно применяет выбор, если пользователь отменяет переход. Адаптировано из решения Caleb Davenport в Swift. Проверено только на iOS 9. Протестировано как работа с переходом с помощью пользователя (swipe), так и с нажатием кнопки "Назад" старого стиля.

В подклассе UITableViewController:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    // Workaround. clearsSelectionOnViewWillAppear is unreliable for user-driven (swipe) VC dismiss
    NSIndexPath *indexPath = self.tableView.indexPathForSelectedRow;
    if (indexPath && self.transitionCoordinator) {
        [self.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
            [self.tableView deselectRowAtIndexPath:indexPath animated:animated];
        } completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
            if ([context isCancelled]) {
                [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
            }
        }];
    }
}

Ответ 5

После того, как я начал это сегодня, я узнал, что это, видимо, довольно известная проблема с UITableView, его поддержка для интерактивных навигационных переходов слегка нарушена. Люди за Кастро опубликовали отличный анализ и решение для этого: http://blog.supertop.co/post/80781694515/viewmightappear

Я решил использовать их решение, которое также рассматривает отмененные переходы:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    NSIndexPath *selectedRowIndexPath = [self.tableView indexPathForSelectedRow];
    if (selectedRowIndexPath) {
        [self.tableView deselectRowAtIndexPath:selectedRowIndexPath animated:YES];
        [[self transitionCoordinator] notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context) {
            if ([context isCancelled]) {
                [self.tableView selectRowAtIndexPath:selectedRowIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
            }
        }];
    }
}

Ответ 6

Вы можете попробовать установить

self.clearsSelectionOnViewWillAppear = YES;

в UITableViewController или

[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:NO];

в viewWillAppear, возможно, перед вызовом [super viewWillAppear: анимированный]; Если ваш UItableView не внутри UITableViewController, вы должны отменить выбор ячейки вручную:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES]; 
}

Ответ 7

Я использую

[tableView deselectRowAtIndexPath:indexPath animated:YES];

в конце метода

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

Вот так:

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

{

    //doing something according to selected cell...

    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}

Ответ 8

Простой ответ Swift 3:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    if tableView.indexPathForSelectedRow != nil {
        self.tableView.deselectRow(at: tableView.indexPathForSelectedRow! as IndexPath, animated: true)
    }
}

Ответ 9

Используйте

[tableView deselectRowAtIndexPath:indexPath animated:YES];

код в

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method

Ответ 10

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

Все, что вам нужно сделать, чтобы предотвратить это странное поведение, - это перезагрузить таблицу при отображении вида:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self.tableView reloadData];
}

Ответ 11

Codestage answer, в Swift 3. notifyWhenInteractionEnds устарел.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(true)

    if let indexPath = self.tableView.indexPathForSelectedRow {
        self.tableView.deselectRow(at: indexPath, animated: true)
        self.transitionCoordinator?.notifyWhenInteractionChanges { (context) in
            if context.isCancelled {
                self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
            }
        }
    }
}

Ответ 12

На основе кода Rhult я внес несколько изменений.

Эта реализация позволяет пользователю отменить салфетки назад и по-прежнему сохранять выбранные для последующего прокрутки назад отменить выделение анимации

@property(strong, nonatomic) NSIndexPath *savedSelectedIndexPath;


- (void)viewDidLoad {
   [super viewDidLoad];
   self.clearsSelectionOnViewWillAppear = NO;
}

-(void) viewDidAppear:(BOOL)animated {
   [super viewDidAppear:animated];
   self.savedSelectedIndexPath = nil;
}

-(void) viewWillDisappear:(BOOL)animated {
   [super viewWillDisappear:animated];
   if (self.savedSelectedIndexPath && ![self.tableView indexPathForSelectedRow]) {
       [self.tableView selectRowAtIndexPath:self.savedSelectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
   } else {
      self.savedSelectedIndexPath = [self.tableView indexPathForSelectedRow];
   }
}

Ответ 13

Решение Rhult отлично работает на iOS 9.2. Это реализация в Swift:

Объявите переменную в MasterViewController для сохранения IndexPath:

var savedSelectedIndexPath: NSIndexPath?

Затем вы можете поместить код в расширение для ясности:

extension MasterViewController {
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        self.savedSelectedIndexPath = nil
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        if let indexPath = self.savedSelectedIndexPath {
            self.tableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: .None)
        }
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        self.savedSelectedIndexPath = tableView.indexPathForSelectedRow
        if let indexPath = self.savedSelectedIndexPath {
            self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
        }
    }
}

Ответ 14

Codestage предоставил лучший ответ, поэтому я решил преобразовать его в Swift 2.

override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(true)

        let selectedRowIndexPath = self.tableView.indexPathForSelectedRow
        if ((selectedRowIndexPath) != nil) {
            self.tableView.deselectRowAtIndexPath(selectedRowIndexPath!, animated: true)
            self.transitionCoordinator()?.notifyWhenInteractionEndsUsingBlock({ context in
                if (context.isCancelled()) {
                    self.tableView.selectRowAtIndexPath(selectedRowIndexPath, animated: false, scrollPosition: UITableViewScrollPosition.None)
                }
            })
        }
    }

Ответ 15

Для быстрого

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    guard let indexPath = tableView.indexPathForSelectedRow else{
        return
    }
    tableView.deselectRowAtIndexPath(indexPath, animated: true)
}