问题
I've this weird app crash when pulling to refresh occurs.
My code goes as it follows:
var posts: [Posts] = []
override func viewDidLoad() {
super.viewDidLoad()
// refreshControl -> pull to refresh handler
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self,
action: #selector(Main_TVC.getData),
for: UIControlEvents.valueChanged)
self.refreshControl = refreshControl
getData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",
for: indexPath) as! PostsTableViewCell
cell.titleLabel.text = posts[indexPath.row].postTitle
cell.bodyLabel.text = posts[indexPath.row].postBody
return cell
}
func getData() {
self.posts.removeAll()
// queries the backend and fills the data - sorry for code omission
refresh()
}
func refresh() {
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
The app runs properly and even when I pull down to refresh, everything runs perfectly, but if do a long pull down to refresh, like pulling down almost hitting the bottom of the screen, the app crashes and prompts the following error:
fatal error: Index out of range
on the line
cell.titleLabel.text = posts[indexPath.row].postTitle
If I print the post count and the indexPath as follows:
print("posts.count = (posts.count)") print("indexPath.row = (indexPath.row)")
When I'm pulling down in the normal way, it prints the correct data, but if I pull down like a long pull, through the whole screen if prompts this when it crashes
posts.count = 0
indexPath.row = 2
This kind of thing has never happen to me using the refreshControl the way I'm using here.
Hope my information is understandable, mostly on the long pull to refresh issue.
回答1:
Your problem is that the first thing you do in getData
is remove all of the posts from self.posts
, but you do not (presumably since code is missing) reload the table view, so now the number of posts in the array (0) and the number of posts that the tableview *thinks is in the array (not zero) is different, so you get an array bounds crash.
Calling reloadData
after self.posts.removeAll()
would fix the issue but result in the tableview 'flashing' as it redrew empty and then redrew with the new data.
Since you haven't shown the full code for getData
I can't provide the exact code you need, but it should be something like:
func getData() {
fetchPostsWithCompletion() {
if let tempPosts = dataFromNetwork {
self.posts = tempPosts
} else {
self.posts.removeAll()
}
self.refresh() // Dispatch this on the main queue if your completion handler is not already on the main queue
}
}
This way you don't manipulate the backing array until you have the new data.
回答2:
I just had the same issue. My finding is that delaying the execution of removeAll()
on the posts
array allows the table view to get up to date on its count
.
func getData() {
self.delayExecutionByMilliseconds(500) {
self.posts.removeAll()
}
// queries the backend and fills the data - sorry for code omission
refresh()
}
fileprivate func delayExecutionByMilliseconds(_ delay: Int, for anonFunc: @escaping () -> Void) {
let when = DispatchTime.now() + .milliseconds(delay)
DispatchQueue.main.asyncAfter(deadline: when, execute: anonFunc)
}
来源:https://stackoverflow.com/questions/40698224/fatal-error-index-out-of-range-when-refreshing-table-view