61

我现在已经将我的三个应用程序更新到 iOS 7,但是在所有三个应用程序中,尽管它们没有共享任何代码,但我遇到的问题是,如果用户快速滑动以返回导航控制器(而不是点击返回按钮) ,单元格将保持其选定状态。

对于这三个应用程序,一个使用以编程方式创建的自定义单元格,另一个使用在情节提要中创建的自定义单元格,第三个使用 UITableView 的一个非常基本的子类中的默认单元格,也在情节提要中。在所有三种情况下,单元格都不会自行取消选择。如果用户缓慢滑动或点击后退按钮,他们会像往常一样取消选择。

这仅发生在我的 iOS 7 应用程序中,Apple 自己的应用程序和为 iOS 7 升级的第三方应用程序似乎都表现正常(尽管单元格被取消选择的速度略有不同)。

一定有什么我做错了,但我不确定是什么?

4

16 回答 16

54

这对我最有效:

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

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

当我缓慢地向后滑动时,我什至得到了更好的取消选择淡入淡出。

于 2013-11-25T17:21:45.837 回答
37

我现在正在处理同样的问题。Apple的UICatalog样本似乎带来了肮脏的解决方案。

这真的一点都不让我开心。如前所述,它用于[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 9iOS 10 就绪


真正让我困惑的是UITableViewController 类参考

当表格视图第一次加载时即将出现时,表格视图控制器会重新加载表格视图的数据。每次显示表视图时,它也会清除其选择(有或没有动画,取决于请求)。该类UITableViewController 在超类方法中实现了这一点viewWillAppear:clearsSelectionOnViewWillAppear您可以通过更改属性中的值来禁用此行为 。

这正是我所期望的行为……但它似乎不起作用。既不为你,也不为我。我们真的必须使用“肮脏”的解决方案并自己做。

于 2013-11-10T11:52:11.487 回答
19

Fabio 的回答效果很好,但如果用户稍微滑动一下然后改变主意,就不会给出正确的外观。为了使这种情况正确,您需要保存选定的索引路径并在必要时将其重置。

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

于 2014-09-13T07:30:57.913 回答
9

此解决方案与转换协调器(用于用户驱动的 VC 关闭)一起动画行取消选择,并在用户取消转换时重新应用选择。改编自Caleb Davenport在 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];
            }
        }];
    }
}
于 2015-12-04T16:11:44.350 回答
7

今天我自己遇到这个问题后,我发现这显然是 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];
            }
        }];
    }
}
于 2016-01-25T10:10:16.510 回答
5

你可以尝试设置

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]; 
}
于 2013-10-15T11:14:54.480 回答
3

简单的 Swift 3/4 答案:

override func viewWillAppear(_ animated: Bool) {
    if tableView.indexPathForSelectedRow != nil {
        self.tableView.deselectRow(at: tableView.indexPathForSelectedRow! as IndexPath, animated: true)
    }
}
于 2016-11-06T18:06:45.167 回答
2

我在用着

[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];
}
于 2014-03-27T12:50:16.190 回答
1

利用

[tableView deselectRowAtIndexPath:indexPath animated:YES];

代码在

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

于 2013-10-15T11:18:15.043 回答
1

对于这个问题,我找到了一个非常简单的解决方案,它可以让默认行为正常工作。我对所涉及的解决方案不满意,deselectRowAtIndexPath因为由此产生的视觉效果略有不同。

为了防止这种奇怪的行为,您所要做的就是在显示视图时重新加载表:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self.tableView reloadData];
}
于 2014-12-20T00:39:01.243 回答
1

Swift 3.中的Codestage answernotifyWhenInteractionEnds已弃用。

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)
            }
        }
    }
}
于 2016-09-22T09:06:58.287 回答
1

您可能没有调用超级视图的 viewWillAppear 方法 ([super viewWillAppear:animated];)。当您这样做并且 UITableViewController 的参数 clearsSelectionOnViewWillAppear 为 YES 时,单元格将在 viewWillAppear 上被取消选择。

于 2018-10-11T22:54:02.273 回答
0

基于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];
   }
}
于 2015-03-19T03:07:23.003 回答
0

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)
        }
    }
}
于 2015-12-29T10:14:42.720 回答
0

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)
                }
            })
        }
    }
于 2016-05-17T09:19:13.003 回答
0

为了迅速

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

    guard let indexPath = tableView.indexPathForSelectedRow else{
        return
    }
    tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
于 2016-09-01T07:06:06.647 回答