Question about loading collectionView cell repeatedly in Swift - ios

I want to load collectionView Cell repeatedly.
I want to make a dynamic cell, and when 20 cells are loaded, I want to make 40 cells by loading 20 underneath again.
API.requestBookCategory(bookCategory: 9, bookAddPoint: 0, completionHandler: handleBooksCategory(books:error:))
func handleBooksCategory(books: Books?, error: Error?) {
self.booksCategory = books
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
This code retrieves information from 20 books in bookCategory 9.
When importing 1 to 20 book information, insert bookAddPoint 0.
After that, we insert book information into the bookCatagory variable and use bookCategory to display the book information data in the cell.
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return booksCategory?.books.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView
.dequeueReusableCell(withReuseIdentifier: "MainBookCell", for: indexPath) as! MainBookCell
func handleImageResponse() {
guard let imageURL = URL(string: booksCategory?.books[indexPath.row].bookImage ?? "") else {
return
}
API.requestImageFile(url: imageURL, completionHandler: handleImageFileResponse(image:error:))
}
func handleImageFileResponse(image: UIImage?, error: Error?) {
DispatchQueue.main.async {
cell.bookImageView.image = image
}
}
cell.bookTitleLabel.text = booksCategory?.books[indexPath.row].bookTitle
cell.bookWriterLabel.text = booksCategory?.books[indexPath.row].authorName
handleImageResponse()
return cell
}
}
I go through the process up to here, and a cell displaying 20 book information is created.
If a cell showing the 20th book information is created,
API.requestBookCategory (bookCategory: 9, bookAddPoint: 20, completionHandler: handleBooksCategory (books: error :))
I want to run a code that modifies bookAddPoint from 0 to 20, like the code above, to create a cell that displays the 21st to 40th book information. What should I do?

in viewControoller create a variables
var bookAddPoint = 0
var pageLength = 20
func callNextPageData(){
bookAddPoint = booksCategory?.books.count + 1
API.requestBookCategory(bookCategory: 9, bookAddPoint: bookAddPoint, completionHandler: handleBooksCategory(books:error:))
}
change
func handleBooksCategory(books: Books?, error: Error?) {
for book in books{
self.booksCategory.appen(book)
}
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
Change cellforItem
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView
.dequeueReusableCell(withReuseIdentifier: "MainBookCell", for: indexPath) as! MainBookCell
func handleImageResponse() {
guard let imageURL = URL(string: booksCategory?.books[indexPath.row].bookImage ?? "") else {
return
}
API.requestImageFile(url: imageURL, completionHandler: handleImageFileResponse(image:error:))
}
func handleImageFileResponse(image: UIImage?, error: Error?) {
DispatchQueue.main.async {
cell.bookImageView.image = image
}
}
cell.bookTitleLabel.text = booksCategory?.books[indexPath.row].bookTitle
cell.bookWriterLabel.text = booksCategory?.books[indexPath.row].authorName
handleImageResponse()
if indexPath.row == self.booksCategory.count-3 // it will load records before you reach end point automatically you can increase or decrease number
{
callNextPageData()
}
return cell
}
NOTE: if you want to do this on load more button just call callNextPageData() in button action or in tableView footer actions.

Related

Image in Collection view cell is not updated when the image is downloaded asynchronously

