Data Source for CollectionView inside CollectionViewCell is always nil - ios

I have a UICollectionView inside a UICollectionViewCell, and a separate NSObject that is the dataSource. I am able to set the dataSource for the external UICollectionView, but not the internal one.
Here's the cell containing the internal UICollectionView:
class FeaturedCell: UICollectionViewCell, UICollectionViewDelegate {
#IBOutlet var collectionView: UICollectionView!
let data = FeaturedData()
override init(frame: CGRect) {
super.init(frame: frame)
//setUp()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
func setUp() {
collectionView.dataSource = data
collectionView.delegate = self
collectionView.reloadData()
}
}
extension FeaturedCell {
func setCollectionViewDataSourceDelegate <D: FeaturedData> (_ dataSourceDelegate: D, forRow row: Int) {
collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
collectionView.reloadData()
print("Reload Data")
}
}
And the UIView containing the external UICollectionView:
class MainView: UIView, UICollectionViewDelegate {
#IBOutlet var collectionView: UICollectionView!
let data = MainData()
override func awakeFromNib() {
setUp()
}
func setUp() {
collectionView.dataSource = data
collectionView.delegate = self
collectionView.backgroundColor = UIColor.orange
collectionView.collectionViewLayout = createLayout()
collectionView.isPagingEnabled = true
collectionView.bounces = false
collectionView.allowsSelection = true
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
print("WillDisplay")
guard let cell: FeaturedCell = collectionView.dequeueReusableCell(withReuseIdentifier: "FeaturedCell", for: indexPath) as? FeaturedCell else {
fatalError("Unable to dequeue FeaturedCell.")
}
cell.setCollectionViewDataSourceDelegate(featuredData, forRow: indexPath.item)
}
}
Both of these methods are being called, but the dataSource and delegates are never being set. I also followed this tutorial exactly (with a UITableView, even) and it still would not set the dataSource or delegate. What am I doing wrong?

I think this will help
class FeaturedCell: UICollectionViewCell {
#IBOutlet var collectionView: UICollectionView!
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
extension FeaturedCell {
func setCollectionViewDataSourceDelegate <T: UICollectionViewDelegate , D: FeaturedData> (delegate: T dataSource: D, forRow row: Int) {
collectionView.delegate = delegate
collectionView.dataSource = dataSource
collectionView.reloadData()
print("Reload Data")
}
}
and in the MainView
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
print("WillDisplay")
guard let cell: FeaturedCell = collectionView.dequeueReusableCell(withReuseIdentifier: "FeaturedCell", for: indexPath) as? FeaturedCell else {
fatalError("Unable to dequeue FeaturedCell.")
}
cell.setCollectionViewDataSourceDelegate(self, featuredData, forRow: indexPath.item)
}
And for the tutorial you followed this a Github link for the tutorial, compare your code with that and see where did you missed out.
Hope this will helps.

Solved it. The problem was that this:
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
print("WillDisplay")
guard let cell: FeaturedCell = collectionView.dequeueReusableCell(withReuseIdentifier: "FeaturedCell", for: indexPath) as? FeaturedCell else {
fatalError("Unable to dequeue FeaturedCell.")
}
cell.setCollectionViewDataSourceDelegate(featuredData, forRow: indexPath.item)
}
Needed to be changed to this:
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
print("WillDisplay")
guard let featuredCell = cell as? FeaturedCell else { return }
featuredCell.setCollectionViewDataSourceDelegate(featuredData, forRow: indexPath.item)
}

Related

UICollectionView subclass with internal delegate doesn't get triggered when external delegate hasn't implemented the same method

