Issue with cells flicker when loading UICollectionViewCell Swift 4 - ios

I am currently having an issue with UICollectionViewCell and when it gets loaded.
Here is the link to the video to see it in action
Below is my code in viewDidLoad i call
retrieveUrls { (success) in
if success {
self.filterImageLabel(handler: { (success) in
if success {
if self.spinner != nil {
self.spinner.stopAnimating()
self.spinner.isHidden = true
}
self.collectionView.reloadData()
}
})
}
}
In retrieveUrls i am parsing the Url to retrieve image URL and for filterImageLabel i set up an photoUrlArray to use with collectionView indexpath
func filterImageLabel(handler: #escaping (_ status: Bool) -> ()) {
photoUrlArray = photoUrlArray.filter {$0.label == "Large Square"}
if photoUrlArray.count > 0 {
handler(true)
}
}
Methods for CollectionView
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return photoUrlArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath) as? PhotoCell {
cell.updateCellImage(imageUrl: self.photoUrlArray[indexPath.row].source)
return cell
}
return PhotoCell()
}
And Finally in photocell class i am setting up the image
override func prepareForReuse() {
super.prepareForReuse()
photoImg.image = nil
}
func updateCellImage(imageUrl : String) {
Alamofire.request(imageUrl).responseImage(completionHandler: { (response) in
guard let image = response.result.value else {return}
self.photoImg.image = image
})
}
I have looked at various different thread on stack-overflow. however cannot seem to resolve the issue.
Any ideas would be helpful.

Method for CollectionView is as follows:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath) as? PhotoCell {
// ********* ISSUE IS HERE *********
let imageurl = URL(string: self.photoUrlArray[indexPath.row].source)
cell.photoImg.af_setImage(withURL: imageurl!)
return cell
}
return PhotoCell()
}

Related

want to add a collectionView inside tableView by downloading data from firebase to show . ios App Swift