The image in the collection view cell is not updated when the image is downloaded from the server. The image gets updated when the collection view is scrolled.
Every section of the table view has a collection view. And table view cell has datasource for the collection view.
extension OffersCell: UICollectionViewDataSource,UICollectionViewDelegate{
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return photoViewModel.photos.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath)
(cell as! PhotoCell).imageView.contentMode = .scaleAspectFill
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let photo = self.photoViewModel.photos[indexPath.row]
(cell as! PhotoCell).imageView.image = UIImage(named: "dummyImage")
ImageDownloadManager.shared.downloadImage(photo, indexPath: indexPath) { (image, imageIndexPath, error) in
if let indexPathNew = imageIndexPath, indexPathNew == indexPath {
DispatchQueue.main.async {
(cell as! PhotoCell).imageView.image = image
}
}
}
}
}
Please find the image downloader class :
typealias ImageDownloadHandler = (_ image: UIImage?, _ indexPath: IndexPath?, _ error: Error?) -> Void
final class ImageDownloadManager {
private var completionHandler: ImageDownloadHandler?
lazy var imageDownloadQueue: OperationQueue = {
var queue = OperationQueue()
queue.name = "imageDownloadQueue"
queue.qualityOfService = .userInteractive
return queue
}()
let imageCache = NSCache<NSString, UIImage>()
static let shared = ImageDownloadManager()
private init () {}
func downloadImage(_ photo: Photos, indexPath: IndexPath?, handler: #escaping ImageDownloadHandler) {
self.completionHandler = handler
guard let url = photo.getImageURL() else {
return
}
if let cachedImage = imageCache.object(forKey: photo.id as NSString) {
self.completionHandler?(cachedImage, indexPath, nil)
} else {
let operation = CustomOperation(url: url, indexPath: indexPath)
if indexPath == nil {
}
operation.queuePriority = .high
operation.downloadHandler = { (image, indexPath, error) in
if let newImage = image {
self.imageCache.setObject(newImage, forKey: photo.id as NSString)
}
self.completionHandler?(image, indexPath, error)
}
imageDownloadQueue.addOperation(operation)
}
}
func cancelAll() {
imageDownloadQueue.cancelAllOperations()
}
}
After you downloaded the image, you execute the instruction (cell as! PhotoCell).imageView.image = image on the main thread. But this does not redisplay your collectionView cell.
Also, collectionView:willDisplayCell:forItemAtIndexPath: will normally not be called. The docs say
The collection view calls this method before adding a cell to its
content.
It is however called, when you scroll in the cell, i.e. when it becomes visible. This is there reason why your image is displayed after the cell is scrolled in.
So my suggestion is:
After downloading the image, update your collectionView data source
so that collectionView:cellForItemAtIndexPath: can configure the cell
with the image.
Call reloadItems(at:) with an array that contains only the index path of the updated cell.
It depends on how you define the class CustomOperation, but the problem seems to be in the method downloadImage of ImageDownloadManager where in the next line you set self.completionHandler = handler. Note that ImageDownloadManager is a singleton. This means that every operation you start replaces completionHandler of the singleton object with the new completion (I bet only the last cell was refreshed). The solution consists of elimination the property completionHandler and replacing the operation download handler with this
operation.downloadHandler = { (image, indexPath, error) in
if let newImage = image {
self.imageCache.setObject(newImage, forKey: photo.id as NSString)
}
handler(image, indexPath, error)
}
Note that it calls the handler of the context and not the stored property of the download manager
Here is a full working example with all the class and struct definitions. Adapt it as needed.
typealias ImageDownloadHandler = (_ image: UIImage?, _ indexPath: IndexPath?, _ error: Error?) -> Void
enum ImageDownloadError: Error {
case badDataURL
}
class CustomOperation: Operation {
var downloadHandler: (UIImage?, IndexPath?, Error?) -> () = { _,_,_ in }
private let url: URL
private let indexPath: IndexPath?
init(url: URL, indexPath: IndexPath?) {
self.url = url
self.indexPath = indexPath
}
override func main() {
guard let imageData = try? Data(contentsOf: self.url) else {
self.downloadHandler(nil, self.indexPath, ImageDownloadError.badDataURL)
return
}
let image = UIImage(data: imageData)
self.downloadHandler(image, self.indexPath, nil)
}
}
final class ImageDownloadManager {
private var completionHandler: ImageDownloadHandler?
lazy var imageDownloadQueue: OperationQueue = {
var queue = OperationQueue()
queue.name = "imageDownloadQueue"
queue.qualityOfService = .userInteractive
return queue
}()
let imageCache = NSCache<NSString, UIImage>()
static let shared = ImageDownloadManager()
private init () {}
func downloadImage(_ photo: Photos, indexPath: IndexPath?, handler: #escaping ImageDownloadHandler) {
//self.completionHandler = handler
guard let url = photo.getImageURL() else {
return
}
if let cachedImage = imageCache.object(forKey: photo.id as NSString) {
//self.completionHandler?(cachedImage, indexPath, nil)
handler(cachedImage, indexPath, nil)
} else {
let operation = CustomOperation(url: url, indexPath: indexPath)
if indexPath == nil {
}
operation.queuePriority = .high
operation.downloadHandler = { (image, indexPath, error) in
if let newImage = image {
self.imageCache.setObject(newImage, forKey: photo.id as NSString)
}
//self.completionHandler?(image, indexPath, error)
handler(image, indexPath, error)
}
imageDownloadQueue.addOperation(operation)
}
}
func cancelAll() {
imageDownloadQueue.cancelAllOperations()
}
}
-------------------------------------------------------
struct Photos {
let id: String
let url: URL
func getImageURL() -> URL? {
return self.url
}
}
struct PhotoViewModel {
let photos: [Photos]
}
class PhotoCell: UICollectionViewCell {
#IBOutlet weak var imageView: UIImageView!
}
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
private let photoViewModel: PhotoViewModel = PhotoViewModel(
photos: [
Photos(
id: "kitty1",
url: URL(
string: "https://cdn.pixabay.com/photo/2019/06/18/11/23/cat-4282110_960_720.jpg"
)!
),
Photos(
id: "kitty2",
url: URL(
string: "https://cdn.pixabay.com/photo/2019/07/23/20/08/cat-4358536_960_720.jpg"
)!
),
Photos(
id: "kitty3",
url: URL(
string: "https://cdn.pixabay.com/photo/2016/09/28/13/15/kittens-1700474_960_720.jpg"
)!
)
]
)
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.reloadData()
}
}
extension ViewController: UICollectionViewDataSource,UICollectionViewDelegate{
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return photoViewModel.photos.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath)
(cell as! PhotoCell).imageView.contentMode = .scaleAspectFill
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let photo = self.photoViewModel.photos[indexPath.row]
(cell as! PhotoCell).imageView.image = UIImage(named: "dummyImage")
ImageDownloadManager.shared.downloadImage(photo, indexPath: indexPath) { (image, imageIndexPath, error) in
if let indexPathNew = imageIndexPath, indexPathNew == indexPath {
DispatchQueue.main.async {
(cell as! PhotoCell).imageView.image = image
}
}
}
}
}
Yes, once image is downloaded is will not display unless collection view is scrolled as said by #Reinhard Männer
Instead you can go for the third-party SDKs(which fit your needs) for image downloading and caching in your app.
I will recommend to use Kingfisher SDK (developed in pure swift).
It is easy to use and integrate. it does lot of thing like async. downloading, caching(on memory or disk), built-in transition animation when setting images, etc. and it is popular too
For you'r problem it is one line code if you use Kingfisher SDK.
For eg.
To load image asynchronously you can use following in cellForRowAtItem: method.
let url = URL(string: "https://example.com/image.png")
imageView.kf.setImage(with: url)
What you all need to do is...
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath) as! PhotoCell
cell.imageView.contentMode = .scaleAspectFill
//I'm assuming photo is URL(in string) of Photo. if 'photo' is URL type then you can pass it directly in 'setImage' method.
let photo = self.photoViewModel.photos[indexPath.row]
let imgUrl = URL(string: photo)
//It will download image asynchronously and cache it for later use. If the image is failed to downloaded due to some issue then "dummyImage" will be set in image view.
cell.imageView.kf.setImage(with: imgUrl, placeholder: UIImage(named: "dummyImage"))
return cell
}
Here you can remove cell willDisplay: method.

