iOS swift 应用程序在滚动到底部时在 TableView 上终止
iOS swift app terminating on TableView when scrolled to bottom
我正在使用 Firebase
在我的 iOS 应用程序中填充 TableView
。前几个对象已加载,但一旦我到达列表中的第三个项目,应用程序就会崩溃,但出现异常:
'NSRangeException', reason: '*** __boundsFail: index 3 beyond bounds [0 .. 2]'
我知道这意味着我在索引处引用了一个数组,但我不知道为什么。
我用 TableViewController
创建了 TableView
并像这样初始化它:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(posts.count)
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let post = posts[indexPath.row]
print(post)
let cell = tableView.dequeueReusableCell(withIdentifier: K.cellIdentifier, for: indexPath) as! PostCell
let firstReference = storageRef.child(post.firstImageUrl)
let secondReference = storageRef.child(post.secondImageUrl)
cell.firstTitle.setTitle(post.firstTitle, for: .normal)
cell.secondTitle.setTitle(post.secondTitle, for: .normal)
cell.firstImageView.sd_setImage(with: firstReference)
cell.secondImageView.sd_setImage(with: secondReference)
// Configure the cell...
return cell
}
我相信第一个函数创建了一个包含 posts
中对象数量的数组,第二个函数将值分配给单元格的模板。第一个方法中的 print 语句打印 4,这是从 firebase 检索到的正确对象数。我假设这意味着创建了一个数组,其中包含 4 个要显示在 TableView
中的对象。这才是真正令人困惑的地方,因为错误指出数组中只有 3 个对象。我是否误解了 TableView
是如何实例化的?
这是填充TableView
的代码:
func loadMessages(){
db.collectionGroup("userPosts")
.addSnapshotListener { (querySnapshot, error) in
self.posts = []
if let e = error{
print("An error occured trying to get documents. \(e)")
}else{
if let snapshotDocuments = querySnapshot?.documents{
for doc in snapshotDocuments{
let data = doc.data()
if let firstImage = data[K.FStore.firstImageField] as? String,
let firstTitle = data[K.FStore.firstTitleField] as? String,
let secondImage = data[K.FStore.secondImageField] as? String,
let secondTitle = data[K.FStore.secondTitleField] as? String{
let post = Post(firstImageUrl: firstImage, secondImageUrl: secondImage, firstTitle: firstTitle, secondTitle: secondTitle)
self.posts.insert(post, at: 0)
print("Posts: ")
print(self.posts.capacity)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
}
}
该应用程序会构建、运行并显示前几项,但一旦我滚动到列表底部就会崩溃。非常感谢任何帮助。
编辑:
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.register(UINib(nibName: K.cellNibName, bundle: nil), forCellReuseIdentifier: K.cellIdentifier)
loadMessages()
}
您收到越界错误,因为您正在危险地填充数据源。您必须记住,table 视图在滚动时不断添加和删除单元格,这使得更新其数据源成为一项敏感任务。您在每次文档迭代时重新加载 table,并在索引 0
处的数据源中插入一个新元素。更新期间的任何滚动都会引发越界错误。
因此,填充一个临时数据源并在准备就绪时将其移交给实际数据源(然后立即重新加载 table,在更改的数据源和活动滚动之间不留任何 space从该数据源获取)。
private var posts = [Post]()
private let q = DispatchQueue(label: "userPosts") // serial queue
private func loadMessages() {
db.collectionGroup("userPosts").addSnapshotListener { [weak self] (snapshot, error) in
self?.q.async { // go into the background (and in serial)
guard let snapshot = snapshot else {
if let error = error {
print(error)
}
return
}
var postsTemp = [Post]() // setup temp collection
for doc in snapshot.documents {
if let firstImage = doc.get(K.FStore.firstImageField) as? String,
let firstTitle = doc.get(K.FStore.firstTitleField) as? String,
let secondImage = doc.get(K.FStore.secondImageField) as? String,
let secondTitle = doc.get(K.FStore.secondTitleField) as? String {
let post = Post(firstImageUrl: firstImage, secondImageUrl: secondImage, firstTitle: firstTitle, secondTitle: secondTitle)
postsTemp.insert(post, at: 0) // populate temp
}
}
DispatchQueue.main.async { // hop back onto the main queue
self?.posts = postsTemp // hand temp off (replace or append)
self?.tableView.reloadData() // reload
}
}
}
}
除此之外,我会在后台处理(主队列上的 Firestore returns),并且仅在数据源被修改时才重新加载 table。
经过一些摆弄和实施@bsod 的响应后,我能够得到我的项目运行。解决方案在 Main.Storyboard
属性检查器下,我必须将内容设置为动态原型。
我正在使用 Firebase
在我的 iOS 应用程序中填充 TableView
。前几个对象已加载,但一旦我到达列表中的第三个项目,应用程序就会崩溃,但出现异常:
'NSRangeException', reason: '*** __boundsFail: index 3 beyond bounds [0 .. 2]'
我知道这意味着我在索引处引用了一个数组,但我不知道为什么。
我用 TableViewController
创建了 TableView
并像这样初始化它:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(posts.count)
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let post = posts[indexPath.row]
print(post)
let cell = tableView.dequeueReusableCell(withIdentifier: K.cellIdentifier, for: indexPath) as! PostCell
let firstReference = storageRef.child(post.firstImageUrl)
let secondReference = storageRef.child(post.secondImageUrl)
cell.firstTitle.setTitle(post.firstTitle, for: .normal)
cell.secondTitle.setTitle(post.secondTitle, for: .normal)
cell.firstImageView.sd_setImage(with: firstReference)
cell.secondImageView.sd_setImage(with: secondReference)
// Configure the cell...
return cell
}
我相信第一个函数创建了一个包含 posts
中对象数量的数组,第二个函数将值分配给单元格的模板。第一个方法中的 print 语句打印 4,这是从 firebase 检索到的正确对象数。我假设这意味着创建了一个数组,其中包含 4 个要显示在 TableView
中的对象。这才是真正令人困惑的地方,因为错误指出数组中只有 3 个对象。我是否误解了 TableView
是如何实例化的?
这是填充TableView
的代码:
func loadMessages(){
db.collectionGroup("userPosts")
.addSnapshotListener { (querySnapshot, error) in
self.posts = []
if let e = error{
print("An error occured trying to get documents. \(e)")
}else{
if let snapshotDocuments = querySnapshot?.documents{
for doc in snapshotDocuments{
let data = doc.data()
if let firstImage = data[K.FStore.firstImageField] as? String,
let firstTitle = data[K.FStore.firstTitleField] as? String,
let secondImage = data[K.FStore.secondImageField] as? String,
let secondTitle = data[K.FStore.secondTitleField] as? String{
let post = Post(firstImageUrl: firstImage, secondImageUrl: secondImage, firstTitle: firstTitle, secondTitle: secondTitle)
self.posts.insert(post, at: 0)
print("Posts: ")
print(self.posts.capacity)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
}
}
该应用程序会构建、运行并显示前几项,但一旦我滚动到列表底部就会崩溃。非常感谢任何帮助。
编辑:
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.register(UINib(nibName: K.cellNibName, bundle: nil), forCellReuseIdentifier: K.cellIdentifier)
loadMessages()
}
您收到越界错误,因为您正在危险地填充数据源。您必须记住,table 视图在滚动时不断添加和删除单元格,这使得更新其数据源成为一项敏感任务。您在每次文档迭代时重新加载 table,并在索引 0
处的数据源中插入一个新元素。更新期间的任何滚动都会引发越界错误。
因此,填充一个临时数据源并在准备就绪时将其移交给实际数据源(然后立即重新加载 table,在更改的数据源和活动滚动之间不留任何 space从该数据源获取)。
private var posts = [Post]()
private let q = DispatchQueue(label: "userPosts") // serial queue
private func loadMessages() {
db.collectionGroup("userPosts").addSnapshotListener { [weak self] (snapshot, error) in
self?.q.async { // go into the background (and in serial)
guard let snapshot = snapshot else {
if let error = error {
print(error)
}
return
}
var postsTemp = [Post]() // setup temp collection
for doc in snapshot.documents {
if let firstImage = doc.get(K.FStore.firstImageField) as? String,
let firstTitle = doc.get(K.FStore.firstTitleField) as? String,
let secondImage = doc.get(K.FStore.secondImageField) as? String,
let secondTitle = doc.get(K.FStore.secondTitleField) as? String {
let post = Post(firstImageUrl: firstImage, secondImageUrl: secondImage, firstTitle: firstTitle, secondTitle: secondTitle)
postsTemp.insert(post, at: 0) // populate temp
}
}
DispatchQueue.main.async { // hop back onto the main queue
self?.posts = postsTemp // hand temp off (replace or append)
self?.tableView.reloadData() // reload
}
}
}
}
除此之外,我会在后台处理(主队列上的 Firestore returns),并且仅在数据源被修改时才重新加载 table。
经过一些摆弄和实施@bsod 的响应后,我能够得到我的项目运行。解决方案在 Main.Storyboard
属性检查器下,我必须将内容设置为动态原型。