reloadData on CollectionView, Swift - swift

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! UsersCollectionViewCell
someRequest(username: self.usernameUrl) { (userInfo, error) in
guard let userInfo = userInfo else {
// present error
return
}
print("Running")
let user_image_url = userInfo.items.map{($0.avatarURL)}
cell.userCellLabel.text = user_name[indexPath.item]
}
return cell
}
Why the code inside someRequest( ... ) is not running when I call reloadData on viewDidAppear?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.collectionViewUsers.reloadData()
}
P.s. Somerequest just perform an Alamofire get

Async Alamofire request must be outside of the cellForItemAt handler. Because cellForItemAt is called every time the cell appeared on the screen, that means it will try to fetch same data for same cell for multiple times while scrolling, which is not intended. So viewDidLoad is a good place to start fetching, after async call completed don't forget to reload collectionview.
var user_image_url: String?
override func viewDidLoad(_ animated: Bool) {
someRequest(username: self.usernameUrl) { (userInfo, error) in
guard let userInfo = userInfo else {
// present error
return
}
print("Running")
user_image_url = userInfo.items.map{($0.avatarURL)}
self.collectionViewUsers.reloadData()
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! UsersCollectionViewCell
if let user_image_url = user_image_url {
//user_image_url is avaliable
}
return cell
}

Related

Deleting cell in collectionView bug. image overlaid on image

I have a collectionView and deleting cells in it seems wrong.
https://giphy.com/gifs/9VIPAbXq8GScmDNiZa
As you can see when i tried to delete first cell, image overlaid on image. it happens randomly
DataSource for Cell
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.identifier, for: indexPath) as? CollectionViewCell else { return UICollectionViewCell() }
if let image = ImageModel.images[indexPath.row].image {
cell.configureCell(image: image)
}
return cell
}
Delegate for deleting
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
UIView.animate(withDuration: 0.5) {
if let cell = collectionView.cellForItem(at: indexPath) as? CollectionViewCell {
cell.image.center.x += 300 }
} completion: { Bool in
ImageModel.images.remove(at: indexPath.row)
collectionView.deleteItems(at: [indexPath])
collectionView.reloadData()
}
}
Delegate for downloading new Images
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.presenter?.fetchData()
}
Also i have cell config with reusable cell
override func prepareForReuse() {
super.prepareForReuse()
self.image.image = nil
self.image.center.x = 0
}
What can be wrong with cell?

Hero ViewController animation with collectionView - swift

I'm trying to present a view controller whenever I tap on a collection view cellwith the zoom effect.
As far as I know Hero framework works using HeroID's, you set the same id in the fromView and in the toView and the frameworks does the hard work for you.
I have set It up this way:
in viewcontroller1 :
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! CollectionViewCell
cell.heroID = "profheroid"
let viewcontroller2 = ViewController2()
viewcontroller2.configureController(with: arrayOfModels[indexPath.item])
viewcontroller2.heroModalAnimationType = .zoom
viewcontroller2.modalPresentationStyle = .fullScreen
self.navigationController?.present(viewcontroller2, animated: true, completion: nil)
}
and in viewcontroller2:
override func viewDidLoad() {
super.viewDidLoad()
view.heroID = "profheroid"
}
The problem happens whenever I tap on a collectionviewCell the presentation happens correctly but I see so many cells being presented at the same time.
I think this happens because cell.heroID = "profheroid" is applied to more cells at the same time.
How can I make sure that when the cell is presented the heroID's are only in the cell tapped and in the viewcontroller's view??
I think you must clear this heroID when cell is reused and after VC is presented.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
cell.heroID = nil // or empty
}
I think you must reset heroID to nil in cellForItemAt:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: YourCollectionViewCell.description(), for: indexPath) as? YourCollectionViewCell else {
return UICollectionViewCell()
}
cell.heroID = nil
return cell
}
Reset all visible cell's heroID to nil, and only set heroID for cell selected with value before present:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.visibleCells.forEach { cell in
cell.heroID = nil //reset heroID
}
guard let cell = collectionView.cellForItem(at: indexPath) else { return }
cell.heroID = "profheroid"
let viewcontroller2 = ViewController2()
viewcontroller2.configureController(with: arrayOfModels[indexPath.item])
viewcontroller2.heroModalAnimationType = .zoom
viewcontroller2.modalPresentationStyle = .fullScreen
self.navigationController?.present(viewcontroller2, animated: true, completion: nil)
}