Smooth animation in UICollectionView when changing data source

I have a UISegmentControl that I use to switch the datasource for a UICollectionView. The datasources are different types of objects.
For example the objects might look like this
struct Student {
let name: String
let year: String
...
}
struct Teacher {
let name: String
let department: String
...
}
And in the view that contains the CollectionView, there would be code like this:
var students = [Student]()
var teachers = [Teachers]()
... // populate these with data via an API
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if(segmentControl.titleForSegment(at: segmentControl.selectedSegmentIndex) == "Students") {
return students?.count ?? 0
} else {
return teachers?.count ?? 0
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "personCell", for: indexPath) as! PersonCell
if(segmentControl.titleForSegment(at: segmentControl.selectedSegmentIndex)! == "Students") {
cell.title = students[indexPath.row].name
cell.subtitle = students[indexPath.row].year
} else {
cell.title = teachers[indexPath.row].name
cell.subtitle = teachers[indexPath.row].subject
}
return cell
}
#IBAction func segmentChanged(_ sender: AnyObject) {
collectionView.reloadData()
}
This correctly switches between the two datasources, however it does not animate the change. I tried this:
self.collectionView.performBatchUpdates({
let indexSet = IndexSet(integersIn: 0...0)
self.collectionView.reloadSections(indexSet)
}, completion: nil)
But this just crashes (I think this is because performBatchUpdates gets confused about what to remove and what to add).
Is there any easy way to make this work, without having a separate array storing the current items in the collectionView, or is that the only way to make this work smoothly?
Many thanks in advance!
If your Cell's UI just look the same from different datasource, you can abstract a ViewModel upon your datasource, like this:
struct CellViewModel {
let title: String
let subTitle: String
...
}
Then every time you got data from an API, generate ViewModel dynamically
var students = [Student]()
var teachers = [Teachers]()
... // populate these with data via an API
var viewModel = [CellViewModel]()
... // populate it from data above by checking currently selected segmentBarItem
if(segmentControl.titleForSegment(at: segmentControl.selectedSegmentIndex)! == "Students") {
viewModel = generateViewModelFrom(students)
} else {
viewModel = generateViewModelFrom(teachers)
}
So you always keep one datasource array with your UICollectionView.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return viewModel?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "personCell", for: indexPath) as! PersonCell
cell.title = viewModel[indexPath.row].title
cell.subtitle = viewModel[indexPath.row].subTitle
return cell
}
#IBAction func segmentChanged(_ sender: AnyObject) {
collectionView.reloadData()
}
Then try your performBatchUpdates:
self.collectionView.performBatchUpdates({
let indexSet = IndexSet(integersIn: 0...0)
self.collectionView.reloadSections(indexSet)
}, completion: nil)

