簡體   English   中英

刪除 TableView 的底行

[英]Deleting the bottom row of TableView

非常感謝DonMag對這個問題的貢獻。

原始問題

我在我的代碼中實現了滑動刪除行手勢,如下所示:

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            // Delete Rows Action.
            Results.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .left)
            tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .fade)
        }
    }

這是它的外觀圖片。

毛刺

我想要實現的目標:

電話記錄動畫

平台:Xcode 10、iOS 12。

解決方案

DonMag指出它很可能是 iOS 12 的一個錯誤。所以接下來的解決方案只是針對 iOS 的幾個特定版本的解決方案。我的單元格高度是 100,所以為了簡單起見,我只輸入 100。 在 iOS 13 Apple 已修復該問題。

iOS 12

    // DELETE Action
    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        
        if (editingStyle == .delete && indexPath.row < Results.count) {
            // HIDE the glitch when deleting rows.
            let totalheight = self.tableView.frame.size.height // Total height of screen.
            let contentOffsetY = self.tableView.contentOffset.y // The current position of top
            let SizeShrinkage = (self.tableView.contentSize.height-100 < self.tableView.frame.size.height-getToolbarHeight()-getTitleBarHeight()) && (self.tableView.contentSize.height > self.tableView.frame.size.height-getToolbarHeight()-getTitleBarHeight()) // If the height of content size is near to the screen height.
            let currentHeight = contentOffsetY + totalheight - getToolbarHeight() // Current position of bottom. (with toolbar.)
            let contentHeight = self.tableView.contentSize.height
            MovingOffset = Double(currentHeight - contentHeight + 100) // The distance from the content's bottom edge to the top of toolbar (The length that the content exceeded from the safe area) after deleting a row.
            let onlyOnBottom = (MovingOffset <= 100 && MovingOffset > 0) // It's on the bottom. The exceeded is larger than 0.
            // Delete Rows Action.
            tableView.beginUpdates()
            if (onlyOnBottom || SizeShrinkage) {
                tableView.contentInset.bottom += (currentHeight-contentHeight+100) // Set the inset that it exceeded
            }
            Results.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)
            tableView.endUpdates()
            
            if SizeShrinkage {
                NotificationCenter.default.post(name: NSNotification.Name(rawValue: NotificationDeleteOversizedRecordKey), object: self)
            } else if onlyOnBottom {
                NotificationCenter.default.post(name: NSNotification.Name(rawValue: NotificationDeleteBottomRecordKey), object: self)
            }
        }
    }

getToolbarHeight()getTitleBarHeight()是我自定義的 function 可以獲取不同設備的工具欄高度和標題欄高度。 而我在NFCenter中添加了兩個監聽器,讓他們處理rest animation。這是因為我發現當我在刪除行function中運行animation時,有時animation不會執行。

     /**
     Delete Animation
     */
    @objc func scrollOneLineUp(_ button: UIBarButtonItem?) {
        UIView.animate(withDuration: 1, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 8.0, options: .curveEaseInOut, animations: {self.tableView.contentInset.bottom=0}, completion: {finished in self.tableView.contentOffset.y += CGFloat(self.MovingOffset)})
    }
    @objc func scrollToTop(_ button: UIBarButtonItem?) {
        self.tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
        
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5) {
            self.tableView.contentInset.bottom = 0
        }
    }

這是它最終的樣子。 刪除“水5”和“水6”時,為了隱藏故障,它會移動到頂部(只是到頂部,但不顯示大標題,是我向下拖動讓它顯示大標題),並且其他時間它將移動 1 行。 對於小於 water 5 的行,我們不需要處理 glitch。 它看起來很棒。

它最終是什么樣子

iOS 11

在 iOS 11 上,animation 在大部分時間看起來都不錯。 但是刪除某行時,刪除后內容大小先小於內容顯示區域,就會出現bug。 iOS 11 的解決方案比 iOS 12 簡單得多。

在刪除行 function 中實現它( override func tableView

if(tableView.contentSize.height<=tableView.bounds.height+100.0 && tableView.contentSize.height>tableView.bounds.height) {
    tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
}
// The 100.0f is the cell height.

100.0f 是單元格的高度。 因此,當內容的高度首先小於 tableView 的邊框時,tableview 滾動到最頂部,並且 glitch animation 被滾動 animation 覆蓋。

iOS的其他版本沒有大標題,工具欄、標題欄和狀態欄的高度一直在變化,所以我沒有測試。

編輯 2

經過一些實驗,這似乎是iOS 12的問題(至少在我的iOS 12.4模擬器設備上是這樣)。

這可以(有點)證實

  • 啟動默認的 iPhone提醒應用程序
  • 添加一堆提醒(我剛剛使用了 A、B、C 等...)所以您有足夠的時間需要滾動
  • 向下滾動到最后一行
  • 刪除最后一行

你會看到行刪除 animation 是錯誤的。 事實上,刪除任何行都不會提供我們在iOS 13中看到的平滑 animation 。

進行一些搜索后,我發現與iOS 7類似的問題據報道已在iOS 8+中修復...因此可能在iOS 12版本之一中返回了相同的錯誤。 也許可以使用其中一種較舊的變通方法,但根據評論,它是很失敗的。

不過,我仍然堅持我最初的評論,沒有理由:

tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .fade)

在刪除行后調用。


原始答案

聽起來你做的太多了......

這就是讓要刪除的行和其他行“向下滑動”所需的全部內容:

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        Results.remove(at: indexPath.row)
        tableView.deleteRows(at: [indexPath], with: .fade)
    }
}

無需重新加載數據。

請參閱此處的示例: https://imgur.com/a/JvDQ1P6


編輯

這是一個完整(非常簡單)的例子。 沒有@IBOutlet@IBAction連接...只需將新表視圖 controller 添加到您的 Storyboard 並將其自定義 class 分配給SampleTableViewController

class SampleCell: UITableViewCell {

    let testView: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .blue
        return v
    }()
    let testLabel: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .cyan
        return v
    }()

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }

    @objc
    func commonInit() -> Void {

        contentView.addSubview(testView)
        contentView.addSubview(testLabel)
        let g = contentView.layoutMarginsGuide

        // avoid constraint warnings
        let c = testView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0)
        c.priority = UILayoutPriority(rawValue: 999)

        NSLayoutConstraint.activate([

            c,
            testView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
            testView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            testView.heightAnchor.constraint(equalToConstant: 50.0),
            testView.widthAnchor.constraint(equalTo: testView.heightAnchor),

            testLabel.leadingAnchor.constraint(equalTo: testView.trailingAnchor, constant: 12.0),
            testLabel.centerYAnchor.constraint(equalTo: testView.centerYAnchor),
            testLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),

        ])

    }

}

class SampleTableViewController: UITableViewController {
    var data: [String] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        // fill data array with 30 strings
        data = (1...30).map { "This is row \($0)" }

        tableView.register(SampleCell.self, forCellReuseIdentifier: "SampleCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let c = tableView.dequeueReusableCell(withIdentifier: "SampleCell", for: indexPath) as! SampleCell
        c.testLabel.text = data[indexPath.row]
        return c
    }

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            data.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM