it-swarm.com.ru

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

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

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

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

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

59
Robert

Я имею дело с той же проблемой прямо сейчас. Образец 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 7iOS 8iOS 9 iOS 10 готова


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

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

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

36
Fabio Poloni

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

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

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

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

54
emreberge

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

- (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;

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

@property(strong, nonatomic) NSIndexPath *savedSelectedIndexPath;

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

19
Rhult

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

В подклассе 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];
            }
        }];
    }
}
9
nevan king

После того, как я сам сегодня столкнулся с этим, я обнаружил, что это, очевидно, довольно известная проблема с UITableView, его поддержка интерактивных переходов навигации немного нарушена. Люди, стоящие за Castro, опубликовали отличный анализ и решение этой проблемы: 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];
            }
        }];
    }
}
7
CodeStage

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

self.clearsSelectionOnViewWillAppear = YES;

в UITableViewController или

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

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

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

Простой Свифт 3/4 Ответ:

override func viewWillAppear(_ animated: Bool) {
    if tableView.indexPathForSelectedRow != nil {
        self.tableView.deselectRow(at: tableView.indexPathForSelectedRow! as IndexPath, animated: true)
    }
}
2
SteffenK

Я использую

[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];
}
2
Evgeny

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

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

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

Ответ Codestage , в 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)
            }
        }
    }
}
1
Mike

Использование 

[tableView deselectRowAtIndexPath:indexPath animated:YES]; 

код в 

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

1
Pradhyuman sinh

Для Свифта 

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

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

решение 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)
        }
    }
}
0
Kymer

Вероятно, вы не вызываете метод viewWillAppear суперпредставления ([super viewWillAppear: animated];) . Когда вы делаете это, а параметр UITableViewController clearsSelectionOnViewWillAppear имеет значение YES, ячейки будут отменены в viewWillAppear. 

0
Bess

Основываясь на коде 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];
   }
}
0
Simon

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)
                }
            })
        }
    }
0
AppreciateIt