1
votes

Hi I am trying to make a home feed like facebook using UICollectionView But in each cell i want to put another collectionView that have 3 cells.

you can clone the project here

I have two bugs the first is when i scroll on the inner collection View the bounce do not bring back the cell to center. when i created the collection view i enabled the paging and set the minimumLineSpacing to 0 i could not understand why this is happening. when i tried to debug I noticed that this bug stops when i remove this line

layout.estimatedItemSize = CGSize(width: cv.frame.width, height: 1)

but removing that line brings me this error

The behavior of the UICollectionViewFlowLayout is not defined because: the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values

because my cell have a dynamic Height here is an example

example

my second problem is the text on each inner cell dosent display the good text i have to scroll until the last cell of the inner collection view to see the good text displayed here is an example example

1
So first issue is fixed? What is the second issue?meaning-matters
no none of them is fixed on the first Gif you can see that at the end the cell is not coming back to centercczak
my second problem is the text on each inner cell dosent display the good text i have to scroll until the last cell of the inner collection view to see the good text displayed here is an example you can see the second GIFcczak
What do you mean with 'good' text?meaning-matters
i mean the text that suppose to be there.I have an array of Posts each post has an array of String so to create all the Outer cells i use Posts.count then i have to create the inner cells to do so i use posts.listText.count in the cellForItemAt indexPath function i do cell.text = listText[indexPath.row] but the indexPath.row is not the good one you can see on the second gif that the index path that i print on the cell before the Lorem Ipsum is not the good one but after i scroll and come back the good one is displayedcczak

1 Answers

1
votes

You first issue will be solved by setting the minimumInteritemSpacing for the innerCollectionView in the OuterCell. So the definition for innerCollectionView becomes this:

let innerCollectionView : UICollectionView = {
    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .horizontal
    layout.minimumLineSpacing = 0
    layout.minimumInteritemSpacing = 0

    let cv = UICollectionView(frame :.zero , collectionViewLayout: layout)
    cv.translatesAutoresizingMaskIntoConstraints = false
    cv.backgroundColor = .orange
    layout.estimatedItemSize =  CGSize(width: cv.frame.width, height: 1)
    cv.isPagingEnabled = true
    cv.showsHorizontalScrollIndicator = false

    return cv

}()

The second issue is solved by adding calls to reloadData and layoutIfNeeded in the didSet of the post property of OuterCell like this:

var post: Post? {
    didSet {
        if let numLikes = post?.numLikes {
            likesLabel.text = "\(numLikes) Likes"
        }

        if  let numComments = post?.numComments {
            commentsLabel.text = "\(numComments) Comments"
        }
        innerCollectionView.reloadData()
        self.layoutIfNeeded()
    }
}

What you are seeing is related to cell reuse. You can see this in effect if you scroll to the yellow bordered text on the first item and then scroll down. You will see others are also on the yellow bordered text (although at least with the correct text now).

EDIT

As a bonus here is one method to remember the state of the cells.

First you need to track when the position changes so in OuterCell.swft add a new protocol like this:

protocol OuterCellProtocol: class {
    func changed(toPosition position: Int, cell: OutterCell)
}

then add an instance variable for a delegate of that protocol to the OuterCell class like this:

public weak var delegate: OuterCellProtocol?

then finally you need to add the following method which is called when the scrolling finishes, calculates the new position and calls the delegate method to let it know. Like this:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    if let index = self.innerCollectionView.indexPathForItem(at: CGPoint(x: self.innerCollectionView.contentOffset.x + 1, y: self.innerCollectionView.contentOffset.y + 1)) {
        self.delegate?.changed(toPosition: index.row, cell: self)
    }
}

So that's each cell detecting when the collection view cell changes and informing a delegate. Let's see how to use that information.

The OutterCellCollectionViewController is going to need to keep track the position for each cell in it's collection view and update them when they become visible.

So first make the OutterCellCollectionViewController conform to the OuterCellProtocol so it is informed when one of its

class OutterCellCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, OuterCellProtocol   {

then add a class instance variable to record the cell positions to OuterCellCollectionViewController like this:

var positionForCell: [Int: Int] = [:]

then add the required OuterCellProtocol method to record the cell position changes like this:

func changed(toPosition position: Int, cell: OutterCell) {
    if let index = self.collectionView?.indexPath(for: cell) {
        self.positionForCell[index.row] = position
    }
}

and finally update the cellForItemAt method to set the delegate for a cell and to use the new cell positions like this:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OutterCardCell", for: indexPath) as! OutterCell
    cell.post = posts[indexPath.row]

    cell.delegate = self

    let cellPosition = self.positionForCell[indexPath.row] ?? 0
    cell.innerCollectionView.scrollToItem(at: IndexPath(row: cellPosition, section: 0), at: .left, animated: false)
    print (cellPosition)

    return cell
}

If you managed to get that all setup correctly it should track the positions when you scroll up and down the list.