I'm working on a 3rd party framework for swift so i cannot use the delegate methods of UICollectionViewDelegate myself but I do need them for some custom logic.
Tried multiple approaches to make it work, including method swizzling but in the end I felt like it was too hacky for what i'm doing.
Now i'm subclassing UICollectionView and setting the delegate to an internal (my) delegate.
This works well except for when the UIViewController hasn't implemented the method.
right now my code looks like this:
fileprivate class UICollectionViewDelegateInternal: NSObject, UICollectionViewDelegate {
var userDelegate: UICollectionViewDelegate?
override func responds(to aSelector: Selector!) -> Bool {
return super.responds(to: aSelector) || userDelegate?.responds(to: aSelector) == true
}
override func forwardingTarget(for aSelector: Selector!) -> Any? {
if userDelegate?.responds(to: aSelector) == true {
return userDelegate
}
return super.forwardingTarget(for: aSelector)
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let collection = collectionView as! CustomCollectionView
collection.didEnd(item: indexPath.item)
userDelegate?.collectionView?(collectionView, didEndDisplaying: cell, forItemAt: indexPath)
}
}
class CustomCollectionView: UICollectionView {
private let internalDelegate: UICollectionViewDelegateInternal = UICollectionViewDelegateInternal()
required init?(coder: NSCoder) {
super.init(coder: coder)
super.delegate = internalDelegate
}
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
super.delegate = internalDelegate
}
func didEnd(item: Int) {
print("internal - didEndDisplaying: \(item)")
}
override var delegate: UICollectionViewDelegate? {
get {
return internalDelegate.userDelegate
}
set {
self.internalDelegate.userDelegate = newValue
super.delegate = nil
super.delegate = self.internalDelegate
}
}
}
In the ViewController I just have a simple set up with the delegate method for didEndDisplaying not implemented
Is it possible to listen to didEndDisplaying without the ViewController having it implemented?
Edit 1:
Here's the code of the ViewController to make it a little more clear what i'm doing
class ViewController: UIViewController {
#IBOutlet weak var collectionView: CustomCollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
1000
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.backgroundColor = .blue
return cell
}
// func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
// print("view controller - did end displaying: \(indexPath.item)")
// }
}
the didEndDisplaying of CustomCollectionView is only triggered when i uncomment the didEndDisplaying method in the ViewController.
what i'm looking for is to have the didEndDisplaying of CustomCollectionView also triggered if the didEndDisplaying method in the ViewController is NOT implemented.
hope it's a little more clear now
Edit 2:
Figured out that the code above had some mistakes which made the reproduction not work as I intended. updated the code above.
also made a github page to make it easier to reproduce here:
https://github.com/mees-vdb/InternalCollectionView-Delegate
I did a little reading-up on this approach, and it seems like it should work - but, obviously, it doesn't.
Played around a little bit, and this might be a solution for you.
I made very few changes to your existing code (grabbed it from GitHub - if you want to add me as a Collaborator I can push a new branch [same DonMag ID on GitHub]).
First, I implemented didSelectItemAt to make it a little easier to debug (only one event at a time).
ViewController class
class ViewController: UIViewController {
#IBOutlet weak var collectionView: CustomCollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
1000
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.backgroundColor = .blue
return cell
}
// DonMag - comment / un-comment these methods
// to see the difference
//func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
// print("view controller - did end displaying: \(indexPath.item)")
//}
//func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// print("view controller - didSelectItemAt", indexPath)
//}
}
UICollectionViewDelegateInternal class
fileprivate class UICollectionViewDelegateInternal: NSObject, UICollectionViewDelegate {
var userDelegate: UICollectionViewDelegate?
override func responds(to aSelector: Selector!) -> Bool {
return super.responds(to: aSelector) || userDelegate?.responds(to: aSelector) == true
}
override func forwardingTarget(for aSelector: Selector!) -> Any? {
if userDelegate?.responds(to: aSelector) == true {
return userDelegate
}
return super.forwardingTarget(for: aSelector)
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let collection = collectionView as! CustomCollectionView
collection.didEnd(item: indexPath.item)
userDelegate?.collectionView?(collectionView, didEndDisplaying: cell, forItemAt: indexPath)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let collection = collectionView as! CustomCollectionView
collection.didSel(p: indexPath)
userDelegate?.collectionView?(collectionView, didSelectItemAt: indexPath)
}
}
CustomCollectionView class
// DonMag - conform to UICollectionViewDelegate
class CustomCollectionView: UICollectionView, UICollectionViewDelegate {
private let internalDelegate: UICollectionViewDelegateInternal = UICollectionViewDelegateInternal()
required init?(coder: NSCoder) {
super.init(coder: coder)
super.delegate = internalDelegate
}
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
super.delegate = internalDelegate
}
func didEnd(item: Int) {
print("internal - didEndDisplaying: \(item)")
}
func didSel(p: IndexPath) {
print("internal - didSelectItemAt", p)
}
// DonMag - these will NEVER be called,
// whether or not they're implemented in
// UICollectionViewDelegateInternal and/or ViewController
// but, when implemented here,
// it allows (enables?) them to be called in UICollectionViewDelegateInternal
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
print("CustomCollectionView - didEndDisplaying", indexPath)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("CustomCollectionView - didSelectItemAt", indexPath)
}
override var delegate: UICollectionViewDelegate? {
get {
// DonMag - return self instead of internalDelegate.userDelegate
return self
//return internalDelegate.userDelegate
}
set {
self.internalDelegate.userDelegate = newValue
super.delegate = nil
super.delegate = self.internalDelegate
}
}
}

