How to use rbcollectionviewinfofolderlayout with custom UICollectionViewCell & CollectionViewLayout - ios

What I want to do is display a UICollectionView inside a UICollectionViewCell when this cell is selected. I want to use rbcollectionviewinfofolderlayout to fold out my collection view and show a new collection view inside of this. However I'm not sure how I can implement this into my existing code. I have three collection views inside one view. I hide and unhide my views accordingly to what selection the user makes. I use a custom cell xib for the cells inside my collection views and I have a custom collectionviewflowlayout that makes sure there are always 3 cells showing in the width of the device.
This is what my view controller looks like.
in my viewDidLoad I have to set the RBCollectionViewInfoFolderLayout to my collectionview. As you can see the layout variable holds my CustomCollectionViewFlow which I set as the collection view layout before I implemented the RBCollectionviewinfofolderlayout.
override func viewDidLoad() {
super.viewDidLoad()
musicLib.loadLibrary()
PlaylistCollectionView.indicatorStyle = UIScrollViewIndicatorStyle.White
AlbumCollectionView.indicatorStyle = UIScrollViewIndicatorStyle.White
ArtistCollectionView.indicatorStyle = UIScrollViewIndicatorStyle.White
layout = CustomCollectionViewFlow()
cview = ArtistCollectionView
let lay: RBCollectionViewInfoFolderLayout = ArtistCollectionView.collectionViewLayout as! RBCollectionViewInfoFolderLayout
lay.cellSize = CGSizeMake(80, 80)
lay.interItemSpacingY = 10
lay.interItemSpacingX = 0
let nib = UINib(nibName: "CollectionViewCell", bundle: nil)
cview.registerClass(UICollectionReusableView.self, forSupplementaryViewOfKind: RBCollectionViewInfoFolderHeaderKind, withReuseIdentifier: "header")
cview.registerClass(UICollectionReusableView.self, forSupplementaryViewOfKind: RBCollectionViewInfoFolderFooterKind, withReuseIdentifier: "footer")
cview.registerClass(collectionViewFolder.self, forSupplementaryViewOfKind: RBCollectionViewInfoFolderFolderKind, withReuseIdentifier: "folder")
cview.registerClass(RBCollectionViewInfoFolderDimple.self, forSupplementaryViewOfKind: RBCollectionViewInfoFolderDimpleKind, withReuseIdentifier: "dimple")
ArtistCollectionView.collectionViewLayout = lay
ArtistCollectionView.registerNib(nib, forCellWithReuseIdentifier: "item")
ArtistCollectionView.dataSource = self
ArtistCollectionView.delegate = self
PlaylistCollectionView.collectionViewLayout = layout
PlaylistCollectionView.registerNib(nib, forCellWithReuseIdentifier: "item")
PlaylistCollectionView.dataSource = self
AlbumCollectionView.collectionViewLayout = layout
AlbumCollectionView.registerNib(nib, forCellWithReuseIdentifier: "item")
AlbumCollectionView.dataSource = self
}
My CustomCollectionViewFlow looks like this
class CustomCollectionViewFlow: UICollectionViewFlowLayout{
override init(){
super.init()
setupLayout()
}
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
setupLayout()
}
override var itemSize: CGSize {
set {
}
get {
let numberOfColumns: CGFloat = 3
let itemWidth = (CGRectGetWidth(self.collectionView!.frame) - (numberOfColumns - 1)) / numberOfColumns
return CGSizeMake(itemWidth, itemWidth)
}
}
func setupLayout() {
minimumInteritemSpacing = 0
minimumLineSpacing = 0
scrollDirection = .Vertical
}
}
I will hold of on putting all my coding here as it will be become to big of a post and the other methodes are kind of irrelevant at this point. However what I did do is put the example application that I made for this on git here for anyone who wants to check it out.
This image shows what the state of my collectionview was before I implemented the rbcollectionview. The second image shows what I'm trying to achieve
This is how the view should look when an item is being tapped
EDIT
I have been able to get it working kind of. I was able to show the layout like I desired. Just like I had it before I implemented the rbcollectionviewinfofolderlayout. However it seems that when the folder is bigger then the screen size it won't actually fold out. It will fold out for a second and collapse again. It might be caused by the layout i've implemented. Below is the code that is responsible for this.
the class that is responsible for my layout
class RBCollectionLayout: RBCollectionViewInfoFolderLayout
{
var view: UIView!
init(view: UIView){
super.init()
self.view = view
setupLayout()
}
override init(){
super.init()
setupLayout()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupLayout()
}
func setupLayout(){
let numberofItems: CGFloat = 3
let itemWidth = (CGRectGetWidth(view.frame)) / numberofItems
cellSize = CGSizeMake(itemWidth, itemWidth)
interItemSpacingX = 0
interItemSpacingY = 0
}
}
The method that will calculate the desired with when the screen is changed from portrait to landscape
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if(view_artist.hidden == false){
guard let ArtistFlowLayout = ArtistCollectionView.collectionViewLayout as? RBCollectionViewInfoFolderLayout else {
return
}
lay = RBCollectionLayout(view: self.view)
ArtistCollectionView.collectionViewLayout = lay
ArtistFlowLayout.invalidateLayout()
}
}
this is how the layout is being set in my viewdidload
lay = RBCollectionLayout(view: self.view)
ArtistCollectionView.collectionViewLayout = lay
again all my code is available on my git here

