Assume I have a user cell. I need to start a ChatViewController for this User. So, I have UICollectionView which presented as raws (1 cell = 1 row). How to segue to ChatViewController from cell?
I don't use InterfaceBuilder at all. Only through code way.
I planned to use didSelectItemAt but I don't have enough swift programming skills to figure out how to use this function for my purpose. When I try to write prepare for segue or performSegue with Identifier inside didSelectItemAt function the Xcode doesn't provide proper autocomplete. My Code is below:
import UIKit
class MyList: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
override init(frame: CGRect) {
super .init(frame: frame)
setUpLocalizedUserList()
}
//Click on User Cell
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Click")
}
//Localized User List (Collection View)
private func setUpLocalizedUserList(){
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 0)
layout.minimumLineSpacing = 0
layout.itemSize = CGSize(width: UIScreen.main.bounds.width, height: 80)
let userListFrame = CGRect(x: 0, y: 0, width: Int(UIScreen.main.bounds.width), height: Int(UIScreen.main.bounds.height) - 160)
let myCollectionView:UICollectionView = UICollectionView(frame: userListFrame, collectionViewLayout: layout)
myCollectionView.dataSource = self
myCollectionView.delegate = self
myCollectionView.register(LocalizedUserCell.self, forCellWithReuseIdentifier: "userCell")
myCollectionView.register(GroupChatCell.self, forCellWithReuseIdentifier: "groupCell")
myCollectionView.backgroundColor = UIColor.white
addSubview(myCollectionView)
}
//Number of Section in User List
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
//Lists' Sizes
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return (section == 0) ? 5 : 4
}
//Cells content here
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "userCell", for: indexPath) as! LocalizedUserCell
return cell
}else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "groupCell", for: indexPath) as! GroupChatCell
return cell
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Maybe it's important - my users cells are inside another cell.
You can't make a segue from inSide collectionViewCell as present func needs a UIViewController , see here my answer for a table cell , the same logic TableCellNavigate
Related
I am facing one issue. And I think it might be a weird one. I am using UICollectionViewController and configure it something like this
class ViewController: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
flowLayout.minimumLineSpacing = 10
flowLayout.itemSize = CGSize(width: UIScreen.main.bounds.width - 20, height: 60)
flowLayout.sectionInset = UIEdgeInsets(
top: 10, left: 10, bottom: 10, right: 10
)
collectionView.register(DemoTakesCollectionViewCell.self, forCellWithReuseIdentifier: DemoTakesCollectionViewCell.identifier)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DemoTakesCollectionViewCell.identifier,
for: indexPath) as? DemoTakesCollectionViewCell else {
fatalError("UICollectionView must be downcasted to CollectionViewCell")
}
return cell
}
}
I did also register a cell for UICollectionView, but its content not showing up somehow. In View Hierarchy, Cell is there but not it's content. What could be a problem here?
Ah... you left out an important piece of information in your question:
"It's xib"
So this line:
collectionView.register(DemoTakesCollectionViewCell.self, forCellWithReuseIdentifier: DemoTakesCollectionViewCell.identifier)
registers the class -- but the class isn't adding any UI elements.
You need to register the nib:
let nib = UINib(nibName: "DemoTakesCollectionViewCell", bundle: nil)
collectionView.register(nib, forCellWithReuseIdentifier: DemoTakesCollectionViewCell.identifier)
That should fix the issue.
EDIT: I am trying to re-create the instagram/twitter profile page. So if the current way I am trying to do it won't work, feel free to tell me how to recreate these profile pages in a totally different way.
I have set up a UICollectionView with a header. Inside this header, there is a menuBar that has two different selection options. If the user selects the first index, the menuBar returns 0. If the user selects the second index, the menuBar returns 1.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CoverCell
return cell
}
Here is a picture of the menu bar and the collectionview cells below it:
I also have this code to set up the cells:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let menuBarIndex = profileMenuBarSelectionIndex.menuBarIndexSelection
var cell: UICollectionViewCell
switch menuBarIndex {
case 0:
cell = collectionView.dequeueReusableCell(withReuseIdentifier: test1CellId, for: indexPath) as! test1Cell
case 1:
cell = collectionView.dequeueReusableCell(withReuseIdentifier: test2CellId, for: indexPath) as! test2Cell
default:
cell = collectionView.dequeueReusableCell(withReuseIdentifier: test3CellId, for: indexPath) as! test3Cell
}
return cell
}
EDIT 2: I have now added this code, where myTestAction is the delegate function of testAction in a different file.
var index: Int = 0
func testAction() {
index = index == 1 ? 0 : 1
collectionView.reloadData()
}
func myTestAction() {
delegate?.testAction()
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
switch indexPath.row{
case 0:
menuBarIndexSelection = 0
menuBarIndexChangeFlag = 1
case 1:
menuBarIndexSelection = 1
menuBarIndexChangeFlag = 2
case 2:
menuBarIndexSelection = 2
menuBarIndexChangeFlag = 3
default:
menuBarIndexSelection = 0
}
myTestAction()
}
I want to be able to display different cells below the header depending on which value menuBar returns. How would I do this?
OR
How can I recreate the Instagram profile page?
Put a check on cellForRow and supplimentaryViewsForHeader for menuBar value. And when the value of menuBar changes just reload the table. At the time of reloading the data will be shown according to your check present in dataSources.
// MENU BAR BUTTON CLICK
self.collectionView.reloadData()
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
if menuBar == 0 {
// show this image
}else{
//show that image
}
return cell
}
Try this,
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//Take a variable menuBar. when the user tap change it's value to 0 or 1 and reload collection view when user tap on menu button.
if menuBar == 0 {
return 0
}else{
return 1
}
The reason for getting the compiler error you describe is that you create 4 different variables called "cell":
var cell: UICollectionViewCell
switch menuBarIndex {
case 0:
var cell = ...
case 1:
var cell = ...
default:
var cell = ...
}
return cell
The cell instance you return at the end of the function was never assigned a value.
Assigning the first cell variable instead of creating new ones in each case should fix the compiler error:
var cell: UICollectionViewCell
switch menuBarIndex {
case 0:
cell = collectionView.dequeueReusableCell(withReuseIdentifier: test1CellId, for: indexPath) as! test1Cell
case 1:
cell = collectionView.dequeueReusableCell(withReuseIdentifier: test2CellId, for: indexPath) as! test2Cell
default:
cell = collectionView.dequeueReusableCell(withReuseIdentifier: test3CellId, for: indexPath) as! test3Cell
}
return cell
------------------------- EDIT ---------------------
The answer above fixed the compiler error but not the actual question.
I created a small sample project that does what is needed and works fine. Maybe that helps with figuring out the problem. I just created a single view application in Xcode, this is the code for the one view controller in the project and the cells:
class ViewController: UIViewController, UICollectionViewDataSource {
var index: Int = 0
var collectionView: UICollectionView?
override func viewDidLoad() {
super.viewDidLoad()
let btn = UIButton(frame: CGRect(x: 10, y: 10, width: 200, height: 50))
btn.backgroundColor = .green
btn.setTitle("Change cell color", for: .normal)
btn.setTitleColor(.black, for: .normal)
btn.addTarget(self, action: #selector(testAction), for: .touchUpInside)
view.addSubview(btn)
let layout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = 10
layout.minimumLineSpacing = 10
layout.scrollDirection = .vertical
collectionView = UICollectionView(frame: CGRect(x: 0, y: 60, width: view.frame.width, height: view.frame.height - 60), collectionViewLayout: layout)
collectionView?.backgroundColor = .white
collectionView?.dataSource = self
collectionView?.register(RedCell.self, forCellWithReuseIdentifier: "red")
collectionView?.register(BlueCell.self, forCellWithReuseIdentifier: "blue")
view.addSubview(collectionView!)
}
func testAction() {
index = index == 1 ? 0 : 1
collectionView?.reloadData()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell: UICollectionViewCell
switch index {
case 0:
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "red", for: indexPath)
case 1:
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "blue", for: indexPath)
default:
cell = UICollectionViewCell()
}
return cell
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
return CGSize(width: 50, height: 50)
}
}
class RedCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .red
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class BlueCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .blue
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I'm trying to build a collectionView that can expand multiple cells after they've been selected/tapped or collapsed when they are deselected, everything works fine when the cells remain on the screen, but once the expanded cells go off screen, I get unexpected behaviour.
For example if I select a cell with IndexPath 0 and then scroll down, tap on cell with IndexPath of 8, scroll back to cell with IndexPath 0 (it's already collapsed), I would tap on it and scroll back to the cell with IndexPath 8 and tap on it again it expands + cell with IndexPath 10 would expand too.
The CollectionView has been implemented programmatically as well the UICollectionViewCell has been subclassed.
ViewController that holds the UICollectionView:
import UIKit
class CollectionViewController: UIViewController {
// MARK: - Properties
fileprivate var collectionView: UICollectionView!
var manipulateIndex: NSIndexPath? {
didSet {
collectionView.reloadItems(at: collectionView.indexPathsForSelectedItems!)
}
}
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(CustomCell.self, forCellWithReuseIdentifier: "Cell")
collectionView.backgroundColor = UIColor.white
self.view.addSubview(collectionView)
}
}
// MARK: - UICollectionViewDataSource
extension CollectionViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 13
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CustomCell
cell.textLabel.text = "\(indexPath.item)"
cell.backgroundColor = UIColor.orange
return cell
}
}
// MARK: - UICollectionViewDelegate
extension CollectionViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
let cell = collectionView.cellForItem(at: indexPath) as! CustomCell
cell.expanded = !cell.expanded
manipulateIndex = indexPath as NSIndexPath
return false
}
}
// MARK: - UICollectionViewDelegateFlowLayout
extension CollectionViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if let cell = collectionView.cellForItem(at: indexPath) as? CustomCell {
if cell.expanded == true {
return CGSize(width: self.view.bounds.width - 20, height: 300)
}
if cell.expanded == false {
return CGSize(width: self.view.bounds.width - 20, height: 120.0)
}
}
return CGSize(width: self.view.bounds.width - 20, height: 120.0)
}
}
And the subclassed custom UICollectionViewCell:
import UIKit
class CustomCell: UICollectionViewCell {
var expanded: Bool = false
var textLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
textLabel = UILabel(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height/3))
textLabel.font = UIFont.systemFont(ofSize: UIFont.smallSystemFontSize)
textLabel.textAlignment = .center
contentView.addSubview(textLabel)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Please help and thank you so much to the amazing person, who can help me out! :)
Try this:
Example 1: Expand only one cell at a time
Note: No need to take expanded bool variable in custom cell
var section:Int?
var preSection:Int?
var expand:Bool = false
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 13
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CustomCell
cell.textLabel.text = "\(indexPath.item)"
cell.backgroundColor = UIColor.orange
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if (self.section != nil) {
self.preSection = self.section
}
self.section = indexPath.row
if self.preSection == self.section {
self.preSection = nil
self.section = nil
}else if (self.preSection != nil) {
self.expand = false
}
self.expand = !self.expand
self.collectionView.reloadItems(at: collectionView.indexPathsForSelectedItems!)
}
}
// MARK: - UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if self.expand, let row = self.section, row == indexPath.row {
return CGSize(width: self.view.bounds.width - 20, height: 300)
}else{
return CGSize(width: self.view.bounds.width - 20, height: 120.0)
}
}
}
Example 2:
Expand multiple cell
import UIKit
class ViewController: UIViewController {
// MARK: - Properties
fileprivate var collectionView: UICollectionView!
var expandSection = [Bool]()
var items = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.items = ["A","B","C","D","E","F","G","H","J","K"]
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(CustomCell.self, forCellWithReuseIdentifier: "Cell")
collectionView.backgroundColor = UIColor.white
self.expandSection = [Bool](repeating: false, count: self.items.count)
self.view.addSubview(collectionView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CustomCell
cell.textLabel.text = self.items[indexPath.row]
cell.backgroundColor = UIColor.orange
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.expandSection[indexPath.row] = !self.expandSection[indexPath.row]
self.collectionView.reloadItems(at: collectionView.indexPathsForSelectedItems!)
}
}
// MARK: - UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if self.expandSection[indexPath.row] {
return CGSize(width: self.view.bounds.width - 20, height: 300)
}else{
return CGSize(width: self.view.bounds.width - 20, height: 120.0)
}
}
}
UICollectionView will reuse your cells for multiple objects in your data model. You can't control which cells get reused when during reloadItems You should not assume that the expanded state in a given cell corresponds to the state of your data model. Instead, you should be holding onto the expanded state somehow in your data model and re-setting that in every call to cellForItemAt.
In other words, hold your state in your model and set the cell state in cellForItemAt, don't hold it in the cells themselves.
I want to show banner like this:
My approach is adding a CollectionView as a TableViewHeader
My code:
extension HomeViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func configureHeaderView() {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
let headerView = UICollectionView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: headerHeight), collectionViewLayout: layout)
headerView.backgroundColor = .blue
headerView.isPagingEnabled = true
headerView.isUserInteractionEnabled = true
headerView.dataSource = self
headerView.delegate = self
headerView.register(BannerCollectionViewCell.self, forCellWithReuseIdentifier: BannerCollectionViewCell.reuseIdentifier)
headerView.showsHorizontalScrollIndicator = false
tableView.tableHeaderView = headerView
}
// MARK: UICollectionViewDataSource
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BannerCollectionViewCell.reuseIdentifier, for: indexPath) as! BannerCollectionViewCell
return cell
}
// MARK: UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width, height: headerHeight)
}
}
My BannerCollectionViewCell has a default image.
class BannerCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var bannerImageView: UIImageView!
}
But I don't see that image on my header. It just show an empty header.
you use the NIB, so you should use func register(UINib?, forCellWithReuseIdentifier: String) instead of func register(AnyClass?, forCellWithReuseIdentifier: String)
Maybe you're looking for an image pager like KIImagePager on the top instead of a collection view. You can also create the same using inbuilt PageViewController.
You can add a TableView separately below this.
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{
}