I tried many days to realise this:
I want to add in my UIViewController two different CollectionView.
For example I want to put images in these collectionView
Each CollectionView use its own images.
Is this possible?
I will be very happy if somebody can give me a hand. :)
This is possible, you just need to add each UICollectionView as a subview, and set the delegate and dataSource to your UIViewController.
Here's a quick example. Assuming you have one UICollectionView working, you should be able to adapt this code to your own uses to add a second fairly easily:
let collectionViewA = UICollectionView()
let collectionViewB = UICollectionView()
let collectionViewAIdentifier = "CollectionViewACell"
let collectionViewBIdentifier = "CollectionViewBCell"
override func viewDidLoad() {
// Initialize the collection views, set the desired frames
collectionViewA.delegate = self
collectionViewB.delegate = self
collectionViewA.dataSource = self
collectionViewB.dataSource = self
self.view.addSubview(collectionViewA)
self.view.addSubview(collectionViewB)
}
In the cellForItemAtIndexPath delegate function:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
if collectionView == self.collectionViewA {
let cellA = collectionView.dequeueReusableCellWithReuseIdentifier(collectionViewAIdentifier) as UICollectionViewCell
// Set up cell
return cellA
}
else {
let cellB = collectionView.dequeueReusableCellWithReuseIdentifier(collectionViewBIdentifier) as UICollectionViewCell
// ...Set up cell
return cellB
}
}
In the numberOfItemsInSection function:
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.collectionViewA {
return 0 // Replace with count of your data for collectionViewA
}
return 0 // Replace with count of your data for collectionViewB
}
Yes--this is entirely possible. You can either assign their respective UICollectionViewDelegates/UICollectionViewDataSources to different classes or subclass the CollectionViews, assigning both the delegate and data source to your current viewController and downcast your reference to collectionView in the delegation methods like so:
#IBOutlet collectionViewA: CustomCollectionViewA!
#IBOutlet collectionViewB: CustomCollectionViewB!
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
if let a = collectionView as? CustomCollectionViewA {
return a.dequeueReusableCellWithIdentifier("reuseIdentifierA", forIndexPath: indexPath)
} else {
return collectionView.dequeueReusableCellWithIdentifier("reuseIdentifierB", forIndexPath: indexPath)
}
}
Subclass UICollectionView like this:
class CustomCollectionViewA: UICollectionView {
// add more subclass code as needed
}
class CustomCollectionViewB: UICollectionView {
// add more subclass code as needed
}
You can use the factory design pattern to build two different collection views and return them via functions. Here's my working version for swift 4.
This code goes in a separate helper file:
import UIKit
class collectionViews {
static func collectionViewOne() -> UICollectionView {
let layout = UICollectionViewFlowLayout()
let collectionViewOne = UICollectionView(frame: CGRect(x: 0, y: 20, width: 200, height: 100), collectionViewLayout: layout)
return collectionViewOne
}
static func collectionViewTwo() -> UICollectionView {
let layout = UICollectionViewFlowLayout()
let collectionViewTwo = UICollectionView(frame: CGRect(x: 0, y: 300, width: 200, height: 100), collectionViewLayout: layout)
return collectionViewTwo
}
}
And here is the view controller code:
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
let collectionViewOne = collectionViews.collectionViewOne()
let collectionViewTwo = collectionViews.collectionViewTwo()
var myArray = ["1", "2"]
var myArray2 = ["3", "4"]
override func viewDidLoad() {
super.viewDidLoad()
collectionViewOne.delegate = self
collectionViewOne.dataSource = self
collectionViewOne.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "MyCell")
view.addSubview(collectionViewOne)
collectionViewTwo.delegate = self
collectionViewTwo.dataSource = self
collectionViewTwo.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "MyCell2")
view.addSubview(collectionViewTwo)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.collectionViewOne {
return myArray.count
} else {
return myArray2.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.collectionViewOne {
let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath as IndexPath)
myCell.backgroundColor = UIColor.red
return myCell
} else {
let myCell2 = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell2", for: indexPath as IndexPath)
myCell2.backgroundColor = UIColor.blue
return myCell2
}
}
}
Result
You can also name the collection views outlets differently (without subclassing):
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var SecondCollectioView: UICollectionView!
method:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCell", for: indexPath) as UICollectionViewCell
if(collectionView == self.SecondCollectioView) {
cell.backgroundColor = UIColor.black
} else {
cell.backgroundColor = self.randomColor()
}
return cell;
}
This is will be an another way.
Here's my working version for swift 5 and Xcode 11:
create outlets for corresponding collectionviews: outlets:
#IBOutlet weak var bgCollectionView: UICollectionView!
#IBOutlet weak var frontCollectionView: UICollectionView!
var arrImages = [String : [UIImage]]()
arrImages is contain like
override func viewDidLoad() {
super.viewDidLoad()
arrImages = [
"frontImg": [//Front UIImage array],
"bgImg": [//Background UIImage array]
]
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let arrImg = arrImages["bgImg"] {
return arrImg.count
} else if let arrImg = arrImages["frontImg"]{
return arrImg.count
}
return 0
}
You can do this two ways
Using CollectionView Outlets
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
if collectionView == self.bgCollectionView{
if let arrImg = arrImages["bgImg"]{
cell.imgView.image = arrImg[indexPath.row]
}
}else{
if let arrImg = arrImages["frontImg"]{
cell.imgView.image = arrImg[indexPath.row]
}
}
return cell
}
Using CollectionView Tag:
Here Background Images collectionview tag is 1 and Front Images collectionview tag is 2.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
if collectionView == collectionView.viewWithTag(1){
if let arrImg = arrImages["bgImg"]{
cell.imgView.image = arrImg[indexPath.row]
}
}else{
if let arrImg = arrImages["frontImg"]{
cell.imgView.image = arrImg[indexPath.row]
}
}
return cell
}
Please Add Tag in CollectionView Like this:
Thank You. Hope It's working for you !!
Swift 5 Answer!
If you try connecting both collectionViews to the same view controller Xcode will throw an error "Outlets cannot connect to repeating content"
Solution:
Head to Storyboard
Connect the first collectionView via outlet, set the delegate/dataSource in viewDidLoad and then add a tag to the second collectionView by heading to the attributes inspector in storyboard and change the value from 0 to 1
Select the secondCollectionView and go to the connections inspector and select delegate and drag the connection to the UIViewController and the same for the dataSource.
Simply check which collectionView is passing through.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == collectionView.viewWithTag(1) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "secondCollectionView", for: indexPath)
return cell
}
else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "firstCollectionView", for: indexPath) as! HomeMainCollectionViewCell
cell.configureCell()
return cell}
}
Related
I'm making a Calendar in Swift 5 and now I'm encountering a problem.
First this is my code:
import UIKit
class YearViewController: UIViewController {
#IBOutlet weak var yearName: UILabel!
#IBOutlet weak var yearCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
yearName.text = String(CalenderBrain.init().curruntYear)
yearCollectionView.dataSource = self
yearCollectionView.delegate = self
yearCollectionView.register(UINib(nibName: "ReusableYearCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "MyCell3")
if let layout = self.yearCollectionView.collectionViewLayout as? UICollectionViewFlowLayout{
layout.minimumInteritemSpacing = -10
layout.minimumLineSpacing = 1
}
}
}
extension YearViewController : UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell3", for: indexPath) as! ReusableYearCollectionViewCell
cell.monthName.text = Calendar.current.monthSymbols[indexPath.row]
if cell.monthName.text == CalenderBrain.init().curruntMonthName(){
cell.monthName.textColor = .red
cell.tag = 999
}
cell.month = indexPath.row + 1
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return .init(width: 126.3, height:(570-3)/4)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "lastOne") as? ViewController
vc?.whatIGetFromYearViewController = indexPath.row + 1
vc?.curruntMonthNameThatIHaveToPut = Calendar.current.monthSymbols[indexPath.row]
vc?.nextMonthNameThatIHaveToPut = Calendar.current.monthSymbols[indexPath.row + 1]
self.navigationController?.pushViewController(vc!, animated: true)
print("i'm pressed")
}
}
I want if my cells are clicked then a segue occurs.
And this is an image of my cell. (I make collectionView in collectionViewCell.)
The problem is the segue only occurs when I click the left part of the label. If I click collectionView in CollectionViewCell then the segue doesn't occur. I think that's because Swift recognizes collectionView in collectionViewCell another CollectionView. So for now, I want to make collectionViewCell clicked and segue occurred regardless of part that I click. Is that possible?
Try set collectionView.isUserInteractionEnable = false in collectionViewCell.
This code will prevent user action onto collectionView in collectionViewCell, just receive didSelectItem action of main collectionView.
I want to show cells in collection view like in circular list, means that after the last cell of the collection view, on scrolling the collection view shows the first cell again, like circular linklist
I have tried using icrousel, but as icarosuel deals with views only, I don't want to finish the collection view completely and start again with icarousel, so is there any way I can make me collection view circular
this is my collectionView code
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell =
collectionView.dequeueReusableCell(withReuseIdentifier:
"CellName", for: indexPath as IndexPath) as! CellName
let gigModel = self.datasoruce?[indexPath.row]
cell.lblTitle.text = gigModel?.title
cell.btnPrice.setTitle(gigModel?.getPriceAccordingToGigType(),
for: .normal)
cell.itemImageView.sd_setImage(with: URL(string:
(gigModel?.getPhotoPath())!), placeholderImage:
UIImage.init(named: "place_holder"))
cell.itemImageView.layer.cornerRadius = 10.0
if Utilities.isValidString(object: gigModel?.adminId as
AnyObject) {
cell.btnStar.isHidden = false
}
else {
cell.btnStar.isHidden = true
}
return cell
}
and I want this to be circular list.
I tried to create sample project and it was pretty simple, here is example code how you can implement "infinite" scroll
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var array: [Any] = [0,1,2,3,4,5,6,7,8,9]
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Int(Int16.max) // Int.max cause crash
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCell
let correctIndex = indexPath.row >= array.count ? indexPath.row % array.count : indexPath.row
cell.nameLabel.text = "\(array[correctIndex])"
return cell
}
}
Hope it will help you
//Use Below code to get next cell.
func scrollToNextCell(){
//get Collection View Instance
let collectionView:UICollectionView;
//get cell size
let cellSize = CGSizeMake(self.view.frame.width, self.view.frame.height);
//get current content Offset of the Collection view
let contentOffset = collectionView.contentOffset;
//scroll to next cell
collectionView.scrollRectToVisible(CGRectMake(contentOffset.x + cellSize.width, contentOffset.y, cellSize.width, cellSize.height), animated: true);
}
I have two collection Views in One View Controller, First Collection View have class CollectionCellA with imageA as UIImageView! and labelA as UILabel!. Similarly Second Collection View with class CollectionCellB with imageB and labelB. I tried to run with following swift code but it show just blank(white) screen.
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var imageArroy = [UIImage(named: "1"), UIImage(named: "2"), UIImage(named: "3"), UIImage(named: "5"), UIImage(named: "6")]
var imageArroyB = [UIImage(named: "a"), UIImage(named: "b"), UIImage(named: "c"), UIImage(named: "d"), UIImage(named: "e")]
var labelA: ["Electronics", "Cars", "Pets", "Mobiles", "Food"]
var labelB: ["UK", "Ireland", "India", "Germany", "Other EU"]
#IBOutlet weak var CollectionViewA: UICollectionView!
#IBOutlet weak var CollectionViewB: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
CollectionViewA.delegate = self
CollectionViewB.delegate = self
CollectionViewA.dataSource = self
CollectionViewB.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.CollectionViewA {
return 0 // Replace with count of your data for collectionViewA
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.CollectionViewA {
let cellA = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCellA", for: indexPath) as! CollectionCellA
// Set up cell
return cellA
}
else {
let cellB = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCellB", for: indexPath) as! CollectionCellB
// ...Set up cell
return cellB
}
}
This code works for Two Collection View in One View Controller with images and label.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.CollectionViewA {
return imageArroy.count
}
return imageArroyB.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.CollectionViewA {
let cellA = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCellA", for: indexPath) as! CollectionCellA
cellA.imageA.image = imageArroyB[indexPath.row]
cellA.labelA.text = labelA[indexPath.row]
// Set up cell
return cellA
}
else {
let cellB = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCellB", for: indexPath) as! CollectionCellB
cellB.imageB.image = imageArroyB[indexPath.row]
cellB.labelB.text = labelB[indexPath.row]
// ...Set up cell
return cellB
}
}
For me below code works like a charm . you should send/assign value in cellForItemAt indexPath
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.CollectionViewA {
return imageArroy.count
}
return imageArroyB.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.CollectionViewA {
let cellA = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCellA", for: indexPath) as! CollectionCellA
// Set up cell
cellA.lbl.text = labelA[indexPath.row]
return cellA
}
else {
let cellB = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCellB", for: indexPath) as! CollectionCellB
// ...Set up cell
cellB.lbl.text = labelB[indexPath.row]
return cellB
}
}
}
class CollectionCellA : UICollectionViewCell {
#IBOutlet weak var lbl: UILabel!
}
class CollectionCellB : UICollectionViewCell {
#IBOutlet weak var lbl: UILabel!
}
Add two collection view and connect delegate and data source with view countroller,create collection view cell and connect with cell and outlets
#IBOutlet weak var collectionView2: UICollectionView!
#IBOutlet weak var collectionview1: UICollectionView!
var days = ["Sun","Mon","Tues","Wed","Thur","Frid","Sat"]
var dayTask = [String]()
var task = [["Sun1","Mon1","Tues1","Wed1","Thur1","Frid1","Sat1"],["Sun2","Mon2","Tue2","Wed2"],["Sun3","Mon3","Tues3","Wed3","Thur3","Frid3","Sat3"],["Sun4","Mon4"],["Sun5","Mon5","Tues5","Wed5","Thur5","Frid5","Sat5"],["Sun6","Mon6","Tues6","Wed6"],["Sun7","Mon7","Tues7","Wed7","Thur7"]]
override func viewDidLoad() {
super.viewDidLoad()
dayTask = task[0]
// Do any additional setup after loading the view.
}
Collection view data source and delgate
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == collectionview1{
return days.count
}else{
return dayTask.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell1", for: indexPath) as! CollectionViewCell1
if collectionView == collectionview1{
cell.label.text = days[indexPath.row]
let collectionViewLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
collectionViewLayout?.sectionInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 40)
collectionViewLayout?.invalidateLayout()
}else
{
let collectionViewLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
collectionViewLayout?.sectionInset = UIEdgeInsets(top: 10, left: 20, bottom: 0, right: 40)
collectionViewLayout?.invalidateLayout()
cell.label.text = dayTask[indexPath.row]
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == collectionview1{
dayTask = task[indexPath.row]
collectionView2.reloadData()
}
}
I have a horizontally scrollable UICollectionView with three cells each of which are different subclasses of UICollectionViewCell. Each one of these cells contains a UITableView.
Inside of the first two cells, my table view cells are the same subclasses of UITableViewCell and have just a UIImageView. I use it to set its backgroundColor. Inside of the third cell, my table view's cells are different subclasses of UITableViewCell than in the previous two. They have both a UILabel and a UIImageView. The label has some dummy text, and I set imageView's backgroundColor to some color, again.
In order to follow MVC pattern, I use my UIViewController as a data source and a delegate for both collection view, and table view. Here is the code of UIViewController:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let collectionViewCellId = "collectionViewCell"
let tableViewCellId = "tableViewCell"
let collectionViewCellId2 = "collectionViewCellId2"
let collectionViewCellId3 = "collectionViewCellId3"
let tableViewCellDif = "tableViewCellDif"
var collectionViewIndex: Int?
#IBOutlet weak var collectionView: UICollectionView! {
didSet {
collectionView.delegate = self
collectionView.dataSource = self
collectionView.isPagingEnabled = true
}
}
//MARK: UITableViewDataSource
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let colors: [UIColor] = [.red, .green, .purple, .orange, .blue]
let colors2: [UIColor] = [.blue, .brown, .yellow, .magenta, .cyan]
if collectionViewIndex == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellId, for: indexPath) as! TableViewCell
cell.colorForImageView = colors[indexPath.row]
return cell
} else
if collectionViewIndex == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellId, for: indexPath) as! TableViewCell
cell.colorForImageView = colors2[indexPath.row]
return cell
} else
if collectionViewIndex == 2 {
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellDif, for: indexPath) as! TableViewCellDifferent
cell.colorForImageView = colors2[indexPath.row]
return cell
} else {
return UITableViewCell()
}
}
}
//MARK: UICollectionViewDataSource
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let identifier: String
if indexPath.item == 0 {
identifier = collectionViewCellId
} else if indexPath.item == 1 {
identifier = collectionViewCellId2
} else if indexPath.item == 2 {
identifier = collectionViewCellId3
} else {
identifier = ""
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
return cell
}
}
//MARK: UICollectionViewDelegate
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.item == 0 {
let cell = cell as! CollectionViewCell
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 0
}
if indexPath.item == 1 {
let cell = cell as! CollectionViewCell2
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 1
}
if indexPath.item == 2 {
let cell = cell as! CollectionViewCell3
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 2
print (collectionViewIndex)
}
}
}
//MARK: UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let layout = collectionViewLayout as! UICollectionViewFlowLayout
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
}
}
As I stated in a title of the question, nothing happens on a background thread. I, basically, only set the backgroundColor of table view's cells.
The problem is that inside the collection view's third cell (and only inside of there), my table view dequeues its cells only after a minor scroll or tap happens. Here is how it looks like:
I can't figure out why this happens. Maybe, this happens because inside of the third cell of the collection view, my table view's cells are instances of different subclass than inside of the first two?
EDITED
I could solve the problem by reloading the table view before before showing the collection view's each cell but I'm not sure that this is the most efficient solution. Here is the code:
//MARK: UICollectionViewDelegate
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.item == 0 {
let cell = cell as! CollectionViewCell
cell.tableView.dataSource = self
cell.tableView.delegate = self
cell.tableView.reloadData()
collectionViewIndex = 0
}
if indexPath.item == 1 {
let cell = cell as! CollectionViewCell2
cell.tableView.dataSource = self
cell.tableView.delegate = self
cell.tableView.reloadData()
collectionViewIndex = 1
}
if indexPath.item == 2 {
let cell = cell as! CollectionViewCell3
cell.tableView.dataSource = self
cell.tableView.delegate = self
cell.tableView.reloadData()
collectionViewIndex = 2
}
}
}
If you know a better way, I would appreciate your help.
I gave this a try, and saw the same results. So, I moved your collection view cell "setup" code from willDisplay cell: to cellForItemAt and it fixed the problem.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: collectionViewCellId, for: indexPath) as! CollectionViewCell
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 0
return cell
}
if indexPath.item == 1 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: collectionViewCellId2, for: indexPath) as! CollectionViewCell2
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 1
return cell
}
// if we get here, indexPath.item must equal 2
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: collectionViewCellId3, for: indexPath) as! CollectionViewCell3
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 2
return cell
}
Now, since you're not showing your code for your tableview cells, it's possible there might be another issue, but this worked for me:
class TableViewCell: UITableViewCell {
#IBOutlet var theImageView: UIImageView!
var colorForImageView: UIColor = UIColor.gray {
didSet {
self.theImageView.backgroundColor = colorForImageView
}
}
}
You can try to dequeue CollectionViewCell or TableViewCell explicitly on the main thread
DispatchQueue.main.async {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
}
or
DispatchQueue.main.async {
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellDif, for: indexPath) as! TableViewCellDifferent
cell.colorForImageView = colors2[indexPath.row]
}
It could help to wake up the main thread
But in general, it would be much easier if a data source for the table view was inside collection view cell
In Table view we can put checkmark easily on cells.
But in Collection View how can we put check mark, when we select a cell (image)?
I just took a image view inside the cell and image view and put a tick mark image. My code is below.
But it's not working.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
// handle tap events
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! customCollectionViewCell
if(cell.checkMarkImage.hidden == true)
{
print("Hidden")
cell.checkMarkImage.hidden = false
}
else
{
cell.checkMarkImage.hidden = true
print("No Hidden")
}
}
//Delegate Method cellForItemAtIndexPath
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) ->
UICollectionViewCell
{
//Get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(
"pickSomecell",
forIndexPath: indexPath) as! pickSomeGridViewController
//Show Images in grid view
cell.cellImage.image = self.arrAllOriginalImages[indexPath.row]
as? UIImage
//Check Mark toggle.
cell.toggleSelected()
//return cell.
return cell
}
And in pickSomeGridViewController show checkMark image selected or not.
class pickSomeGridViewController: UICollectionViewCell{
//Outlet of cell image.
#IBOutlet var cellImage: UIImageView!
//Outlet of checkMark image.
#IBOutlet var cellCheckMarkImage: UIImageView!
//Function for select and deselect checkmark.
func toggleSelected ()
{
//If image is selected.
if (selected)
{
//Show check mark image.
self.cellCheckMarkImage.hidden = false
}
else
{
//Hide check mark image.
self.cellCheckMarkImage.hidden = true
}
}
}
I see two main problems with this code:
You use dequeueReusableCellWithReuseIdentifier method which obtains different cell from collection view cache, not the one on screen.
Use cellForItemAtIndexPath method of collection view instead.
You try to save cell's state (selected/not selected) in the cell itself. It's common mistake when working with UITableView/UICollectionView and this approach will not work. Instead, keep the state in some other place (in dictionary, for example) and restore it every time collection view calls your data source cellForItemAtIndexPath method.
var arrData = NSMutableArray()
// 1.Make a ModalClass.swift and NSArray with modal class objects like this
class CustomModal: NSObject {
//Declare bool variable for select and deselect login
var is_selected = Bool()
//you can declare other variable also
var id = Int32()
}
// 2. custom array with modal objects
override func viewDidLoad() {
super.viewDidLoad()
let arrTemp = NSArray()
arrTemp = [1,2,3,4,5,6,7,8,9,10]
for i in 0 ..< arrTemp.count{
let eventModal = CustomModal()
eventModal.is_selected = false
eventModal.id = arrTemp[i]
arrData.add(eventModal)
}
tblView.reloadData()
}
// 2. Use collection view delegate method
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let modal = arrData[indexPath.row] as! CustomModal()
modal.is_selected = true
self.arrData.replaceObject(at: indexPath.row, with: modal)
tblView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let modal = arrData[indexPath.row] as! CustomModal()
modal.is_selected = false
self.arrData.replaceObject(at: indexPath.row, with: modal)
tblView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! YourCellClass
let modal = arrData[indexPath.row] as! CustomModal
if modal.is_selected == true{
cell.imgView.image = UIImage(named:"selected_image")
}else{
cell.imgView.image = UIImage(named:"deselected_image")
}
}
#Kishor, paintcode is the third party tool through which you can do that. I have provided the link too. since by default you don't have this facility, you should make your custom behavior to achiever this. Thanks.
Swift 4
In ViewController
// make a cell for each cell index path
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCollectionViewCellID", for: indexPath as IndexPath) as! YourCollectionViewCell
cell.someImageView.image = imgArr[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.item)!")
let cell = collectionView.cellForItem(at: indexPath) as? YourCollectionViewCell
cell?.isSelected = true
cell?.toggleSelected()
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as? YourCollectionViewCell
cell?.isSelected = false
cell?.toggleSelected()
}
In YourCollectionViewCell
class YourCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var someImageView: UIImageView!
#IBOutlet weak var checkImageView: UIImageView!
//Function for select and deselect checkmark.
public func toggleSelected() {
if (isSelected == false) {
//Hide check mark image.
self.checkImageView.image = UIImage(named: "unCheckImage")
isSelected = true
}else{
//Show check mark image.
self.checkImageView.image = UIImage(named: "CheckImage")
isSelected = false
}
}
}
Hope enjoy!!
var selectedCellIndex:Int?
take variable if you want to show selected Item after reloadData() : which is previously selected CellItem. {inspired by above answer }
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ColorCollectionCell", for: indexPath) as! ColorCollectionCell
cell.isSelected = false
if selectedCellIndex == indexPath.item {
cell.checkMarkImgView.image = UIImage(named: "icn_checkMark")
}else {
cell.toggleSelected()
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! ColorCollectionCell
cell.isSelected = true
selectedCellIndex = indexPath.item
cell.toggleSelected()
}
In CollectionViewCell u can use this method
class ColorCollectionCell: UICollectionViewCell {
#IBOutlet weak var cellimgView: UIImageView!
#IBOutlet weak var checkMarkImgView: UIImageView!
func toggleSelected() {
if (isSelected) {
self.checkMarkImgView.image = UIImage(named: "icn_checkMark")
}else{
self.checkMarkImgView.image = UIImage(named: "")
// here you can use uncheck img here i am not using any image for not selected.
}
}
}