Well, I'm probably the only person who can answer your question, since I wrote this control and I doubt anyone else is actually using it.
You seem to be missing a key component which is the delegate method collectionView:viewForSupplementaryElementOfKind:atIndexPath: where you tell the CollectionView what views to use for the various elements. If you go to the GitHub Repo and look at the example you will see that in that method I return 4 different views depending on the viewKind that was asked for. In your code I believe what you want to do is return your custom flow layout based collection view for the RBCollectionViewInfoFolderFolderKind. This would place your 3 cell flow layout view into the expanded folder of the selected cell.
I cloned your repo but it doesn't appear to be up to date with the code you are showing here.

Related

Nested UIView inside UICollectionViewCell not updating

I'm trying to display a list of recepcies for at pet proyect, which I've modeled with a UICollectionView and for each item, a custom UICollectionViewCell.
This UICollectionViewCell has inside a UIImageView and a custom UIView for layout pruposes.
The tree goes like this
HomeViewController (UIViewController)
└── UICollectionView
└── RecepieCardCollectionViewCell (UICollectionViewCell)
├── UIImageView
└── RecepieCardInfoView (UIView)
└── UILabel
I'm comforming to the UICollectionViewDelegate and UICollectionViewDataSource protocols at the HomeViewController, and then I'm configuring the cells and their nested UIViews though a configure method
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: RecepieCardCollectionViewCell.identifier, for: indexPath
) as? RecepieCardCollectionViewCell else {
return UICollectionViewCell()
}
cell.configure(self.recepies[indexPath.row])
return cell
}
Then, at the RecepieCardCollectionViewCell:
func configure(_ recepie: Recepie) {
cardInfoView.configure(recepie)
thumbnail.kf.setImage(with: URL(string: recepie.getCover()))
}
And, finally, at the RecepieCardInfoView
func configure(_ recepie: Recepie) {
label.text = recepie.getName()
}
The thing is that the most deep nested UILabel at the RecepieCardInfoView, is receiving the data, but is not being updated. It always shows the same placeholder text, instead of the actual recepie name.
Things I've tried:
Placing .setNeedsDisplay() to all the element, with no results.
Using DispatchQueue.main.async but didn't work, which makes sense since I'm not using an API request to show the recepies (for now).
Move the label inside RecepieCardInfoView to the RecepieCardCollectionViewCell. This worked for some reason, but I would like to understand why.
If you need more context of the code, you can find the full repository here at the branch feat/recepies-list
I've been asked to add the full code of the cell, here it is:
import UIKit
import Kingfisher
class RecepieCardCollectionViewCell: UICollectionViewCell {
static let identifier = "RecepieCardUICollectionViewCell"
let thumbnail: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.kf.setImage(with: URL(string: "https://i.imgur.com/ISxVZHA.png"))
return image
}()
let content: UIView = {
let view = UIView(frame: .zero)
view.clipsToBounds = true
view.layer.cornerRadius = 40
return view
}()
let cardInfoView = RecepieCardInfoView(frame: .zero)
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .themeWhite
layer.cornerRadius = 40
addSubview(content)
content.frame = bounds
content.addSubview(thumbnail)
let cardInfoView = RecepieCardInfoView(frame: .zero)
content.addSubview(cardInfoView)
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.17
layer.shadowOffset = .zero
layer.shadowRadius = 10
NSLayoutConstraint.activate([
thumbnail.leadingAnchor.constraint(equalTo: content.leadingAnchor),
thumbnail.trailingAnchor.constraint(equalTo: content.trailingAnchor),
thumbnail.topAnchor.constraint(equalTo: content.topAnchor),
thumbnail.heightAnchor.constraint(equalTo: thumbnail.widthAnchor),
cardInfoView.topAnchor.constraint(equalTo: content.bottomAnchor, constant: -100),
cardInfoView.bottomAnchor.constraint(equalTo: content.bottomAnchor),
cardInfoView.leadingAnchor.constraint(equalTo: content.leadingAnchor),
cardInfoView.trailingAnchor.constraint(equalTo: content.trailingAnchor),
])
}
required init(coder: NSCoder) {
fatalError()
}
func configure(_ recepie: Recepie) {
cardInfoView.configure(recepie)
thumbnail.kf.setImage(with: URL(string: recepie.getCover()))
}
}
Thanks you in advance!
You create 2 instances of cardInfoView inside RecepieCardCollectionViewCell
let cardInfoView = RecepieCardInfoView(frame: .zero) // here 1
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .themeWhite
layer.cornerRadius = 40
addSubview(content)
content.frame = bounds
content.addSubview(thumbnail)
let cardInfoView = RecepieCardInfoView(frame: .zero) // and here 2
The problem is that you add the inner view without updating it's content , and update the outer view without adding it to cell hierarchy

