【问题标题】:Scroll UITableView to certain indexPath depending on selection根据选择将 UITableView 滚动到某个 indexPath
【发布时间】:2018-08-09 04:36:25
【问题描述】:

我有一个带有自定义单元格的 UITableView。自定义单元格为三个按钮,覆盖整个单元格。我正在尝试根据用户选择的标签自动滚动表格视图。

我尝试使用 NotificationCenter,但在尝试滚动时它会崩溃。有没有更优雅的解决方案?

编辑:我的最终目标是让用户点击 optionOne 并让它滚动到紧邻下方的单元格。诀窍是在没有 cellForRowAt 的情况下执行此操作,因为由于有多个按钮,我在单元格中处理它。

自定义单元格:

@IBOutlet weak var optionOneLabel: UILabel!
@IBOutlet weak var optionTwoLabel: UILabel!
@IBOutlet weak var optionThreeLabel: UILabel!

override func prepareForReuse() {
    super.prepareForReuse()

    let optionOne = UITapGestureRecognizer(target: self, action: #selector(CustomCell.optionOnePressed(_:)))
    optionOneLabel.addGestureRecognizer(optionOne)

    let optionTwo = UITapGestureRecognizer(target: self, action: #selector(CustomCell.optionTwoPressed(_:)))
    optionTwoLabel.addGestureRecognizer(optionTwo)

    let optionThree = UITapGestureRecognizer(target: self, action: #selector(CustomCell.optionThreePressed(_:)))
    optionThreeLabel.addGestureRecognizer(optionThree) 
}

@objc func optionOnePressed(_ sender: UITapGestureRecognizer) {
    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "scrollTableOptionOne"), object: nil)
}

@objc func optionTwoPressed(_ sender: UITapGestureRecognizer) {
    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "scrollTableOptionTwo"), object: nil)
}

@objc func optionThreePressed(_ sender: UITapGestureRecognizer) {
    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "scrollTableOptionthree"), object: nil)
}

TableViewController

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(optionOne), name: NSNotification.Name(rawValue: "scrollTableOptionOne"), object: nil)

    NotificationCenter.default.addObserver(self, selector: #selector(optionTwo), name: NSNotification.Name(rawValue: "scrollTableOptionTwo"), object: nil)

    NotificationCenter.default.addObserver(self, selector: #selector(optionThree), name: NSNotification.Name(rawValue: "scrollTableOptionThree"), object: nil)

}

@objc func optionOne() {
    let indexPath = NSIndexPath(item: posts.count, section: 0)
    tableView.scrollToRow(at: indexPath as IndexPath, at: UITableViewScrollPosition.init(rawValue: indexPath.row + 2)!, animated: true)
}

@objc func optionTwo() {
    print("Don't scroll this one for now")
}

@objc func optionThree() {
    print("Don't scroll this one for now")
}

如果您需要更多信息,请告诉我。谢谢。

【问题讨论】:

  • 你是单节还是多节?
  • 崩溃报告说什么?另外,不要在prepareForReuse 中添加通知,因为您最终会添加多个相同类型的手势,因为您没有删除第一次添加的手势。

标签: ios swift uitableview


【解决方案1】:

您可以使用按钮在单元格上设置回调,而不是使用通知,这样当点击其中一个按钮时,回调会使用有关点击哪个按钮的信息运行,以便视图控制器可以执行它需要它:

作为您想要做的一个示例,我创建了一个示例项目,您可以下载并查看(Xcode 9.4.1,Swift 4.1)它有点乱,但它可以满足您的需求:https://bitbucket.org/abizern/so51758928/src/master/

具体是,首先,设置带有按钮的单元格来处理回调:

class SelectionCell: UITableViewCell {
    enum Button {
        case first
        case second
        case third
    }

    var callback: ((Button) -> Void)?

    @IBAction func firstButtonSelected(_ sender: UIButton) {
        callback?(.first)
    }

    @IBAction func secondButtonSelected(_ sender: UIButton) {
        callback?(.second)
    }

    @IBAction func thirdButtonSelected(_ sender: UIButton) {
        callback?(.third)
    }
}

对按钮使用枚举使代码比仅用于识别按钮的原始数字更清晰。每个按钮调用回调标识自己。

在表格视图控制器中,当您在委托中设置单元格时,您将这个回调传递给单元格:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let value = values[indexPath.row]
    switch value {
    case .header:
        let cell = tableView.dequeueReusableCell(withIdentifier: "Selection", for: indexPath) as! SelectionCell
        cell.callback = handler
        return cell
    case .value:
        let cell = tableView.dequeueReusableCell(withIdentifier: "Display", for: indexPath) as! DisplayCell
        cell.configure(with: value)
        return cell
    }
}

注意,我传递了handler 而不是handler(),因此它将闭包而不是运行闭包的结果传递给单元格。它是运行闭包的单元格。

回调看起来像这样(对于我的例子,你的需求会有所不同) 我正在使用传递给闭包的按钮标识符来识别我感兴趣的 IndexPath 并将其滚动到顶部