Why is my UICollectionViewCell not getting data from UICollectionViewController?

I have a UICollectionViewController called SwipingController that creates a TeamCell and gives it a teamName of "Boston Celtics"
class SwipingController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = .white
collectionView?.register(TeamCell.self, forCellWithReuseIdentifier: "cellId")
collectionView?.isPagingEnabled = true
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! TeamCell
cell.teamName = "Boston Celtics"
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
}
However, when I run the code, teamName still prints out as an empty string.
class TeamCell: UICollectionViewCell {
var teamName: String = ""
override init(frame: CGRect) {
super.init(frame: frame)
print(teamName) //this prints nothing
}
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! TeamCell
it inits the cell, and so initially it is empty.
After initialization cell.teamName = "Boston Celtics" is called, actually having no effect
you have to update the name
class TeamCell: UICollectionViewCell {
var teamName: String = ""
override init(frame: CGRect) {
super.init(frame: frame)
print(teamName) //this prints nothing
}
func updateName(name : String) {
self.testName = name
}
}
in collectionview
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! TeamCell
cell.updateName(name : "Boston Celtics")
return cell
}
You are simply looking at teamName too early in the process. By the time cellForItemAt hits the cell.teamName = ... line, the cell’s init method has already been called. So, teamName will always be blank during the init method. But, teamName will be set before the cell appears within the view.
Often cells would just have UIKit controls, such as a UILabel, and you’d set the text of that, e.g.
class TeamCell: UICollectionViewCell {
var teamLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure() {
addSubview(teamLabel)
NSLayoutConstraint.activate([
teamLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
teamLabel.centerYAnchor.constraint(equalTo: centerYAnchor)
])
}
func update(for team: String) {
teamLabel.text = team
}
}
And then
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! TeamCell
cell.update(for: "Boston Celtics")
return cell
}
Please try this :
class TeamCell: UICollectionViewCell {
var teamName : String = "" {
didSet {
print(teamName)
}
}
override func awakeFromNib() {
super.awakeFromNib()
//custom logic goes here
}
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! TeamCell
cell.teamName = "Boston Celtics"
return cell
}
OR
class TeamCell: UICollectionViewCell {
override func awakeFromNib() {
super.awakeFromNib()
//custom logic goes here
}
func getTeamName(teamName : String)
{
print(teamName)
}
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! TeamCell
cell.getTeamName(teamName: "Boston Celtics")
return cell
}
in TeamCell class try this
var teamcell = String()
override init(frame: CGRect) {
super.init(frame: frame)
if teamcell != nil {
print(teamName)
}
}
It’s just a matter of order of events. The cell is created, you print, the cell is used in the table view, the team name is assigned.

Creating UICollectionView programmatically (outside of UIViewController)

