Swift Uicollection view inside a table View - swift

I got an image url array inside a post object array I want to display this post array in a table view and display the images as a collection view inside the table view. How can I enter image description heredo it? I tired to use a int i as an indicator but it doesn't work.
here are the codes
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeCell", for: indexPath) as! HomeTableViewCell
cell.postsText.text = postLists[indexPath.row].postText
i = indexPath.row
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return (postLists[i].imageUrlList?.count)!
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCell", for: indexPath) as! PhotosCollectionCell
let url = postLists[i].imageUrlList![indexPath.row]
let imgUrl = URL(string: url)
URLSession.shared.dataTask(with: imgUrl!, completionHandler: { (data, response, error) in
if error != nil {
// if download hits an error, so lets return out
print(error)
return
}
// if there is no error happens...
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { // in half a second...
cell.postPhoto.image = UIImage(data: data!)
}
}).resume()
return cell
}

Never forcefully wrap optional ever Until you are very sure. (I can comment also but I want to add more details which is not suitable for comment so forgive )
Another thing I can see is use indexPath.item in collectionView not indexPath.row both will be same but this is standard I always follows.
You are not calling URLSession.data task in background thread. it is always recommend to do so. my suggestion is to that you should use some third party library like SDWebIamge which will helpful to you cache already downloaded image.
Wy Crash:
What I am seeing is Crash because of your variable i in your code postLists[i].
What you should do
Inside
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
you can set tag to your collection view like
cell.yourCollectionViewObject.tag = indexPath.row
and replace
let url = postLists[i].imageUrlList![indexPath.row]
with
let url = postLists[collectionView.tag].imageUrlList![indexPath.row]
Hope it is helpful

Related

How can I fix a UIcollectionViewCell from displaying an empty cell?

I have purposely left an empty image in my assets catalog so that I can get my collectionView to somehow skip that image if it is nil, but so far it will render an empty image in that cell. It is better than crashing my app but how can I get it to skip that image?
here is my cellForItemAt indexPath code. Any ideas would be much appreciated.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: K.collectionViewCell, for: indexPath) as! CollectionViewCell
DispatchQueue.main.async {
let image = RoomModel.roomModel.rooms[indexPath.item]
if let image = image {
cell.imageView.image = image
}
}
return cell
}
Model Solution:
import UIKit
var data = [long list of UIImage(named: ...)]
class RoomModel {
var rooms = data.compactMap { ($0) }
var roomNo = 0
func setRoomNo(sender: Int) {
roomNo = sender
}
func getRoom() -> UIImage {
let image = rooms[roomNo]
return image
}
}
itemForRow Solution:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: K.collectionViewCell, for: indexPath) as! CollectionViewCell
DispatchQueue.main.async {
cell.imageView.image = self.roomModel.rooms[indexPath.item]
}
return cell
}
You would need to modify the data model to not include it if the image is nil and can be done by a simple if check before adding it in the model and then your cell would not render it as it is omitted from the data model.

UICollectionViewCell is populated with previous cells data before new the cell's data loads, need to implement PrepareForReuse()

I have a problem with my UICollectionView, The problem is that if you scroll fast enough (not even that fast) the data that was in the cell before the reuse gets displayed for a second or two before the new data gets displayed.
This is the video of it happening for more context (youtube video link): https://youtu.be/I63hBuxBGI0
This is the code inside of the cellForItemAt:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let post = feedElements[indexPath.row] as? FeedPost {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! feedCollectionViewCell
cell.delegate = self
cell.post = post
cell.commentButton.tag = indexPath.row
// assigning all of the data
cell.captionLabel.text = post.caption
cell.locationLabel.text = post.location
cell.timeAgoLabel.text = post.timeAgoString
cell.setUpElements()
cell.prepareForReuse()
return cell
}
}
The code for inside of the numberOfRowsInSection:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return feedElements.count
}
I'm pretty sure what I need to implement is prepareForReuse() but I'm not sure exactly how to do that and couldn't find anything helpful online.
Thanks a lot!
You need to add prepareForReuse in feedCollectionViewCell class like this
class feedCollectionViewCell: UITableViewCell {
override func prepareForReuse() {
super.prepareForReuse()
// Reset your values here as you want ...
self.post = nil
self.commentButton.tag = 0
// assigning all of the data
self.captionLabel.text = nil
self.locationLabel.text = nil
self.timeAgoLabel.text = nil
}
}

UICollectionView inside UITableViewCell how pass data to Uicollectionviewcell

