Ios 使用简单的TableView Swift构建单元格时的奇怪行为
我花了一段疯狂的时间来修复我们应用程序中的这个错误。我们目前正在聊天,我们使用了tableview 在出现一定数量的消息之前,我们的tableview工作正常,之后,表开始翻转。我编码时没有故事板,因此我认为约束是问题的原因。因此,我决定用聊天表视图的一些功能制作一个非常简单的表视图(实际上,我们的表视图是coredata链接的,并且有很多图形) 因为我怀疑约束,所以我在下面的代码中没有使用它,只是为了看到一切正常,但事实并非如此。在gif图像中,我们可以看到两种不受欢迎的行为,第一种是有时表格会完全重新生成,因此单元格会在很短的时间内消失并出现(这会导致非常恼人的翻转)。第二个同样令人恼火:单元是重复的(我认为这是为了单元的可重用性特性),但在很短的一段时间后,它们被容纳了,一切都很好 我尝试添加prepareForReuse()方法并删除视图,然后再次在单元格中创建它们,但没有结果 这是示例代码,您可以在游乐场中复制并运行它,不会出现任何问题Ios 使用简单的TableView Swift构建单元格时的奇怪行为,ios,swift,uitableview,Ios,Swift,Uitableview,我花了一段疯狂的时间来修复我们应用程序中的这个错误。我们目前正在聊天,我们使用了tableview 在出现一定数量的消息之前,我们的tableview工作正常,之后,表开始翻转。我编码时没有故事板,因此我认为约束是问题的原因。因此,我决定用聊天表视图的一些功能制作一个非常简单的表视图(实际上,我们的表视图是coredata链接的,并且有很多图形) 因为我怀疑约束,所以我在下面的代码中没有使用它,只是为了看到一切正常,但事实并非如此。在gif图像中,我们可以看到两种不受欢迎的行为,第一种是有时表格
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class Modelito{
// This is the class tableview model
var celda: String
var valor: String
init(celda: String, valor: String){
self.celda = celda
self.valor = valor
}
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
let tableview: UITableView = {
let table = UITableView()
table.translatesAutoresizingMaskIntoConstraints = false
return table
}()
let button: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Click me to add new cell", for: .normal)
button.setTitle("Click me to add new cell", for: .highlighted)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
var model: [Modelito] = []
let tipoDeCelda = ["MyCustomCell", "MyCustomCell2"]
override func viewDidLoad() {
super.viewDidLoad()
self.setupViews()
self.tableview.register(MyCustomCell.self, forCellReuseIdentifier: "MyCustomCell")
self.tableview.register(MyCustomCell2.self, forCellReuseIdentifier: "MyCustomCell2")
self.tableview.dataSource = self
self.tableview.delegate = self
self.button.addTarget(self, action: #selector(self.addRow), for: .touchUpInside)
// Here I generate semi random info
self.dataGeneration()
}
func setupViews(){
self.view.addSubview(self.tableview)
self.tableview.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
self.tableview.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -50).isActive = true
self.tableview.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 1).isActive = true
self.tableview.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
self.tableview.backgroundColor = .gray
self.view.addSubview(self.button)
self.button.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
self.button.topAnchor.constraint(equalTo: self.tableview.bottomAnchor).isActive = true
self.button.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
self.button.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
self.button.backgroundColor = .orange
}
func dataGeneration(){
let number = 200
// Based in the cell types availables and the senteces, we create random cell info
for _ in 0...number{
self.model.append(
Modelito(celda: tipoDeCelda[Int(arc4random_uniform(UInt32(self.tipoDeCelda.count)))], valor: "\(self.model.count)")
)
}
self.tableview.reloadData()
// After we insert elements in the model we scroll table
let indexPaths: [IndexPath] = [IndexPath(row: self.model.count - 1, section: 0)]
self.tableview.scrollToRow(at: indexPaths[0], at: .bottom, animated: false)
}
@objc func addRow(){
// This function insert a new random element
self.tableview.beginUpdates()
self.model.append(
Modelito(celda: tipoDeCelda[Int(arc4random_uniform(UInt32(self.tipoDeCelda.count)))], valor: "\(self.model.count)")
)
// After inserting the element in the model, we insert it in the tableview
let indexPaths: [IndexPath] = [IndexPath(row: self.model.count - 1, section: 0)]
self.tableview.insertRows(at: indexPaths, with: .none)
self.tableview.endUpdates()
// Finally we scroll to last row
self.tableview.scrollToRow(at: indexPaths[0], at: .bottom, animated: false)
}
}
extension ViewController{
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.model.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 150
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let celldata = self.model[indexPath.row]
switch celldata.celda {
case "MyCustomCell":
let cell = tableview.dequeueReusableCell(withIdentifier: "MyCustomCell", for: indexPath) as! MyCustomCell
cell.myLabel.text = self.model[indexPath.row].valor
return cell
default:
let cell = tableview.dequeueReusableCell(withIdentifier: "MyCustomCell2", for: indexPath) as! MyCustomCell2
cell.myLabel.text = self.model[indexPath.row].valor
return cell
}
}
}
class MyCustomCell: UITableViewCell {
var myLabel = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
myLabel.backgroundColor = UIColor.green
self.contentView.addSubview(myLabel)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
myLabel.frame = CGRect(x: 25, y: 0, width: 370, height: 30)
}
}
class MyCustomCell2: UITableViewCell {
var myLabel = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
myLabel.backgroundColor = UIColor.yellow
self.contentView.addSubview(myLabel)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
myLabel.frame = CGRect(x: 0, y: 0, width: 370, height: 30)
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = ViewController()
提前谢谢
编辑:
我更改了@Scriptable answer中的代码库,以便与playway兼容。在这一点上,我开始认为复制单元错误实际上对于tableview是正常的。要查看问题,应快速按下按钮数次。此代码正在重新加载tableview,但它正在删除“恼人的轻弹”,而且太平滑 要修复这个恼人的flick,只需将addRow()函数更改为
@objc func addRow(){
self.model.append(
Modelito(celda: tipoDeCelda[Int(arc4random_uniform(UInt32(self.tipoDeCelda.count)))], valor: "\(self.model.count)")
)
self.tableview.reloadData()
self.scrollToBottom()
}
scrollToBottom()函数:
func scrollToBottom(){
DispatchQueue.global(qos: .background).async {
let indexPath = IndexPath(row: self.model.count-1, section: 0)
self.tableview.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
试试看。在操场上使用当前代码会给我带来致命错误 2017-11-13 15:10:46.739我的游乐场[4005:234029]***由于未捕获的异常“NSRangeException”而终止应用程序,原因:'-[UITableView _contentOffsetForScrollingToRowatineXPath:atScrollPosition::]:第(200)行超出第(0)节的边界(0)。' 我将
dataGeneration
函数更改为以下代码,在生成数据并滚动到底部后,它只调用reloadData
我认为错误在于没有重新加载,没有那么多行可以滚动
func dataGeneration(){
let number = 200
// Based in the cell types available and the sentences, we create random cell info
for _ in 0...number{
self.model.append(
Modelito(celda: tipoDeCelda[Int(arc4random_uniform(UInt32(self.tipoDeCelda.count)))], valor: "\(self.model.count)")
)
}
self.tableview.reloadData()
// After we insert elements in the model we scroll table
let indexPaths: [IndexPath] = [IndexPath(row: self.model.count - 1, section: 0)]
self.tableview.scrollToRow(at: indexPaths[0], at: .bottom, animated: false)
}
一旦我躲过了撞车事故,桌面视图对我来说很好。没有闪烁或重复。谢谢@scriptable,我在问题中更改了上面的代码,但仍然存在相同的问题。事实上,当你在很短的时间内点击按钮时,问题就出现了(想象你正在聊天,而聊天室的参与者写消息的速度非常快)。我很快地键入了按钮,一分钟内添加了大约30行,没有任何问题。你试过在物理设备上运行吗?模拟器的运行速度比实际设备慢很多