Swift custom UICollectionViewCell subViews disappear when imageView image set at ViewController

I faced weird issue while handling UICollectionView
I Created simple custom UICollectionViewCell, which has only one imageView and Label:
There's default placeholder image for Cell's imageView and updating imageView.image from collectionView(_:cellForItemAt:). But When image is set, all subview of cell disappears:
(Cells are not disappear at same time because downloading & setting image is async)
Note: Sample data I used is not wrong (Same data works for TableView in same app)
Why this happens and how can I fix it?
this is Sample data I used:
let movies = [
MovieFront(title: "Spider-Man: No Way Home", posterPath: "1g0dhYtq4irTY1GPXvft6k4YLjm.jpg", genre: "Genre", releaseDate: "2021-12-15", ratingScore: 8.4, ratingCount: 3955),
MovieFront(title: "Spider-Man: No Way Home", posterPath: "1g0dhYtq4irTY1GPXvft6k4YLjm.jpg", genre: "Genre", releaseDate: "2021-12-15", ratingScore: 8.4, ratingCount: 3955),
MovieFront(title: "Spider-Man: No Way Home", posterPath: "1g0dhYtq4irTY1GPXvft6k4YLjm.jpg", genre: "Genre", releaseDate: "2021-12-15", ratingScore: 8.4, ratingCount: 3955),
MovieFront(title: "Spider-Man: No Way Home", posterPath: "1g0dhYtq4irTY1GPXvft6k4YLjm.jpg", genre: "Genre", releaseDate: "2021-12-15", ratingScore: 8.4, ratingCount: 3955)
]
this is my part of ViewController:
lazy var collectionView = { () -> UICollectionView in
// FlowLayout
var flowLayout = UICollectionViewFlowLayout()
flowLayout.headerReferenceSize = CGSize(width: self.preferredContentSize.width, height: 180)
flowLayout.sectionInset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
flowLayout.minimumInteritemSpacing = 20
flowLayout.minimumLineSpacing = 20
// Collection View
var collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: flowLayout)
collectionView.register(DiscoverCollectionViewCell.self, forCellWithReuseIdentifier: identifiers.discover_collection_cell)
collectionView.register(DiscoverCollectionHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: identifiers.discover_collection_header)
collectionView.backgroundColor = UIColor(named: Colors.background)
return collectionView
}()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Discover"
collectionView.dataSource = self
collectionView.delegate = self
self.view.backgroundColor = UIColor(named: Colors.background)
self.view.addSubview(collectionView)
collectionView.snp.makeConstraints { $0.edges.equalTo(self.view.safeAreaLayoutGuide) }
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// Sample Cell
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifiers.discover_collection_cell, for: indexPath) as? DiscoverCollectionViewCell else { return DiscoverCollectionViewCell() }
let movie = movies[indexPath.row]
cell.movieTitle.text = movie.title
DispatchQueue.global().async {
guard let imageURL = URL(string: "https://image.tmdb.org/t/p/original/\(movie.posterPath)") else { return }
guard let imageData = try? Data(contentsOf: imageURL) else { return }
DispatchQueue.main.sync {
cell.posterImage.image = UIImage(data: imageData)
}
}
return cell
}
and this is my custom CollectionViewCell, I used Snapkit, Then library:
class DiscoverCollectionViewCell: UICollectionViewCell {
//MARK: Create properties
lazy var posterImage = UIImageView().then {
$0.image = UIImage(named: "img_placeholder")
$0.contentMode = .scaleAspectFit
}
lazy var movieTitle = UILabel().then {
$0.font = UIFont.systemFont(ofSize: 15)
$0.textColor = .white
$0.numberOfLines = 2
$0.minimumScaleFactor = 10
}
override init(frame: CGRect) {
super.init(frame: frame)
// add to view
self.addSubview(posterImage)
self.addSubview(movieTitle)
//MARK: Add Constraints
posterImage.snp.makeConstraints { make in
make.top.left.right.equalToSuperview()
}
movieTitle.snp.makeConstraints { make in
make.top.equalTo(posterImage.snp.bottom).offset(5)
make.bottom.greaterThanOrEqualToSuperview()
make.leading.equalTo(posterImage.snp.leading)
make.trailing.equalTo(posterImage.snp.trailing)
}
self.backgroundColor = .blue
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Two issues with your cell's layout...
// add to view
self.addSubview(posterImage)
self.addSubview(movieTitle)
//MARK: Add Constraints
posterImage.snp.makeConstraints { make in
make.top.left.right.equalToSuperview()
}
You should always add UI elements to the cell's .contentView, not to the cell itself.
You did not constrain the bottom of the image view.
// add to ContentView!
self.contentView.addSubview(posterImage)
self.contentView.addSubview(movieTitle)
//MARK: Add Constraints
posterImage.snp.makeConstraints { make in
make.top.left.right.bottom.equalToSuperview()
}
Edit
You were missing a couple things from your post (including how you're setting your cell / item size), so while the above changes do fix the image not showing at all, it's not quite what you're going for.
I'm assuming you're setting the flow layout .itemSize somewhere, so your original constraints - without adding .bottom. to the image view constraints - were close...
When you add an image to a UIImageView, the intrinsicContentSize becomes the size of the image. Your constraints are controlling the width, but...
This constraint on your label:
make.bottom.greaterThanOrEqualToSuperview()
means "put the Bottom of the label at the Bottom of its superview or farther down!"
When your image loads, it sets the image view Height to its own Height and pushes the label way down past the bottom of the cell.
That line needs to be:
make.bottom.equalToSuperview()
That will prevent the Bottom of the label from moving.
Next, you need to tell auto-layout "don't compress or stretch the label vertically":
// prevent label from stretching vertically
movieTitle.setContentHuggingPriority(.required, for: .vertical)
// prevent label from compressing vertically
movieTitle.setContentCompressionResistancePriority(.required, for: .vertical)
Without that, the label will be compressed down to Zero height.
I find it very helpful to add comments so I know what I'm expecting to happen:
override init(frame: CGRect) {
super.init(frame: frame)
// add to ContentView
self.contentView.addSubview(posterImage)
self.contentView.addSubview(movieTitle)
//MARK: Add Constraints
posterImage.snp.makeConstraints { make in
// constrain image view to
// Top / Left / Right of contentView
make.top.left.right.equalToSuperview()
}
// prevent label from stretching vertically
movieTitle.setContentHuggingPriority(.required, for: .vertical)
// prevent label from compressing vertically
movieTitle.setContentCompressionResistancePriority(.required, for: .vertical)
movieTitle.snp.makeConstraints { make in
// constrain Top of label to Bottom of image view
// because we've set Hugging and Compression Resistance on the label,
// this will "pull down" the bottom of the image view
make.top.equalTo(posterImage.snp.bottom).offset(5)
// constrain Bottom of label to Bottom of contentView
// must be EQUAL TO
//make.bottom.greaterThanOrEqualToSuperview()
make.bottom.equalToSuperview()
// Leading / Trailing equal to image view
make.leading.equalTo(posterImage.snp.leading)
make.trailing.equalTo(posterImage.snp.trailing)
}
self.backgroundColor = .blue
}
Now we get this result:
and after the images download:
One final thing - although you may have already done something to address this...
As you see in those screenshots, setting .numberOfLines = 2 on a label does not force a 2-line height... it only limits it to 2 lines. If a Movie Title is short, the label height will be shorter as seen in the 2nd cell.
One way to fix that would be to constrain the label height to something like 2.5 lines by adding this to your init:
if let font = movieTitle.font {
movieTitle.snp.makeConstraints { make in
make.height.equalTo(font.lineHeight * 2.5)
}
}
That will give this output:
Although I am not sure, Because collection view cells are being reused the init of cells only gets called at the first time, not the time when image data is getting loaded from the server.
Try moving your layout-related code(specifically adding subviews and constraining them) in a different method of the cell and call it every time image gets loaded.

UIKit: Constraints on a UITableViewCell don't work properly for the first few cells in a table view

I have the following UITableViewCell subclass:
import Foundation
import UIKit
class FlagListTableViewCell: UITableViewCell, ReusableView {
private let flagImageView = UIImageView()
private var cellHeightConstraint: NSLayoutConstraint!
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.cellHeightConstraint = self.flagImageView.heightAnchor.constraint(equalToConstant: 0)
self.cellHeightConstraint.isActive = true
self.flagImageView.contentMode = .scaleAspectFit
self.flagImageView.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(self.flagImageView)
// Constraints the top, leading, trailing, and bottom anchors of `flsgImageView` to `self.contentView`
self.flagImageView.pinEdges(to: self.contentView)
}
#available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func bind(to country: Country) {
let image = UIImage(named: country.flagImageName)!
self.flagImageView.image = image
let imageViewWidth = self.contentView.frame.width
let imageWidth = CGFloat(self.flagImageView.image!.cgImage!.width)
let imageHeight = CGFloat(self.flagImageView.image!.cgImage!.height)
let scaledHeight = imageHeight * (imageViewWidth / imageWidth)
self.cellHeightConstraint.constant = scaledHeight
}
}
I have a UITableViewController subclass which displays ~250 of these cells (as reusable cells).
When I display the table view however, the first three cells (or however many the device screen size fits) have incorrect constraints for some reason and cause the cells to look incorrect. However, for all other cells, they look fine. Additionally, when I scroll down and then back up to the first three cells, they look fine as well.
Anyone know what could be causing this and how to fix it?
I have tried the following solutions, and none have worked:
Implementing the heightForRowAt delegate method and returning UITableView.automaticDimension, and also implementing the estimatedHeightForRowAt delegate and returning various values for it
Implementing layoutSubviews method for the table view cell class to update the constraint, and then calling setNeedsLayout and layoutIfNeeded.
Here is a screenshot of the cells with the incorrect constraints:
And here is a screenshot of the cells working as intended:
Edit:
I call bind(to:) in the table view's diffable data source's cell provider:
private lazy var dataSource: FlagListDataSource = {
let source = FlagListDataSource(tableView: self.tableView, cellProvider: self.cellProvider)
source.viewModel = self.viewModel
return source
}()
private func cellProvider(tableView: UITableView, indexPath: IndexPath, countryId: Country.ID) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(FlagListTableViewCell.self, for: indexPath)
let country = self.viewModel.country(id: countryId)
cell.bind(to: country)
return cell
}
It looks like there's a conflict in how you're setting constraints for your flagImageView; you're activating a height constraint for it, but then you're also pinning all four of its edges. And you're setting the content mode to .scaleAspectFit but it seems like what you actually want is .scaleAspectFill.
Try these 2 changes in your init and see if that works.
self.cellHeightConstraint.isActive = false
self.flagImageView.contentMode = .scaleAspectFill

