根据选择将UITableView滚动到某些indexPath

时间:2018-08-09 04:36:25

标签: ios swift uitableview

我有一个带有自定义单元格的UITableView。自定义单元格为三个按钮,覆盖了单元格的整个范围。我试图根据用户选择的标签自动滚动tableview。

我尝试使用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")
}

让我知道是否需要更多信息。谢谢。

6 个答案:

答案 0 :(得分:2)

除了使用通知,您还可以使用按钮在单元格上设置回调,以便在点击其中一个按钮时,使用关于哪个按钮被点击的信息运行回调,以便视图控制器可以执行其操作需要配合它:

作为您要执行的操作的示例,我创建了一个示例项目,您可以下载并查看该示例项目(Xcode 9.4.1,Swift 4.1),虽然有点混乱,但它可以满足您的需要:{{3 }}

首先,使用按钮来设置单元格以处理回调:

 $body = "<html><body>";
 $Body .= "<table>";
 $Body .= "<thead>";
 $Body .= "<tr>";
 $Body .= "<th></th>";

 ...

 $Body .= "</tr>";
 $Body .= "</thead>";
 $Body .= "<tbody>";

 foreach($urunler as $urun) 
 {
     $gelen_kod = $urun['urun_kodu'];
     $gelen_kod_miktar = $urun['miktar'];
     $gelen_toplam_fiyat = $urun['toplam_fiyat'];
     $urun_kdv_dahil_fiyati = $urun['KDV_dahil_fiyat'];
     $urun_adi = $urun['urun_adi'];

     $Body .= "<tr style='border: 1px solid #ddd;'>";
     $Body .= "<th scope='row'>1</th>";
     $Body .= "<td style='border: 1px solid #ddd;'>$gelen_kod</td>";
     $Body .= "<td style='border: 1px solid #ddd;'>$urun_adi</td>";
     $Body .= "<td style='border: 1px solid #ddd;'>$gelen_kod_miktar</td>";
     $Body .= "<td style='border: 1px solid #ddd;'>ADET</td>";
     $Body .= "<td style='border: 1px solid #ddd;'>34.25 TL</td>";
     $Body .= "<td style='border: 1px solid #ddd;'>%0</td>";
     $Body .= "<td style='border: 1px solid #ddd;'>%5</td>";
     $Body .= "<td style='border: 1px solid #ddd;'>$urun_kdv_dahil_fiyati</td>";
     $Body .= "</tr>"; 
}

$Body .= "</tbody>";    
$Body .= "</table>";
$Body .= "</body></html>";

$mail->Body = $Body;
$mail->IsHTML(true);

对按钮使用枚举可以使代码比仅用于识别按钮的原始数字更整洁。每个按钮都会调用标识自己的回调。

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

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,以便它传递了闭包,而不是运行闭包的结果。是运行闭包的单元。

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

handler()

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

答案 1 :(得分:1)

使用发送通知的单元格作为发送方对象,而不是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)
    }
}

答案 2 :(得分:1)

我认为这是崩溃的原因。

  

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

Refer this

您可以使用关闭委托代替通知

答案 3 :(得分:1)

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

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

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

答案 4 :(得分:1)

ScrollToRow方法有问题。 UITableViewScrollPosition保留以下值-顶部中间底部枚举值。

更改行:

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

答案 5 :(得分:1)

更好的方法是使用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添加,也可以使其通用以在一次回叫中接受响应并执行动画。