I would like to create a subclass ofimport UIKit
class AlbumPlayerProgressBar: UICollectionView to later use it in xib. And can't seem to figure out how to properly use init functions
import UIKit
class AlbumPlayerProgressBar: UICollectionView, UICollectionViewDataSource {
var progressBarType :ProgressBarType = .Player
var numOfSlides: Int = 0
var numOfPlayingSlide: Int = 0
init()
{
super.init()
self.register(UINib(nibName: NSStringFromClass(ProgressBarCell.self), bundle: nil), forCellWithReuseIdentifier: NSStringFromClass(ProgressBarCell.self))
}
func set(progressBarType :ProgressBarType, numOfSlides: Int, numOfPlayingSlide: Int) {
self.progressBarType = progressBarType
self.numOfSlides = numOfSlides
self.numOfPlayingSlide = numOfPlayingSlide
self.reloadData()
}
required init?(coder aDecoder: NSCoder) {
fatalError("AlbumPlayerProgressBar init(coder:) has not been implemented")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numOfSlides
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(ProgressBarCell.self), for: indexPath) as! ProgressBarCell
cell.setType(self.progressBarType)
if (indexPath.row < numOfPlayingSlide)
{
cell.setPlayed()
}
else if (indexPath.row == numOfPlayingSlide)
{
cell.setPlaying()
}
else
{
cell.setUnplayed()
}
return cell
}
}
enum ProgressBarType {
case Player
case Thumnail
}
You can declare your CollectionView as
class AlbumPlayerProgressBar: UICollectionView, UICollectionViewDataSource {
var progressBarType :ProgressBarType = .Player
var numOfSlides: Int = 0
var numOfPlayingSlide: Int = 0
override func awakeFromNib() {
super.awakeFromNib()
//gets called when you instantiate your collectionView from xib
self.register(UINib(nibName: NSStringFromClass(ProgressBarCell.self), bundle: nil), forCellWithReuseIdentifier: NSStringFromClass(ProgressBarCell.self))
self.dataSource = self
}
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
self.register(UINib(nibName: NSStringFromClass(ProgressBarCell.self), bundle: nil), forCellWithReuseIdentifier: NSStringFromClass(ProgressBarCell.self))
self.dataSource = self
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.register(UINib(nibName: NSStringFromClass(ProgressBarCell.self), bundle: nil), forCellWithReuseIdentifier: NSStringFromClass(ProgressBarCell.self))
self.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numOfSlides
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(ProgressBarCell.self), for: indexPath) as! ProgressBarCell
cell.setType(self.progressBarType)
if (indexPath.row < numOfPlayingSlide)
{
cell.setPlayed()
}
else if (indexPath.row == numOfPlayingSlide)
{
cell.setPlaying()
}
else
{
cell.setUnplayed()
}
return cell
}
}
Depending on how would you initialize/instantiate collectionView decide where would like to register your nib and setting data source as self.
Hope it helps

iOS swift custom UICollectionView with Custom Cell does not call didSelectItem

I got some issues with the following code: I created a custom Cell with a Custom UICollectionView class. I used Xib files instead of the storyboard.
It never calls the didSelectItemAtIndexPath method but why? I am really desperate :'(.
import UIKit
class CustomCollectionView: UICollectionView, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var refreshControl : UIRefreshControl = UIRefreshControl()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.delegate = self
self.dataSource = self
var flowLayout:UICollectionViewFlowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = UICollectionViewScrollDirection.Vertical
self.collectionViewLayout = flowLayout
self.refreshControl.addTarget(self, action: "refreshData:", forControlEvents: UIControlEvents.ValueChanged)
self.addSubview(refreshControl)
self.userInteractionEnabled = true
self.registerClass(CustomCollectionViewCell.self, forCellWithReuseIdentifier: "outfit")
self.registerNib(UINib(nibName: layoutHelper.getCellXibName("CustomCollectionViewCell"), bundle: nil), forCellWithReuseIdentifier: "outfit")
self.backgroundColor = UIColor.whiteColor()
}
//
// MARK: UICollectionViewDataSource - Methods
//
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell : CustomCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("outfit", forIndexPath: indexPath) as! CustomCollectionViewCell
cell.label.text = "Outfit #" + String(indexPath.row)
let url = NSURL(string: "http://placehold.it/177x370")
cell.imageView.sd_setImageWithURL(url, completed: nil)
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
println("at index path %i", indexPath.row)
}

UICollectionView in a UICollectionViewCell