didSelectItem not being called

My didSelectItemAt method is not being called and nothing is being printed into the console. I have user interaction turned on and I still can not get it to print out anything. I am not sure if my custom PinterestStyle Layout is causing this or if I am missing something. The ultimate goal would be to segue into a detail view controller showing the profile page of the cell selected. I will do that using prepareForSegue however I still can't even get it to print out the name of the cell when tapped.
class PagesCollectionViewController: UICollectionViewController, firebaseHelperDelegate {
var storageRef: StorageReference!{
return Storage.storage().reference()
}
var usersList = [String]()
var authService : FirebaseHelper!
var userArray : [Users] = []
var images: [UIImage] = []
var names: [String] = []
override func viewWillAppear(_ animated: Bool) {
if Global.Location != "" && Global.Location != nil
{
usersList = Global.usersListSent
print(usersList)
self.authService.ListOfUserByLocation(locationName: Global.Location, type: .ListByLocation)
}
}
override func viewDidLoad() {
self.collectionView?.allowsSelection = true
self.collectionView?.isUserInteractionEnabled = true
super.viewDidLoad()
self.authService = FirebaseHelper(viewController: self)
self.authService.delegate = self
setupCollectionViewInsets()
setupLayout()
}
private func setupCollectionViewInsets() {
collectionView!.backgroundColor = .white
collectionView!.contentInset = UIEdgeInsets(
top: 20,
left: 5,
bottom: 49,
right: 5
)
}
private func setupLayout() {
let layout: PinterestLayout = {
if let layout = collectionViewLayout as? PinterestLayout {
return layout
}
let layout = PinterestLayout()
collectionView?.collectionViewLayout = layout
return layout
}()
layout.delegate = self
layout.cellPadding = 5
layout.numberOfColumns = 2
}
func firebaseCallCompleted(data: AnyObject?, isSuccess: Bool, error: Error?, type: FirebaseCallType) {
if(type == .ListByLocation) {
if(isSuccess) {
self.userArray.removeAll()
self.images.removeAll()
self.images.removeAll()
if(data != nil) {
let dataDict = data as! NSDictionary
let keyArray = dataDict.allKeys
for i in 0 ..< keyArray.count {
var dict = NSDictionary()
dict = dataDict.object(forKey: keyArray[i]) as! NSDictionary
self.userArray.append(Users.init(data: dict))
}
}
self.collectionView?.reloadData()
}
else {
print(error?.localizedDescription)
SVProgressHUD.dismiss()
}
}
}
}
extension PagesCollectionViewController {
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return userArray.count
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(userArray[indexPath.row].name)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "PagesCollectionViewCell",
for: indexPath) as! PagesCollectionViewCell
cell.nameLabel.text = userArray[indexPath.row].name
if let imageOld = URL(string: userArray[indexPath.row].photoURL){
cell.photo.sd_setImage(
with: imageOld,
placeholderImage: nil,
options: [.continueInBackground, .progressiveDownload]
)
}
return cell
}
}
extension PagesCollectionViewController : PinterestLayoutDelegate {
func collectionView(collectionView: UICollectionView,
heightForImageAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat {
var image: UIImage?
let url = URL(string: userArray[indexPath.row].photoURL)
let data = try? Data(contentsOf: url!)
image = UIImage(data: data!)
return (image?.height(forWidth: withWidth))!
}
func collectionView(collectionView: UICollectionView,
heightForAnnotationAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat {
return 30
}
}
Check 2 conditions:-
Make sure you have set delegate to UICollectionView
Make sure Content in PageCollectionCell like image having no user interaction enabled. If image user interaction is enabled then didSelectItemAt will not call.
as Manish Mahajan said a quick fix would be:
in cellForItemAt func set contentView as not clickable
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.contentView.isUserInteractionEnabled = false
return cell
}

Fatal error: Index out of range when downloading data from the Internet

There is a function that updates my UICollectionView:
#objc func refreshFeed(sender:AnyObject)
{
fetchWeatherData()
}
private func fetchWeatherData() {
PostArray = [PostModel]()
offset = 0
self.fetchPost()
self.refreshControl.endRefreshing()
}
This function causes the PostArray collection to be nullified and calls the fetchPost ribbon update function:
func fetchPost(URL: String){
ApiService.sharedInstance.fetchPost(url: URL, completion: { (Posts: [PostModel]) in
self.PostArray.append(contentsOf: Posts)
self.collectionView.reloadData()
})
}
After updating the collection, the function:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! PostCell
cell.PostArray = PostArray[indexPath.item]
return cell
}
Gives an error message: Thread 1: Fatal error: Index out of range in line: cell.PostArray = PostArray[indexPath.item]
Why is this happening? The data falls into the collection.
Sorry for my English
At first, check that func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int returns valid count of items.
Than you can check if PostArray contains item at given index:
if PostArray.indices ~= indexPath.item {
cell.postArray = PostArray[indexPath.item]
}
And you need to make sure that ApiService.sharedInstance.fetchPost runs completion on main queue. If not, try this:
func fetchPost(URL: String){
ApiService.sharedInstance.fetchPost(url: URL, completion: { (Posts: [PostModel]) in
self.PostArray.append(contentsOf: Posts)
DispatchQueue.main.async {
self.collectionView.reloadData()
}
})
}