want to add a UICollectionView inside UITableView by downloading data from firebase to show
Now I'm done with the interface part, but I'm stuck in the problem, can't bring data from firebase to show in the UICollectionView.
I can't run collectionView.reloadData() because the UICollectionView is different in class, how should I fix it?
func showImageRewardData(rewardID:String) {
let databaseRef = Database.database().reference().child("reward").child(rewardID).child("rewardImage")
databaseRef.observe(DataEventType.value, with: { (Snapshot) in
if Snapshot.childrenCount>0{
self.rewardDataArr.removeAll()
for rewardImage in Snapshot.children.allObjects as! [DataSnapshot]{
let rewardObject = rewardImage.value as? [String: AnyObject]
if(rewardObject?["imageURL"] != nil){
let imageUrl = rewardObject?["imageURL"]
let Data = rewardDetailClass(rewardImage: imageUrl as? String)
self.rewardDataArr.insert(Data, at: 0)
}
YOURVIEWCONTROLLER.SWIFT
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let CellIdentifier: String = "Cell_NotesList"
var cell: Cell_NotesList? = (tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as? Cell_NotesList)
if cell == nil {
let topLevelObjects: [Any] = Bundle.main.loadNibNamed("Cell_NotesList", owner: nil, options: nil)!
cell = (topLevelObjects[0] as? Cell_NotesList)
cell?.selectionStyle = .none
}
cell?.reloadCollectionView(arr: arrofofthumbImages)
return cell!
}
YourCell.SWIFT
class Cell_NotesList: UITableViewCell {
var imagesArr = NSMutableArray()
override func awakeFromNib() {
collectionView.register(UINib.init(nibName: "cell_ImageCollection", bundle: nil), forCellWithReuseIdentifier: "cell_ImageCollection")
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
extension Cell_NotesList : UICollectionViewDataSource {
func reloadCollectionView(arr:NSMutableArray) {
imagesArr = arr
collectionView.reloadData()
self.layoutIfNeeded()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imagesArr.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell : cell_ImageCollection? = collectionView.dequeueReusableCell(withReuseIdentifier: "cell_ImageCollection", for: indexPath) as? cell_ImageCollection
//YOUR CODE HERE
return cell!
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
}
extension Cell_NotesList : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 120.0, height: 120.0)
}
}
extension UICollectionViewCell {
var indexPath: IndexPath? {
return (superview as? UICollectionView)?.indexPath(for: self)
}
}
In class that extends UICollectionView, put one setter method like
func setData(data: String){
// Update your cell view here like
Label.text = data
}

Pass data from UITableView to UICollectionView

I want to get a JSON response and display data in my viewcontroller.
I have this JSON response:
{"status":1,"data":{"blocks":[{"name":"CustomBlock","description":"CustomDescription","items":[1]}], "items":[{"id:"1", name: "testgame"}]}
I have blocks with name, description, and array of items. Also, all items is passing here with the key "items"
I have created this tableview class
BlocksTableView.swift
class BlocksTableView : UITableView, UITableViewDataSource,
UITableViewDelegate
{
var blocks = [Block]()
var items : BlockItem!
override func awakeFromNib() {
self.delegate = self
self.dataSource = self
self.loadBlocks()
}
func loadBlocks()
{
guard let url = URL(string : "myURL") else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let response = try JSONDecoder().decode(APIResponse.self, from: data)
self.blocks = response.data.blocks;
self.items = response.data.items;
DispatchQueue.main.async {
self.reloadData()
}
} catch let jsonErr {
print(jsonErr)
}
}.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return blocks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "BlockCell") as? BlockCell else { return UITableViewCell() }
cell.blockName.text = blocks[indexPath.row].name
cell.blockDescription.text = blocks[indexPath.row].description
cell.gameCollectionView.reloadData()
return cell
}
}
Now I want to display items in collectionview inside tableviewcell, but I have no idea how to do this. Since each block has different count of items, I need to pass blocks and items variables to my CollectionViewClass, right? But how to do this properly?
Here is my collectionviewclass
class GameCollectionView : UICollectionView,
UICollectionViewDataSource, UICollectionViewDelegate
{
override func awakeFromNib() {
self.delegate = self
self.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//here i need to return number of items in current block, something like blocks[0].items.count
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "GameCollectionCell", for: indexPath) as? GameCollectionCell else { return
UICollectionViewCell()
}
return cell
}
You can try
class BlocksTableView : UITableView, UITableViewDataSource,UITableViewDelegate,UICollectionViewDataSource, UICollectionViewDelegate {
var blocks = [Block]()
var items : BlockItem!
override func awakeFromNib() {
self.delegate = self
self.dataSource = self
self.loadBlocks()
}
func loadBlocks()
{
guard let url = URL(string : "myURL") else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let response = try JSONDecoder().decode(APIResponse.self, from: data)
self.blocks = response.data.blocks;
self.items = response.data.items;
DispatchQueue.main.async {
self.reloadData()
}
} catch let jsonErr {
print(jsonErr)
}
}.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return blocks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "BlockCell") as? BlockCell else { return UITableViewCell() }
cell.blockName.text = blocks[indexPath.row].name
cell.blockDescription.text = blocks[indexPath.row].description
cell.gameCollectionView.delegate = self
cell.gameCollectionView.dataSource = self
cell.gameCollectionView.tag = indexPath.row
cell.gameCollectionView.reloadData()
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return blocks[collectionView.tag].items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "GameCollectionCell", for: indexPath) as? GameCollectionCell else { return
UICollectionViewCell() }
// here use blocks[collectionView.tag].items[indexPath.row] for each item
return cell
}
}

How to Set a specific Image to a specific array position?

I am using a UICollectionView for Horizontal scrolling having six images in the array. What I want is when an item at index path x(1) is clicked an array with images to be set on remaining items(0,2,3,4,5) except position 1. Can we set a specific image at a specific position of an array if yes then how?
In the case of Android, it is like
if (selectedPosition < 0) {
viewHolder.imageView.setImageResource(coloredSmiley[position]);
} else {
viewHolder.imageView.setImageResource(selectedPosition == position ? coloredSmiley[position] : greySmiley[position]);
}
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var selectedImageButton = -1
var imagearray = ["AngrysmIcon1", "UpsetsmIcon2", "ConfusedsmIcon3", "MehsmIcon4", "CurioussmIcon5" , "HappysmIcon6"]
var bwimagearray = ["AngrysmIcon1Inactive", "UpsetsmIcon2Inactive", "ConfusedsmIcon3Inactive", "MehsmIcon4Inactive", "CurioussmIcon5Inactive", "HappysmIcon6Inactive"]
var positiontag = ["0", "1", "2", "3", "4", "5"]
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.positiontag.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! UIcollectionViewCellCollectionViewCell
cell.imagev.image = UIImage(named: imagearray[indexPath.row])
return cell
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
internal func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedImageButton = indexPath.row
let cell = collectionView.cellForItem(at: indexPath) as! UIcollectionViewCellCollectionViewCell
if selectedImageButton < 0 {
//cell.imagev.image = bwimagearray[indexPath.row]
} else {
cell.imagev.image = UIImage(named: imagearray[indexPath.row])
}
}
}
Where selectedposition is global with value -1
From the Android snippet, i believe you need to change cellForRowAt as below,
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! UIcollectionViewCellCollectionViewCell
let imageName: String
if selectedImageButton < 0 {
imageName = imagearray[indexPath.row]
} else {
imageName = selectedImageButton == indexPath.row ? imagearray[indexPath.row] : bwimagearray[indexPath.row]
}
cell.imagev.image = UIImage(named: imageName)
return cell
}
And then set the selectedImageButton in didSelectItemAt and reload the collectionView,
internal func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedImageButton = indexPath.row
collectionView.reloadData()
}
Note: ReloadData may not be recommended and you should look for some more reactive way to notify the cells to update image.
Add this to cellForItemAt
if cell.isSelected {
cell.imagev.image = UIImage(named: imagearray[indexPath.row])
} else {
cell.imagev.image = UIImage(named: bwimagearray[indexPath.row])
}
and following to didSelectItemAt
collectionView.reloadData()
But would recommend to move the logic that set all this to cell something like below mentioned
class UIcollectionViewCellCollectionViewCell: UICollectionViewCell {
override var isSelected: Bool {
get {
return super.isSelected
}
set {
super.isSelected = newValue
updateImage()
}
}
private func updateImage() {
if isSelected {
imagev.image = UIImage(named: imagearray[indexPath.row])
} else {
imagev.image = UIImage(named: bwimagearray[indexPath.row])
}
}
}