I have a tableview controller with a collectionview inside it , in the tableviewcell i implemented a collection view with horizontal scrolling, the data appears not correct
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = friendsCollectionView.dequeueReusableCell(withReuseIdentifier: "friends", for: indexPath) as! friendCollectioncell
friendsCollectionView.delegate = self
friendsCollectionView.dataSource = self
let ava = Myfriends[indexPath.item]["ava"] as! String
let firstname = Myfriends[indexPath.row]["firstName"] as! String
let lastname = Myfriends[indexPath.row]["lastName"] as! String
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Myfriends.count
}
problem is in
return Myfriends.count
always empty so not thing appears but if a change to 1 or 2. it works ok
in uitableviewcontroller:
var myFriends: [Dictionary<String, Any>] = []
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// section 1 which includes 1 cell that shows my friends
if indexPath.section == 0 {
// accessing cell in main.storyboard that has id "friends". This cell stores my friends
let cell = tableView.dequeueReusableCell(withIdentifier: "friends", for: indexPath) as! friends
cell.Myfriends = myFriends
}
return cell
// section 2 (or any other sections) that shows all the posts of the user
} else {
return cell
}
Thank You in advance
Edit:
If I understand this right:
problem is in return Myfriends.count always empty so not thing appears but if a change to 1 or 2. it works ok
probably your collectionView(_:numberOfItemsInSection:) is called before you this cell.Myfriends = myFriends is set.
In your friends tableViewCell:
class friends: UITableViewCell .. {
...
var Myfriends: [Friend] {
didSet {
friendsCollectionView.reloadData()
}
}
}
Imho your collectionView(_:numberOfItemsInSection:) isn't called at all.
This code
friendsCollectionView.delegate = self
friendsCollectionView.dataSource = self
in your collectionView(_:cellForItemAt:) is useless, because if this method is called, dataSource (and probably delegate) are already set correctly.
Instead, change your tableView(_:cellForRowAt:) to
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView...
cell.friendsCollectionView.delegate = cell
cell.friendsCollectionView.dataSource = cell
return cell
} else {
...
}
}
or set friendsCollectionView dataSource and delegate in your friends cell, where you init your collectionView.
If that shouldn't help you, check where and how you populate your myFriends dictionary.
Besides that, I'd recommend that you create a struct Friend:
struct Friend {
let ava: String
let firstName: String
let lastName: String
}
and change your var myFriends to
var myFriends: [Friend] = []
That would make it easier and safer to access the values for your friendsCollectionCell:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = friendsCollectionView...
let friend = MyFriends[indexPath.item]
let ava = friend.ava
...
return cell
}

change tableview cell content on collection view cell selection

I have a tableView and collectionView in one view controller.
in tableView I have title description and in collectionView I have lable .
I want on collectionView label selection tableView content should change .
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Bookmark.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionViewBookmark.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! BookMarkCollectionViewCell
cell.lblTitle.text = Bookmark[indexPath.row]
cell.backgroundColor = UIColor.white
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionViewBookmark.cellForItem(at: indexPath)
cell?.backgroundColor = UIColor.blue
self.selectedIndexPath = indexPath
//
let newsDict = arrNewsData[indexPath.row]
if (indexPath.row == 1)
{
let cell1 = tableViewBookMark.cellForRow(at: indexPath) as! BookMarkFirstTableViewCell
cell1.lblTitle.text = newsDict["title"] as! String
tableViewBookMark.reloadData()
}
tableViewBookMark.reloadData()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewBookMark.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! BookMarkFirstTableViewCell
let dict = arrNewsData[indexPath.row]
cell.lblTitle.text = dict["title"] as! String
// cell.imgBookMark.image = dict["image_url"]
let url = URL(string: dict["image_url"] as! String)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if data != nil{
DispatchQueue.main.async {
let image = UIImage(data: data!)
cell.imgBookMark.image = image
}
}
}.resume()
return cell
}
See my inline comments.
var tempCell: BookMarkFirstTableViewCell?
//Inside cellForRowAt indexPath
tempCell = cell
//Inside (collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
tempCell.lblTitle.text = newsDict["title"] as! String
You're reloading the tableView after updating the values in the cell,
tableViewBookMark.reloadData()
This will trigger the data source function including cellForRowAt, so you will lose your updated values a solution to this is to have a global variable in the UIViewController and check its values inside the cellForRowAt and update it in the collectionView DidSelect .
Extra tip: you don't need to reload all the tableView for a single change you can use
tableView.reloadRows(at: [indexPath], with: .top)
to reload only number of selected cells in the tableView

Why are image views sometimes not appearing in collection view cells?

I just updated to Swift 3.
My collection views were working great before the update. I did the recommended changes to make the compiler happy, but now I'm having this problem.
Image views that are in my custom UICollectionViewCells are simply not appearing anymore. Neither programmatically generated image views nor prototype image views are appearing.
I've given the image views background colors to check if my images are nil. The background colors aren't appearing, so it is safe to assume the image views are not appearing at all.
The cells themselves ARE appearing. Each image view has a label underneath, and the label is displaying properly with the correct text.
The most confusing part is that sometimes the image views DO appear, but there seems to be no rhyme or reason as to why or when.
My code is pretty standard, but I'll go ahead and share it anyway:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return searchClubs.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: HomeCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! HomeCollectionViewCell
cell.barLabel.text = searchClubs[indexPath.row].name
cell.imageCell.image = searchClubs[indexPath.row].image
cell.imageCell.layer.masksToBounds = true
cell.imageCell.layer.cornerRadius = cell.imageCell.frame.height / 2
return cell
}
func feedSearchClubs(child: AnyObject) {
let name = child.value(forKey: "Name") as! String
//Image
let base64EncodedString = child.value(forKey: "Image")!
let imageData = NSData(base64Encoded: base64EncodedString as! String, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters)
let image = UIImage(data:imageData! as Data)
//Populate clubs array
let club = Club.init(name: name, image: image!)
self.searchClubs.append(club)
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
Since Xcode 8 you have to call layoutIfNeeded() to calculate size (in your case you need to know cell.imageCell.frame.height) and position from auto layout rules or use a fixed value of cornerRadius.
cell.imageCell.layoutIfNeeded()
cell.imageCell.layer.masksToBounds = true
cell.imageCell.layer.cornerRadius = cell.imageCell.frame.height / 2
OR
cell.imageCell.layer.masksToBounds = true
cell.imageCell.layer.cornerRadius = 5
The imageCell's frame isn't set up ready yet in the cellForItemAt method.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: HomeCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! HomeCollectionViewCell
cell.barLabel.text = searchClubs[indexPath.row].name
cell.imageCell.image = searchClubs[indexPath.row].image
return cell
}
Instead put the setting up of layer on willDisplay since cell.imageCell.frame.height will have its value.
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let cell: HomeCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! HomeCollectionViewCell
cell.imageCell.layer.masksToBounds = true
cell.imageCell.layer.cornerRadius = cell.imageCell.frame.height / 2
}