collectionView cell not working in tapGestureRecognizer function

I want to change a cell label text when I click/tap on a collectionView cell.
I tried the following way, but this not working.
#objc func tap(_ sender: UITapGestureRecognizer) {
let location = sender.location(in: self.collectionView)
let indexPath = self.collectionView.indexPathForItem(at: location)
if let index = indexPath {
let subL = zoneDict?.sublevel[index.row]
if (subL?.sublevel.count)! > 0 {
DispatchQueue.main.async {
self.zoneDict = subL!
print("self.zoneDict --\(self.zoneDict!)")
let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "colViewCell", for: index) as! CollectionViewCell
cell.zoneNameLabel.text = self.zoneDict?.name // Cannot update the text label. It show the default value
print("zone name-- \(self.zoneDict?.name)") // Its print the result.
}
self.delegate?.selectedZoneWithCellItems(items: "cell")
}
}
}
I think when you tap collectionViewCell then iOS system default call function didSelectItemAtIndexPath of CollectionView so that you must handle default event selected cell by the way register UITapGestureRecognizer for your cell and after that you must set property of view (isUserInteractionEnabled = true).
For example: self.yourview.isUserInteractionEnabled = true
you can use this in the VC containing the collection view
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = segmentCollectionView.dequeueReusableCell(withReuseIdentifier: SegmentCellId, for: indexPath) as! CollectionViewCell
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(printHello))
cell.addGestureRecognizer(tapGesture)
return cell
}
#objc func printHello(){
print("Hello")
}
You can use its delegate method in this way to change the cell label text when clicked on cell: -> Here I am using two arrays to update the text of the label as an example:
//Variables that are used below.
let nameCapitalArr1 = ["ABC", "DFG", "EFG", "HIJ", "KLM", "NOP", "QRS", "TUV", "WXY", "Z"]
let nameSmallArr2 = ["abc", "dfg", "efg", "hij", "klm", "nop", "qrs", "tuv", "wxy", "z"]
var changeFlag: Bool = false
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return nameCapitalArr1.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LabelTextCollectionViewCell", for: indexPath) as? LabelTextCollectionViewCell else { return UICollectionViewCell() }
cell.nameTextLabel.text = !changeFlag ? nameCapitalArr1[indexPath.row] : nameSmallArr2[indexPath.row]
return cell
}
/*That method is called when tapping on the cell and reload that particular cell and also change the label text.*/
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
changeFlag = !changeFlag
collectionView.reloadItems(at: [indexPath])
}
Output: -> Its reflect the cell text with capital to small array values while tapping on the cell with toggle effect.

Opening another screen by clicking on UICollectionViewCell

