【问题标题】:UITableViewCell doesn't get deselected when swiping back quickly快速向后滑动时 UITableViewCell 不会被取消选择
【发布时间】:2013-10-23 03:45:09
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: ios uitableview ios7


    【解决方案1】:

    简单的 Swift 3/4 答案:

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

    【讨论】:

    • 为 Swift 3 更新祝福你的心。??
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        为了快速

        override func viewWillAppear(animated: Bool) {
            super.viewWillAppear(animated)
        
            guard let indexPath = tableView.indexPathForSelectedRow else{
                return
            }
            tableView.deselectRowAtIndexPath(indexPath, animated: true)
        }
        

        【讨论】:

          【解决方案5】:

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

          这真的让我一点都不开心。如前所述,它使用[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 Class Reference

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

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

          【讨论】:

          • 很高兴我不是唯一一个这么想的人,我已经提交了一个错误。
          • 我不敢相信这仍然是 iOS 8 的问题。
          • 我不敢相信这仍然是 iOS 9 的问题。
          • 我不敢相信这仍然是 iOS 10 的问题。
          • 我不敢相信这仍然是 iOS 11 的问题
          【解决方案6】:

          Codestage provided by far the best looking answer,所以我决定将其转换为 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)
                          }
                      })
                  }
              }
          

          【讨论】:

            【解决方案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];
                        }
                    }];
                }
            }
            

            【讨论】:

              【解决方案8】:

              Rhult's solution 在 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)
                      }
                  }
              }
              

              【讨论】:

                【解决方案9】:

                此解决方案将行取消选择与转换协调器一起动画化(用于用户驱动的 VC 关闭),并在用户取消转换时重新应用选择。改编自 Swift 中 Caleb Davenport 的解决方案。仅在 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];
                            }
                        }];
                    }
                }
                

                【讨论】:

                • 我对此赞不绝口。谢谢!对于其他所有人 - 这是一个比这里介绍的大多数解决方案更好、更优雅的解决方案。
                【解决方案10】:

                基于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];
                   }
                }
                

                【讨论】:

                  【解决方案11】:

                  你可以尝试设置

                  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]; 
                  }
                  

                  【讨论】:

                  • self.clearsSelectionOnViewWillAppear = YES; 不起作用,但我预计会这样,因为无论如何这是默认设置。 [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:animated];- (void)viewWillAppear:(BOOL)animated 内(在调用super 之后)确实有效,但是我很困惑为什么这是必要的。当我在viewWillAppear: 中调用super 时,表视图控制器不应该这样做吗?为什么它只影响拖动模式而不影响标准的“点击后退按钮”?
                  • @falsecrypt [tableView deselectRowAtIndexPath:indexPath animated:YES]; 在我的项目中工作过,就像@Robert 一样,我也很困惑为什么这是必要的。感谢您的解决方案。
                  【解决方案12】:

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

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

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

                  【讨论】:

                  • reloadData 调用在某些情况下可能非常昂贵(想想大型数据库),所以它不是最好的方法。
                  【解决方案13】:

                  法比奥的回答效果很好,但如果用户只滑动一点然后改变主意,就不会给出正确的外观。为了正确处理这种情况,您需要保存选定的索引路径并在必要时重置它。

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

                  【讨论】:

                  • 我正在使用类似的解决方案,但我在调用取消选择动画之前选择了没有动画的行。
                  • 我不敢相信这不是 UITableViewController 的默认实现...
                  • 在 iOS 9.1 上完美运行
                  • 我不敢相信这是必要的!不过,它就像一个魅力。
                  • 在 iOS 9 上完美运行,仍然想知道为什么我们必须自己实现这一点
                  【解决方案14】:

                  我正在使用

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

                  【讨论】:

                    【解决方案15】:

                    这对我来说效果最好:

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

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

                    【讨论】:

                    • 也适用于 7.1
                    • 当用户向后滑动一半,然后又取消了平移手势,你会怎么做?
                    • 我在另一个答案中提到了“半滑动,然后取消”。
                    • 在 iOS 9 上将 clearsSelectionOnViewWillAppear 设置为 YESNO 对我没有任何改变,因此我将其省略了。不错的解决方案!
                    • @edelaney05,我不会做任何事——不值得。如果您查看 iOS 设置应用程序,它也有同样的问题。
                    【解决方案16】:

                    使用

                    [tableView deselectRowAtIndexPath:indexPath animated:YES];

                    代码

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

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2014-12-27
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多