Load more activity cell CollectionViewCell

I'm trying to create a activityCell, so when the user reach the button it will show an cell with an activity indicator. This seem to work fine however if moreDataAvailable is false it should remove this cell. However i keep getting following error?
'NSInternalInconsistencyException', reason: 'attempt to delete item 0 from section 1 which only contains 0 items before the update'
numberOfItemsInSection
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
if section == 0 {
return organizationArray.count
} else {
if self.moreDataAvailable == true {
return 1
} else {
return 0
}
}
}
Hide Collection Cell
func hideCollectionViewFooter() {
self.collectionView!.deleteItemsAtIndexPaths([NSIndexPath(forRow: 0, inSection: 1)])
}
numberOfSectionsInCollectionView
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 2
}
cellForItemAtIndexPath
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
if indexPath.section == 0 {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("OrganizationCell", forIndexPath: indexPath) as! OrganizationCollectionViewCell
cell.customerLabel?.text = organizationArray[indexPath.item].name.uppercaseString
cache.fetch(key: organizationArray[indexPath.item].coverPhoto).onSuccess { data in
cell.customerImageView?.image = UIImage(data: data)
}
return cell
} else {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ActivityCell", forIndexPath: indexPath) as UICollectionViewCell
return cell
}
}
Load More when reach bottom
override func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) {
if !loadingData && indexPath.item == organizationArray.count - 1 && self.moreDataAvailable {
self.loadingData = true
proposeAccess(false, success: {
self.loadingData = false
})
}
}
Update Organization and check if more data is available
func updateOrganizations(refresh: Bool) {
let realm = try! Realm()
GetOrganization.request(String(self.lastLoadedPage), limit: String(limit), location: self.lastLocation!, radius: String(100), refresh: refresh,
success: { numberOfResults in
//Sort by distance
self.organizationArray = GetOrganization.sortOrganizationsByDistanceFromLocation(realm.objects(Organization), location: self.lastLocation!)
self.lastLoadedPage = self.lastLoadedPage + 1
if numberOfResults < self.limit {
//Hide FooterView
self.moreDataAvailable = false
self.hideCollectionViewFooter()
}
}, error: {
self.organizationArray = GetOrganization.sortOrganizationsByDistanceFromLocation(realm.objects(Organization), location: self.lastLocation!)
print("error")
})
}
This error means that you're trying to delete cell that not existed in current table view state. Probably moreDataAvailable already was false before request in updateOrganizations was finished.
I would recommend you using table footer view for displaying activity indicator. Also, after data is loaded you can display a number of loaded items.

Resources