ReloadData with Protocol on custom cell with Collectionview?

I attempted a bunch of answers on here with no luck.
I am attempting to reload my collectionview within my custom cell after fetching data from firebase. I am unable to even get the print statement to show from the custom cell. Not sure what I'm doing wrong.
On my ViewController I have the following delegate and call, I made a button to test to see if the delegate print statement on my custom cell will appear but no luck.
protocol DiscoverControllerDelegate {
func didFetchData()
}
class DiscoverController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
let headerId = "headerId"
let database = FirebaseData.sharedInstance
var delegate: DiscoverControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
title = "Discover"
collectionView?.backgroundColor = .white
collectionView?.register(CategoriesCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.register(HeaderCell.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: headerId)
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Refresh", style: .plain, target: self, action: #selector(reloadData))
database.retrieveData {
print("fetching data")
self.delegate?.didFetchData()
}
}
#objc func reloadData () {
print("attemping to reload")
self.delegate?.didFetchData()
}
*edit to add on my override init
On my custom cell:
class CategoriesCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, DiscoverControllerDelegate {
let itemCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = .clear
return collectionView
}()
func didFetchData() {
print("RELOADING NOW!")
itemCollectionView.reloadData()
}
override init(frame: CGRect) {
super.init(frame: frame)
itemCollectionView.dataSource = self
itemCollectionView.delegate = self
itemCollectionView.register(ItemCell.self, forCellWithReuseIdentifier: cellId)
itemCollectionView.reloadData()
createLayout()
}
Many things wrong here. The reason "RELOADING NOW!" never logs is because you never set the delegate in your view controller. If you inspect self.delegate in DiscoverController it is probably nil. However, your entire setup right now seems flawed unless I'm misunderstanding what you're doing.
What does your UI look like? Why does CategoriesCell have a UICollectionView property? It's itemCollectionView property doesn't even have a datasource or delegate set and its not an #IBOutlet yet you're calling reloadData() on it. Not sure what it's even displaying or what you're tyring to do.
I feel like there is probably a lot more wrong here than just a single thing. You probably want to go through some UITableView and UICollectionView tutorials to learn about delegates and datasources in iOS.
I dont know about collection view but I use similar protocol structures with a table view. They are not much different. your protocol should be in the cell class not in the controller.
I've gone through the steps for a table view in the following post:
Firebase update
Maybe you can adapt it for your needs...