UICollectionView Disappear

I'm quite new to Swift. My problem is that my UICollectionView is disappearing.
In Xcode, it shows that everything is in place, but when I launch on a simulator or a device it disappears only left with the Navigation Bar and the Tab Bar.
Does anyone know what caused this or how to solve this?
Cheers!
Here is my code:
class User: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return 0
}
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "Header", for: indexPath) as! UserHeaderView
header.userEmail.text = PFUser.current()!.email
header.userChosenName.text = PFUser.current()!.object(forKey: "nickname") as? String
header.profilePicture.layer.cornerRadius = header.profilePicture.frame.size.width/2
header.profilePicture.clipsToBounds = true
let query = PFUser.current()?.object(forKey: "profilePicture") as! PFFile
query.getDataInBackground { (image, error) in
if error == nil {
header.profilePicture.image = UIImage(data: image!)
}
else {
SVProgressHUD.showError(withStatus: "Something's Wrong...")
}
}
return header
}
You are using the generated class and you have the number of sections 0 and number of rows 0 you have to change those based the count you want them to show
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// The number of sections you have to provide
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// Number of Items in this section at least it should be 1
return 1
}
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "Header", for: indexPath) as! UserHeaderView
header.userEmail.text = PFUser.current()!.email
header.userChosenName.text = PFUser.current()!.object(forKey: "nickname") as? String
header.profilePicture.layer.cornerRadius = header.profilePicture.frame.size.width/2
header.profilePicture.clipsToBounds = true
let query = PFUser.current()?.object(forKey: "profilePicture") as! PFFile
query.getDataInBackground { (image, error) in
if error == nil {
header.profilePicture.image = UIImage(data: image!)
}
else {
SVProgressHUD.showError(withStatus: "Something's Wrong...")
}
}
return header
}
Edit:
You should have a cellForItemAt indexPath func else app will crash. here is an example of the function
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// this will return an empty cell where you're app will not crash
// but you have to create a cell and populate some data to the cell
return UICollectionViewCell()
}

Firebase storage data showing twice

Trying to load data in collection view with this method
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if urls.count == 0 {
return UICollectionViewCell()
}
let url = urls[indexPath.item]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier,
for: indexPath) as! ImageCollectionViewCell
storageRef.reference(withPath: url).getData(maxSize: 15 * 1024 * 1024, completion: { data, error in
if let data = data {
cell.imageView.image = UIImage(data: data)
}
})
return cell
}
The thing is - at first i can see both cells without data, then completion get called, an i'm getting a data, but the first image in both cells.
How can i do it right? And how can i get metadata in the same time too?
Screenshot:
UPD:
I wrote an array
var datas = ["first", "second", "third", "fourth"]
An changed cell code to
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier,
for: indexPath) as! ImageCollectionViewCell
cell.nameLabel.text = datas[indexPath.item]
return cell
}
And got this:
Still can't see what's wrong. My overrided methods:
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return datas.count / 2
}
SOLVED. Should return numberOfSections = 1
I think the problem is that you're downloading the information in the cellForItemAt indexPath: function and you should download the images in another function like this
//Variable to store the UIImages
var images = [UIImage]()
//function to download the images, run it in the viewdidload like self.getImages()
func getImages(){
for url in self.urls{
storageRef.reference(withPath: url).getData(maxSize: 15 * 1024 * 1024, completion: { data, error in
if let data = data {
self.images.append(UIImage(data: data))
}
})
}
self.YourCollectionView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if urls.count == 0 {
return UICollectionViewCell()
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier,
for: indexPath) as! ImageCollectionViewCell
cell.imageView.image = self.images[indexPath.row]
return cell
}
Should return 1 in numberOfSection func
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}

Resources