How to hide the cell collectionview in section if json data is nil. im loading collectionview in a tableview cell.my code is belowim showing array of images in collection view i have used third party for horizontalautomatic scroll as third party for collection view
func setCollectionData(_collectionData: [Any]) {
self.collectionData = _collectionData as NSArray
collectionViewObj.setContentOffset(CGPoint(x:0,y:0), animated: true)
for i in 0 ..< collectionData.count {
self.strings = StringClass()
self.strings.vehicleImage = (collectionData[i] as AnyObject).value(forKey: "img_name") as? String
self.strings.vehicleTitle=(collectionData[i] as AnyObject).value(forKey: "p_name") as? String
self.modelArray.append(self.strings)
}
collectionViewObj.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
if self.collectionData.count >= 4 {
return 4
}
else
{
return collectionData.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ProductCollectionViewCustomCell", for: indexPath)as! ProductCollectionViewCustomCell
strings = modelArray[indexPath.row]
cell.ProductTitleLabel.text = strings.vehicleTitle
let URLBaseString = "http://vehiclebuzzzz.com/"
let url = URL(string:URLBaseString .appending(strings.vehicleImage!))
let dataurl = try? Data(contentsOf: url!)
cell.productImageViewObj.image=UIImage(data: dataurl!)
return cell
}
func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout,sizeForItemAt indexPath: IndexPath) -> CGSize
override this method and return zero size CGSize.zero if the json data is nil else return actual size of cell
OR
remove nil data from array and call collectionView.reloadData()
Update the collectionData array with the new data & then perform collectionViewObj.reloadData()
-If you does not want show collection view. just hide the collection view until you get the data.
-When you data make it visible
Related
I've been working with Swift and iOS for several months now. I want to download a new image when it comes to CollectionView's penultimate item. I'm downloading, but the reloadData function is constantly redrawing and collectionview is top. How can I prevent it from going to the top?
private func getRandomPhoto() {
let url = "https://source.unsplash.com/random/400x800"
do{
var a = try Data(contentsOf: URL(string: url)!)
images.append(UIImage(data: a)!)
} catch {
print("err")
}
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
and here is my controller view codes
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ImageCollectionViewCell
cell.imageView.image = images[indexPath.row]
return cell
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
for cell in collectionView.visibleCells {
let indexPathh = collectionView.indexPath(for: cell)
if indexPathh?.row == images.count-1 {
let url = "https://source.unsplash.com/random/400x800"
getRandomPhoto()
}
}
}
}
I made a small video for the problem. I hope it's clear.
https://github.com/Egemenclr/StajCase/blob/master/Örnek/problem.gif
Don't reload the whole table, instead insert rows for newly downloaded images
insertItems(at indexPaths: [IndexPath])
suggestion: Don't download the same image only again and again. Download initially and cache it with NSCache. Try to fetch from cache when you need the image again.
currently I have this loadCharacters function
var characterArray = [Character]()
func loadCharacters(with request: NSFetchRequest<Character> = Character.fetchRequest()) {
do {
characterArray = try context.fetch(request)
} catch {
print("error loading data")
}
collectionView.reloadData()
}
My question is: How can I pass the fetched Data from there to my subclass CharacterCollectionViewCell and later on use this cell for my
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)
-> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "characterCell",
for: indexPath) as! CharacterCollectionViewCell {
...
}
any suggestion or any better way to make it works are really appreciated!!
You only need to get the characterArray element corresponding to the indexPath.item and use it to pass it into the cell.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)
-> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "characterCell",
for: indexPath) as! CharacterCollectionViewCell {
cell.someLabel.text = characterArray[indexPath.item]
//...
return cell
}
If you have too much data to be passed on it's better to create a model and use it's instance to pass data on to the cell. So, for that firstly create a model.
struct CharacterCellModel { // all properties... }
Then in your UIViewController sub-class.
var characterCellModels = [CharacterCellModel]() // append this model
And finally in cellForItemAt:
cell.characterCellModel = characterCellModels[indexPath.item]
I want to show cells in collection view like in circular list, means that after the last cell of the collection view, on scrolling the collection view shows the first cell again, like circular linklist
I have tried using icrousel, but as icarosuel deals with views only, I don't want to finish the collection view completely and start again with icarousel, so is there any way I can make me collection view circular
this is my collectionView code
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell =
collectionView.dequeueReusableCell(withReuseIdentifier:
"CellName", for: indexPath as IndexPath) as! CellName
let gigModel = self.datasoruce?[indexPath.row]
cell.lblTitle.text = gigModel?.title
cell.btnPrice.setTitle(gigModel?.getPriceAccordingToGigType(),
for: .normal)
cell.itemImageView.sd_setImage(with: URL(string:
(gigModel?.getPhotoPath())!), placeholderImage:
UIImage.init(named: "place_holder"))
cell.itemImageView.layer.cornerRadius = 10.0
if Utilities.isValidString(object: gigModel?.adminId as
AnyObject) {
cell.btnStar.isHidden = false
}
else {
cell.btnStar.isHidden = true
}
return cell
}
and I want this to be circular list.
I tried to create sample project and it was pretty simple, here is example code how you can implement "infinite" scroll
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var array: [Any] = [0,1,2,3,4,5,6,7,8,9]
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Int(Int16.max) // Int.max cause crash
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCell
let correctIndex = indexPath.row >= array.count ? indexPath.row % array.count : indexPath.row
cell.nameLabel.text = "\(array[correctIndex])"
return cell
}
}
Hope it will help you
//Use Below code to get next cell.
func scrollToNextCell(){
//get Collection View Instance
let collectionView:UICollectionView;
//get cell size
let cellSize = CGSizeMake(self.view.frame.width, self.view.frame.height);
//get current content Offset of the Collection view
let contentOffset = collectionView.contentOffset;
//scroll to next cell
collectionView.scrollRectToVisible(CGRectMake(contentOffset.x + cellSize.width, contentOffset.y, cellSize.width, cellSize.height), animated: true);
}
I have an array (4 items) of type Venue each contains an array (2 items) of imgURLs String and I am getting an ERROR: Index out of range at the following line of my collectionView cellForItemAt. I am trying to display the images for each Venue in a collectionView within the tableview cell.
let urlString = filteredVenueArray[collectionView.tag].imgURLs[collectionView.tag]
cellForItemAt function:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "VenueListingCellImage", for: indexPath) as! VenueListingCellImage
let urlString = filteredVenueArray[collectionView.tag].imgURLs[collectionView.tag]
let imgURL = URL(string: urlString)
cell.configureCell(url: imgURL!)
return cell
}
EDIT: numberOfItemsInSection function
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return filteredVenueArray[collectionView.tag].imgURLs.count
}
Change this line
let urlString = filteredVenueArray[collectionView.tag].imgURLs[collectionView.tag]
To
let urlString = filteredVenueArray[collectionView.tag].imgURLs[indexPath.item]
In your code replace:
let urlString = filteredVenueArray[collectionView.tag].imgURLs[collectionView.tag]
with:
let urlString = filteredVenueArray[collectionView.tag].imgURLs[indexPath.item]
CollectionView cellForItemAt indexPath iterates your array through indexPath, so to access each item you should use indexPath.item
Here is the problem, you should use,
let urlString = filteredVenueArray[collectionView.tag].imgURLs[indexPath.item]
I have a UIImageView and I want to append there images coming from a remote URL
Up to now it works fine with only one image.
I just set cell.related.image to the new image and I return always 1 to the collectionView
How can I switch to appending an array of images?
Is there a way to do the appending in a function?
Do I need always to do the appending inside collectionView ?
I want to do it inside an event listener in the future
var img_uls = ["url1" , "url2", "url3"]
func append_image(_ path: String, _ cell: RelatedCollectionViewCell) {
let url = URL(string: path)
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
DispatchQueue.main.async {
cell.related.image = UIImage(data: data!)
}
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return img_uls.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RelatedCollectionViewCell", for: indexPath) as! RelatedCollectionViewCell
append_image(img_uls[indexPath.row], cell);
return cell
}
Just have the array of String which contains Url of your image and pass it as in numberOfItems Method like this
var img_uls = ["url1" , "url2", "url3"]
//array of strings containing urls
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return img_uls.count
}
Now you have to iterate your array img_uls, So that you can fetch Url from array and fetch the images and set in collectionView cell. So, do it like this
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RelatedCollectionViewCell", for: indexPath) as! RelatedCollectionViewCell
append_image(img_uls[indexPath.row], cell);
// indexPath.row starts from (0) and goes to (img_uls.count - 1)
return cell
}
Now you want to append the image into images array
var images = NSMutableData()
func append_image(_ path: String, _ cell: RelatedCollectionViewCell) {
let url = URL(string: path)
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
DispatchQueue.main.async {
images.appendData(data)
cell.related.image = UIImage(data: data!)
}
}
}
Instead of using your own append method, and handling that append and all, you can use Kingfisher or Haenke. They are lightweight and moreover manages caching. And hence your scrolling will be fast.
Create a imageUrlArray variable of String which will hold the remote image URLs, then return the count of the array from your numberOfItemsInSection method. In collectionView:cellForItemAt function get the Url to display from the array using imageArray[indexPath.row] and pass the Url to the append_image(_ path: String, _ cell: RelatedCollectionViewCell) function. Better use third party image loading library like KingFisher or Nuke to better handle Asynchronous image downloading and caching.
class Example1: UIViewController, UICollectionViewDataSource {
var imageArray: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
}
func updateImages(_ images: [String]) {
self.imageArray = images
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RelatedCollectionViewCell", for: indexPath) as! RelatedCollectionViewCell
//append_image(imageArray[indexPath.row], cell)
//Using Kingfisher
let url = imageArray[indexPath.row]
cell.imageView.kf.setImage(with: url, placeholder: nil, options: [.transition(.fade(1))])
return cell
}
}