private func handler(button: SelectionCell.Button) -> Void {
    print("selected \(button)")
    let index: Int?
    switch button {
    case .first:
        index = values.index(where: { (value) -> Bool in
            if case .value("First") = value {
            return true
            }
            return false
        })

    case .second:
        index = values.index(where: { (value) -> Bool in
            if case .value("Second") = value {
                return true
            }
            return false
        })
    case .third:
        index = values.index(where: { (value) -> Bool in
            if case .value("Third") = value {
                return true
            }
            return false
        })
    }
    guard let row = index else { return }
    let indexPath = IndexPath(row: row, section: 0)

    tableView.scrollToRow(at: indexPath, at: .top, animated: true)
}

正如我已经说过的,您可以查看示例项目以了解这一切是如何工作的,但这些是重要的步骤。

【讨论】:

    【解决方案2】:

    使用发送通知的单元格作为发送者对象而不是 nil。在你的牢房里做:

    NotificationCenter.default.post(
        name: NSNotification.Name(rawValue: "scrollTableOptionOne"), 
        object: self)
    

    更改您的通知处理程序,以便它可以访问通知。从通知中检索单元格并找出 indexPath。

    @objc func optionOne(ntf: Notification) {
        let cell = ntf.object as! UITableViewCell
        if let indexPath = tableView.indexPath(for: cell) {
            tableView.scrollToRow(at: indexPath, 
              at: .bottom, animated: true)
        }
    }
    

    【讨论】:

      【解决方案3】:

      我认为这是崩溃的原因。

      出于性能原因,您应该只重置单元格的属性 与内容无关的内容,例如 alpha、编辑和 选择状态。表视图的委托 tableView(_:cellForRowAt:) 应始终重置所有内容 重用单元格

      Refer this

      您可以使用 ClosuresDelegate 而不是使用通知

      【讨论】:

      • 嗯,好吧。我可以看看我是否将单元格的代表设置为 tableview 我可以让按钮导致一个动作......但这仍然不能解决在按钮单击时将 tableview 向下滚动一行的问题。还是这样?:)
      • @CaseyWest 滚动到该索引不会成为问题。
      • 你能在这方面给我一个提示吗?问题仍然存在。
      【解决方案4】:

      我认为您需要像这样更改代码

      let indexPath = IndexPath(row: posts.count - 1, section: 0)
      tableView.scrollToRow(at: indexPath, at: .top, animated: true)
      

      这会将您的行滚动到表格视图的顶部。

      【讨论】:

      • 嗯...好的,谢谢您的信息。我的最终目标是实际滚动到当前单元格下方的单元格,假设那里有一个单元格。我将更新问题以反映这一点。谢谢。
      【解决方案5】:

      ScrollToRow 方法有一些问题。 UITableViewScrollPosition 保存值 - nonetopmiddlebottom 枚举值。

      换行:

       tableView.scrollToRow(at: indexPath as IndexPath, at: UITableViewScrollPosition.init(rawValue: indexPath.row + 2)!, animated: true)
      

      收件人:

      tableView.scrollToRow(at: indexPath as IndexPath, at: .top, animated: true)
      

      为了跟踪索引路径,将行值设置为单元格的标签,在cellForRow:

      cell.tag = indexPath.row
      

      并将通知发布为:

      NotificationCenter.default.post(name: NSNotification.Name(rawValue: "scrollTableOptionOne"), object: self.tag)
      
      @objc func optionOne(_ notification: Notification) {
          let indexPath = NSIndexPath(item: notification.object+1, section: 0)
          tableView.scrollToRow(at: indexPath as IndexPath, at: UITableViewScrollPosition.init(rawValue: indexPath.row + 2)!, animated: true)
      }
      

      【讨论】:

        【解决方案6】:

        更好的方法是使用closures:

        @IBOutlet weak var optionOneLabel: UILabel!
        @IBOutlet weak var optionTwoLabel: UILabel!
        @IBOutlet weak var optionThreeLabel: UILabel!
        
        var callBackOnLabel : (()->())? /// Add this in cell.
        
        override func awakeFromNib() {
            super.awakeFromNib()
            // Initialization code
        
            let optionOne = UITapGestureRecognizer(target: self, action: #selector(CustomCell.optionOnePressed(_:)))
            optionOneLabel.addGestureRecognizer(optionOne)
        
         }
        
        @objc func optionOnePressed(_ sender: UITapGestureRecognizer) {
            self.callBackOnLabel?()
        }
        

        然后在你的cellForRowAt:

        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
        
            cell.callBackOnLabel = {
                let selectedIndex = IndexPath(row: posts.count, section: 0)
        
                tableView.scrollToRow(at: selectedIndex, at: .middle, animated: true)
            }
        }
        

        以同样的方式,您可以添加其他两个labels,或者您可以使其通用以在单个回调中接受响应并执行动画。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-02
          相关资源
          最近更新 更多