I have a screen that contains a UITableView and inside some UICollectionViews.
I need to click on UICollectionViewCell and open the next screen, and send some information to this new screen. But I can not.
According to my structure "follows" does not work. I need some help to find another way to do this.
Code: - TableViewCell
class CategoriasTableViewCell: UITableViewCell {
var db: Firestore!
var categoriasArray = [Categorias]()
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var labelTitleCategorias: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
collectionView.dataSource = self
collectionView.delegate = self
/*Firebase*/
let autenticacao = Auth.auth()
autenticacao.addStateDidChangeListener { (autenticacao, usuario) in
if let usuarioLogado = usuario {
} else {
//self.performSegue(withIdentifier: "checkEntrarSegue", sender: nil)
}
}
db = Firestore.firestore()
loadData()
}
func loadData() {
db.collection("Categories")
.addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("Error fetching documents: \(error!)")
return
}
self.categoriasArray = querySnapshot!.documents.flatMap({Categorias(dictionary: $0.data())})
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
}
Code - TableView
class TabHomeViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
let autenticacao = Auth.auth()
autenticacao.addStateDidChangeListener { (autenticacao, usuario) in
if usuario == nil {
self.performSegue(withIdentifier: "logoutAutomatico", sender: nil)
//....
}
}
}
}
extension TabHomeViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellSlide", for: indexPath) as! SlideTableViewCell
return cell
} else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellCategorias", for: indexPath) as! CategoriasTableViewCell
//cell.collectionView.reloadData()
return cell
} else if indexPath.row == 2{
let cell = tableView.dequeueReusableCell(withIdentifier: "cellRecomendacoes", for: indexPath) as! RecomendacoesTableViewCell
return cell
} else if indexPath.row == 3 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellPromocoes", for: indexPath) as! PromocoesTableViewCell
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellFamosos", for: indexPath) as! FamososTableViewCell
return cell
}
}
/*func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.row == 1 {
if let cell = cell as? CategoriasTableViewCell {
cell.collectionView.reloadData()
print("Atualizando Collection1")
}
}
}*/
}
extension TabHomeViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch indexPath.row {
case 0:
return 215
case 1:
return 200
case 2:
return 300
case 3:
return 400
case 4:
return 500
default:
return UITableViewAutomaticDimension
}
}
}
//COLLECTION CATEGORIAS
extension CategoriasTableViewCell: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return categoriasArray.count //Int(Constant.totalItem)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//set the image URL
let urlBase = categoriasArray[indexPath.row].foto_horizontal
let imageUrl = URL(string: urlBase)!
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BoxCollectionCategorias", for: indexPath) as! CellCategoriasCollectionViewCell
cell.labelNameCategoria.text = categoriasArray[indexPath.row].nome
cell.imageView.sd_setImage(with: imageUrl) { (image, erro, cache, url) in
// Here my code ...
}
return (cell)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Click... \(categoriasArray[indexPath.row].uid)")
// Here I detect the click on the UICollectionViewCell
}
}
extension CategoriasTableViewCell: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 130)
}
}
I'm using an extension CategoriasTableViewCell: UICollectionViewDataSource {}, to edit the UICollectionView data
You can just create delegate for your TableViewCell
protocol CategoriasTableViewCellDelegate : class {
func categoryTapped(_ cell: CategoriasTableViewCell, categoriasID:Int)
}
class CategoriasTableViewCell: UITableViewCell {
weak var delegate : CategoriasTableViewCellDelegate?
}
And In CategoriasTableViewCell Extention
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if delegate != nil {
delegate?.categoryTapped(self, categoriasID: categoriasArray[indexPath.row].uid)
}
print("Click... \(categoriasArray[indexPath.row].uid)")
// Here I detect the click on the UICollectionViewCell
}
At your TabHomeViewController set cell.delegate = self
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellSlide", for: indexPath) as! SlideTableViewCell
return cell
} else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellCategorias", for: indexPath) as! CategoriasTableViewCell
//cell.collectionView.reloadData()
cell.delegate = self
return cell
} else if indexPath.row == 2{
let cell = tableView.dequeueReusableCell(withIdentifier: "cellRecomendacoes", for: indexPath) as! RecomendacoesTableViewCell
return cell
} else if indexPath.row == 3 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellPromocoes", for: indexPath) as! PromocoesTableViewCell
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellFamosos", for: indexPath) as! FamososTableViewCell
return cell
}
}
// now you can get Data in TabHomeViewController
extension TabHomeViewController:CategoriasTableViewCellDelegate {
func categoryTapped(_ cell: CategoriasTableViewCell, categoriasID:Int){
}
}
You need to trigger a segue to reach the other view, or present your new view on top of the current view (which I don't recommend unless you know what you are doing).
To pass your information from one view to another you have several options :
pass it through the segue (one to one)
use protocols & delegates (one to one)
use events & observers (one to many)
use a third class responsible for holding the current data (one to many)

Swift first selected in collection view

I am trying to get the first selected cell in the collection view. It is showing nil but in the app there is cells showing up. The clientCollectionView is coming up nil too. Here is the function that i am trying to use
func currentClient() -> Client? {
let idx = self.clientsCollectionView?.indexPathsForSelectedItems?.first
guard let firstSelected = idx?.row else {
return nil
}
return clients()[firstSelected]
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return clients().count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "clientCollectionViewCell", for: indexPath)
if let cell = cell as? ClientCollectionViewCell {
cell.client = clients()[indexPath.row]
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
populateExerciseFields()
}
func currentClient() -> Client? {
// You haven't really explained what "clientsCollectionView" is
// I suspect you just need to replace it with a reference to the
// actual collection view, as shown below.
let idx = self.collectionView?.indexPathsForSelectedItems?.first
guard let firstSelected = idx?.row else {
return nil
}
return clients()[firstSelected]
}
See here: https://developer.apple.com/documentation/uikit/uicollectionviewcontroller/1623983-collectionview
You are probably referencing some other instance ... but not the one that is associated with your collection view controller.