![](/img/trans.png)
[英]Incorrect tableView row is deleted when deleting tableView rows in ascending order (bottom to top)
[英]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 已修復該問題。
// 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 上,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
模擬器和設備上是這樣)。
這可以(有點)證實
你會看到行刪除 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.