【问题标题】:Prevent interaction on subview of UITableViewCell防止 UITableViewCell 子视图上的交互
【发布时间】:2016-07-08 22:48:15
【问题描述】:

我有一个很大的 UITableViewCell,里面有 2 个自定义 UIView。其中一个应该允许交互(选择和取消选择),而其中一个视图应该不允许交互。

我知道我可以在整个单元格或每个子视图上启用或禁用交互,但如果我在 UIView 上禁用交互,则单元格仍会被选中或取消选中。

有没有办法,例如,允许触摸单元格的上半部分而不是底部?

这是我描述的 UITableViewCell 的截图

【问题讨论】:

    标签: ios swift cocoa-touch


    【解决方案1】:

    您可以通过点击测试转发或取消触摸

    override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
            if let hitView = super.hitTest(point, withEvent: event) {
                if hitView.isKindOfClass(CustomSubviewClass) {
                    return nil
                } else {
                    return hitView
                }
            } else {
                return nil
            }
        }
    

    【讨论】:

    • 您建议在 UITableView 或其中一个子视图中实现此功能吗?无论哪种方式,我都会使用 EXC_BAD_ACCESS 崩溃
    【解决方案2】:

    我会说您的实现存在缺陷,或者您有 X/Y 问题。考虑将单个单元格拆分为两个单独的单元格,并禁用底部单元格的选择。

    如果您必须继续执行此实现,请考虑在整个单元格上禁用选择。在顶部子视图中放置一个手势识别器,并在触摸时手动调用didSelectRowAtIndexPath:

    【讨论】:

      【解决方案3】:

      我完全同意JAL,你需要检查你所有的单元结构,但我也知道有时,在某些情况下,重构是不可能的。

      所以,我认为,假设您有一个由两个视图组成的CustomTableViewCell,例如命名为 view1 和 view2:

      代码:

      class MyView1 : UIView {
          var isTouched : Bool = false
          override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
              self.isTouched = true
              self.nextResponder()?.touchesBegan(touches, withEvent: event)
          }
          override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
              self.isTouched = false
              self.nextResponder()?.touchesEnded(touches, withEvent: event)
          }
      }
      class MyView2 : UIView {
          var isTouched : Bool = false
          override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
              self.isTouched = true
              self.nextResponder()?.touchesBegan(touches, withEvent: event)
          }
          override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
              self.isTouched = false
              self.nextResponder()?.touchesEnded(touches, withEvent: event)
          }
      }
      class CustomTableViewCell: UITableViewCell {
          @IBOutlet weak var myExampleLabel: UILabel!
          @IBOutlet weak var view1: MyView1!
          @IBOutlet weak var view2: MyView2!
      
          override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
              print("situation: myView1 : \(self.view1.isTouched) myView2: \(self.view2.isTouched)")
              var responder : UIResponder! = self
              while responder.nextResponder() != nil {
                 responder = responder.nextResponder()
                 if responder is SimpleTableView.ViewController { // the name of your tableView delegate class
                      let vc = responder as! ViewController
                      let indexPath = vc.tableView.indexPathForCell(self)
                      vc.tableView(vc.tableView, didSelectRowAtIndexPath: indexPath!)
                  }
              }
              super.touchesBegan(touches, withEvent: event)
          }
      }
      

      到另一边,例如 UIViewControllerUITableView

      class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
          @IBOutlet var tableView: UITableView!
          var items: [String] = ["We", "Heart", "Swift", "So", "Much"]
      
          override func viewDidLoad() {
              super.viewDidLoad()
              tableView.delegate = self
              tableView.dataSource = self
              tableView.rowHeight = UITableViewAutomaticDimension
              tableView.estimatedRowHeight = 140.0
              self.tableView.registerNib(UINib(nibName: "CustomTableViewCell", bundle:nil), forCellReuseIdentifier: "CustomTableViewCell")
          }
      
          func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
              return self.items.count;
          }
      
          func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
              let cell = tableView.dequeueReusableCellWithIdentifier("CustomTableViewCell", forIndexPath: indexPath) as! CustomTableViewCell
              cell.myExampleLabel.text = self.items[indexPath.row]
              return cell
          }
      
          func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
              let cell = tableView.cellForRowAtIndexPath(indexPath) as! CustomTableViewCell
              print("row = %d",indexPath.row)
              if cell.view1.isTouched { 
                 print("tableView: touched view 1") 
                 // do whatever you want with cell.view1
                 // if you want you can disable cell.view2.userInteractionEnabled
              }
              else {
                  print("tableView: touched view 2")
                  // same thing for cell.view2
              }
          }
      }
      

      一些重要的事情

      不要忘记为两个视图设置正确的自定义类,如图所示:

      【讨论】:

      【解决方案4】:

      完成这项工作的一种方法是绕过UITableViewDelegate 方法,并在单元格上使用IBAction 和自定义回调处理程序。

      1. 将单元格的选择类型设置为,以防止在您点击单元格时被选中/取消选中。
      2. 确保您的 UITableViewDelegate 未实现 tableView:didSelectRowAtIndexPath
      3. 在单元格中添加一个按钮视图并将其放置在您希望用户与之交互的区域。
      4. UITableViewCell 子类中添加@IBAction func buttonTapped(sender: UIButton)
      5. 向单元格添加一个闭包变量。点击按钮时调用闭包。
      6. tableView:cellForRowAtIndexPath: 委托方法中设置单元格的闭包以处理调用。

      示例UITableViewCell 子类:

      class MyTableViewCell: UITableViewCell {
      
          var selectionHandler: ((Void) -> Void)?
      
          override func prepareForReuse() { 
              // Remove the closure when the cell is recycled.
              selectionHandler = nil
          }
      
          @IBAction func buttonTapped(sender: UIButton) {
              // Call the closure when the button is tapped.
              selectionHandler?()
          }
      }
      

      在你的UITableViewController:

      class MyTableViewController: UITableViewController {
      
          override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      
              let cell = ....
      
              // Set the closure to be called when the button is tapped.
              // Using weak self here to prevent a retain cycle.
              cell.selectionHandler = { [weak self] in
                  self?.selectedCellAtIndexPath(indexPath)
              }
      
              return cell
          }
      
          // We are not using didSelectRowAtIndexPath
          // override func tableView(tableView: UITableView, didSelectRowAtIndexPath: NSIndexPath) {
          // }
      }
      

      我没有测试过这段代码,所以它可能无法编译,尽管它应该用来说明这个概念。

      【讨论】:

        【解决方案5】:

        在表格视图属性编辑器中,将 Selection 值设置为 No Selection 或以编程方式进行。禁用每个子视图的交互,除了您想要操作的子视图。也许将 indexPath 行值作为标签传递给该子视图。或从UIView 子类化该子视图以存储 indexPath 信息。接下来,对该子视图实施轻击手势,并在轻击手势识别方法上执行您想要的操作。希望这些应该做你想做的。

        【讨论】:

          猜你喜欢
          • 2016-10-03
          • 1970-01-01
          • 2019-07-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多