I am interested in having a collectionview as a part of a collection view cell but for some reason cannot figure out how this would be done. Where would I implement the necessary methods for the cells collectionview?
There's an article that Ash Furrow wrote that explains how to put an UICollectionView inside an UITableViewCell. It's basically the same idea when using it inside an UICollectionViewCell.
Everything is done programatically. No storyboards.
I added a UICollectionView inside my UICollectionViewCell. I also show how to add again a UICollectionViewCell inside the created UICollectionView to have this result
import UIKit
class CategoryCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
private let cellId = "cell"
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let appsCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
return collectionView
}()
func setupViews() {
backgroundColor = .blue
addSubview(appsCollectionView)
appsCollectionView.delegate = self
appsCollectionView.dataSource = self
appsCollectionView.register(AppCell.self, forCellWithReuseIdentifier: cellId)
addConstrainstWithFormat("H:|-8-[v0]-8-|", views: appsCollectionView)
addConstrainstWithFormat("V:|[v0]|", views: appsCollectionView)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
return cell
}
}
class AppCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews(){
backgroundColor = .red
}
}
My UICollectionViewController
import UIKit
class FeaturedAppsController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let cellId = "cell"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
collectionView?.backgroundColor = .white
collectionView?.register(CategoryCell.self, forCellWithReuseIdentifier: cellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(view.frame.width, 150)
}
}
The whole explanation can be found and was developed by "Let´s build that app": https://www.youtube.com/watch?v=Ko9oNhlTwH0&list=PL0dzCUj1L5JEXct3-OV6itP7Kz3tRDmma
This is too late for this answer but it might help others. This is an example of UICollectionView inside a UICollectionViewCell.
Lets start by having a mainCollectionView. Then on each cell of this collection create and initialize a new UICollectionView and right place to do that is in this following delegate of UICollectionView
func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath)
e.g I initialize the MainCollectionViewCell here and then MainCollectionViewCell handles the logic to create a new UICollectionView
guard let collectionViewCell = cell as? MainCollectionViewCell else { return }
collectionViewCell.delegate = self
let dataProvider = ChildCollectionViewDataSource()
dataProvider.data = data[indexPath.row] as NSArray
let delegate = ChildCollectionViewDelegate()
collectionViewCell.initializeCollectionViewWithDataSource(dataProvider, delegate: delegate, forRow: indexPath.row)
collectionViewCell.collectionViewOffset = storedOffsets[indexPath.row] ?? 0
Here is the initializer on MainCollectionViewCell that creates a new UICollectionView
func initializeCollectionViewWithDataSource<D: protocol<UICollectionViewDataSource>,E: protocol<UICollectionViewDelegate>>(dataSource: D, delegate :E, forRow row: Int) {
self.collectionViewDataSource = dataSource
self.collectionViewDelegate = delegate
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .Horizontal
let collectionView = UICollectionView(frame: self.bounds, collectionViewLayout: flowLayout)
collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseChildCollectionViewCellIdentifier)
collectionView.backgroundColor = UIColor.whiteColor()
collectionView.dataSource = self.collectionViewDataSource
collectionView.delegate = self.collectionViewDelegate
collectionView.tag = row
self.addSubview(collectionView)
self.collectionView = collectionView
collectionView.reloadData()
}
Hope that helps !!
I did an example for this and put in on github. It demonstrates the use UICollectionView inside a UICollectionViewCell.
https://github.com/irfanlone/Collection-View-in-a-collection-view-cell
Easiest solution for collectionview inside collectionview using storyboard and Swift 5
Please refer this link for nested collectionview example
import UIKit
class ParentViewController:UIViewController,UICollectionViewDataSource,UICollectionViewDelegate {
//MARK: Declare variable
//MARK: Decalare outlet
#IBOutlet weak var outerCollectionView: UICollectionView!
#IBOutlet weak var pageControl: UIPageControl!
let outerCount = 4
//MARK: Decalare life cycle methods
override func viewDidLoad() {
super.viewDidLoad()
pageControl.numberOfPages = outerCount
}
//MARK: Collection View delegate methods
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return outerCount
}
//MARK: Collection View datasource methods
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OuterCell", for: indexPath) as! OuterCollectionViewCell
cell.contentView.backgroundColor = .none
return cell
}
//MARK:- For Display the page number in page controll of collection view Cell
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let visibleRect = CGRect(origin: self.outerCollectionView.contentOffset, size: self.outerCollectionView.bounds.size)
let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
if let visibleIndexPath = self.outerCollectionView.indexPathForItem(at: visiblePoint) {
self.pageControl.currentPage = visibleIndexPath.row
}
}
}
class OuterCollectionViewCell: UICollectionViewCell ,UICollectionViewDataSource,UICollectionViewDelegate {
#IBOutlet weak var InnerCollectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
InnerCollectionView.delegate = self
InnerCollectionView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
6
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "InnerCell", for: indexPath) as! InnerCollectionViewCell
cell.contentView.backgroundColor = .green
return cell
}
}
class InnerCollectionViewCell: UICollectionViewCell{
}

Resources