How to determine the "z-index of a shadow"

I'm sorry for the title, can not synthesize differently the issue. Here's the problem:
In my UICollectionView have some cells that want to put a shadow, they are arranged very close to each other which makes the shadow of each one reach the neighbor (first image), when what I want, is that it only reaches the background (second image).
What I've thought or tried:
I can not put a view behind the cells, adding their frames to it, and apply shadow in this view because the cells has dynamic movement (UIDynamics CollectionView Layout).
I tried, in the subclass of UICollectionViewLayout, put all these cells in the same z-index. Did not work. Find out why:
var zIndex: Int
(...) Items with the same value have an undetermined
order.
I would like some help with my problem, please. Thanks!
From the TheEye response, I decided to implement UIDecorationViews. All great now.
// MARK: At UICollectionViewCustomLayout:
public override init() {
super.init()
// Register the NIB of the view that will hold the shadows:
let nib = UINib(nibName: "Shadow", bundle: nil)
self.registerNib(nib, forDecorationViewOfKind: "shadow")
}
public override func layoutAttributesForDecorationViewOfKind(elementKind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
let layoutAtt: UICollectionViewLayoutAttributes = UICollectionViewLayoutAttributes(forDecorationViewOfKind: "shadow", withIndexPath: indexPath)
layoutAtt.frame = (layoutAttributesForItemAtIndexPath(indexPath)?.frame)!
layoutAtt.zIndex = -1
return layoutAtt
}
public override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var atts = [UICollectionViewLayoutAttributes]()
let numberOfItems:Int = (self.collectionView?.numberOfItemsInSection(0))!
for index in 0..<numberOfItems {
let layoutItem = layoutAttributesForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))!
let frameItem = layoutItem.frame
if CGRectIntersectsRect(frameItem, rect) {
atts.append(layoutAttributesForDecorationViewOfKind("shadow", atIndexPath: NSIndexPath(forItem: index, inSection: 0))!)
atts.append(layoutItem)
}
}
return atts
}
// MARK: At the Nib Class File:
// Here I created a additional view to append the shadow.
// Thats because awakeFromNib() is called before the UICollectionViewCustomLayout
// have a chance to layout it, but I want to make
// use of shadowPath to gain performance, thats why
// I make use of the additional UIView with autolayout stuffs.
#IBOutlet weak var shadowView: UIView!
override func awakeFromNib() {
super.awakeFromNib()
shadowView.layer.shadowOffset = CGSizeMake(0, 0)
shadowView.layer.shadowColor = UIColor.yellowColor().CGColor
shadowView.layer.shadowRadius = 3
shadowView.layer.shadowOpacity = 1
let shadowFrame: CGRect = shadowView.layer.bounds
let shadowPath: CGPathRef = UIBezierPath(rect: shadowFrame).CGPath
shadowView.layer.shadowPath = shadowPath
shadowView.clipsToBounds = false
self.clipsToBounds = false
}
You could try to define the shadows as supplementary views, aligned with their respective cells, and give them a lower z order than the cells.

Resources