I am new to swift and trying to implement collectionView function in a ViewController.Now I have three types of UICollectionViewCell,here is my code:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.collectionViewCategory {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: categoryIdentifier, for: indexPath as IndexPath) as! CategoryCell
cell.CategoryIcon.image = self.categoryItems[indexPath.item]
cell.layer.borderColor = UIColor.lightGray.cgColor
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 5
return cell
}
else if collectionView == self.collectionViewHour {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: hourIdentifier, for: indexPath as IndexPath) as! HourCell
cell.hourItem.text = self.hourItems[indexPath.item]
cell.layer.borderColor = UIColor.lightGray.cgColor
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 5
return cell
}
else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: minuteIdentifier, for: indexPath as IndexPath) as! MinuteCell
cell.minuteItem.text = self.minuteItems[indexPath.item]
cell.layer.borderColor = UIColor.lightGray.cgColor
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 5
return cell
}
}
Can I use switch statement to achieve the same effect?Make the code look a little more elegant?
I hope my code looks like this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell:UICollectionViewCell
switch collectionView {
case self.collectionViewCategory:
cell = collectionView.dequeueReusableCell(withReuseIdentifier: categoryIdentifier, for: indexPath as IndexPath) as! CategoryCell
cell.CategoryIcon.image = self.categoryItems[indexPath.item]
case self.collectionViewHour:
cell = collectionView.dequeueReusableCell(withReuseIdentifier: hourIdentifier, for: indexPath as IndexPath) as! HourCell
cell.hourItem.text = self.hourItems[indexPath.item]
default:
//do nothing.
}
cell.layer.borderColor = UIColor.lightGray.cgColor
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 5
return cell
}
But I got a lot of mistakes, and I do not know how to fix them.
You can try to assign each collectionView a unique tag like so
collectionView.tag = 1
and then use the tag as the identifier:
switch collectionView.tag {
case 0:
// some code
case 1:
// some code
default:
// some code
}
If you're using your collectionViews inside UITableViewCells you can set each collectionView.tag to be indexPath.section or indexPath.row within tableView(_ tableView: willDisplay cell: forRowAt indexPath:).
Here's a nice subclass you could use:
class CollectionViewTableViewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
func setCollectionViewDataSourceDelegate(dataSource: UICollectionViewDataSource?, dataDelegate: UICollectionViewDelegate?, forSection section: Int) {
collectionView.delegate = dataSource as! UICollectionViewDelegate?
collectionView.dataSource = dataDelegate as! UICollectionViewDataSource?
collectionView.tag = section
collectionView.reloadData()
}
}
and then in your view controller implement
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let tableViewCell = cell as? CollectionViewTableViewCell else { return }
tableViewCell.setCollectionViewDataSourceDelegate(dataSource: self, dataDelegate: self, forSection: indexPath.section)
}
Hope this help. Not testing at all. If any issues, can you take a snip ?
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell:UICollectionViewCell
switch collectionView {
case self.collectionViewCategory:
let categoryCell = collectionView.dequeueReusableCell(withReuseIdentifier: categoryIdentifier, for: indexPath as IndexPath) as! CategoryCell
categoryCell.CategoryIcon.image = self.categoryItems[indexPath.item]
cell = categoryCell
case self.collectionViewHour:
let hourCell = collectionView.dequeueReusableCell(withReuseIdentifier: hourIdentifier, for: indexPath as IndexPath) as! HourCell
hourCell.hourItem.text = self.hourItems[indexPath.item]
cell = hourCell
default:
//do nothing.
}
cell.layer.borderColor = UIColor.lightGray.cgColor
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 5
return cell
}
Related
I have a tableView and collectionView in one view controller.
in tableView I have title description and in collectionView I have lable .
I want on collectionView label selection tableView content should change .
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Bookmark.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionViewBookmark.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! BookMarkCollectionViewCell
cell.lblTitle.text = Bookmark[indexPath.row]
cell.backgroundColor = UIColor.white
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionViewBookmark.cellForItem(at: indexPath)
cell?.backgroundColor = UIColor.blue
self.selectedIndexPath = indexPath
//
let newsDict = arrNewsData[indexPath.row]
if (indexPath.row == 1)
{
let cell1 = tableViewBookMark.cellForRow(at: indexPath) as! BookMarkFirstTableViewCell
cell1.lblTitle.text = newsDict["title"] as! String
tableViewBookMark.reloadData()
}
tableViewBookMark.reloadData()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewBookMark.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! BookMarkFirstTableViewCell
let dict = arrNewsData[indexPath.row]
cell.lblTitle.text = dict["title"] as! String
// cell.imgBookMark.image = dict["image_url"]
let url = URL(string: dict["image_url"] as! String)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if data != nil{
DispatchQueue.main.async {
let image = UIImage(data: data!)
cell.imgBookMark.image = image
}
}
}.resume()
return cell
}
See my inline comments.
var tempCell: BookMarkFirstTableViewCell?
//Inside cellForRowAt indexPath
tempCell = cell
//Inside (collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
tempCell.lblTitle.text = newsDict["title"] as! String
You're reloading the tableView after updating the values in the cell,
tableViewBookMark.reloadData()
This will trigger the data source function including cellForRowAt, so you will lose your updated values a solution to this is to have a global variable in the UIViewController and check its values inside the cellForRowAt and update it in the collectionView DidSelect .
Extra tip: you don't need to reload all the tableView for a single change you can use
tableView.reloadRows(at: [indexPath], with: .top)
to reload only number of selected cells in the tableView
Here i made a collectionView cell variable for accessing both objects. But unable to access the objects inside the cell variable
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell: UICollectionViewCell!
if collectionView == collectionView1 {
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellAttachment", for: indexPath) as! AttachmentCell
cell.imgAttachment.image = imageArray1[indexPath.row]
cell.delegate = self
}
else if collectionView == collectionView2 {
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellAttachmentView", for: indexPath) as! AttachmentViewCell
cell.imgFileIcon.image = imgArray2[indexPath.row].fileIcon
}
return cell
Value of type 'UICollectionViewCell?' has no member 'imgAttachment'
The problem lies here.
var cell: UICollectionViewCell!
You have declared the cell to be of type UICollectionViewCell. So, no matter which subclass you store inside it, you cell will only be of type UICollectionViewCell.
You should change it like this,
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView === collectionView1 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellAttachment", for: indexPath) as! AttachmentCell
cell.imgAttachment.image = imageArray1[indexPath.row]
cell.delegate = self
return cell
} else if collectionView === collectionView2 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellAttachmentView", for: indexPath) as! AttachmentViewCell
cell.imgFileIcon.image = imgArray2[indexPath.row].fileIcon
return cell
} else {
// Return the proper cell for other cases
}
}
Or, if you are adamant you need only a single return statement at the end of the delegate, then you could do it like this,
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var yourCell: UICollectionViewCell!
if collectionView === collectionView1 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellAttachment", for: indexPath) as! AttachmentCell
cell.imgAttachment.image = imageArray1[indexPath.row]
cell.delegate = self
yourCell = cell
} else if collectionView === collectionView2 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellAttachmentView", for: indexPath) as! AttachmentViewCell
cell.imgFileIcon.image = imgArray2[indexPath.row].fileIcon
yourCell = cell
} else {
// Return the proper cell for other cases
}
return yourCell
}
You need to silent the compiler by casting the cell as AttachmentCell. See the below example,
(cell as! AttachmentCell).imgAttachment.image = imageArray1[indexPath.row]
(cell as! AttachmentCell).delegate = self
The reason compiler is not able to recognize the variable is the declaration of variable cell as UICollectionViewCell. As there is no imgAttachment variable in UICollectionViewCell so compiler will complain.
I created a collection view in table view cell and added a segment control on the navigation bar of the table view. How can I change the collection cell item when the segment outlet and action is on table view controller?
I tried this on table view controller but get this error:
request for number of items before section 2147483647 when there are only 1 sections in the collection view
#IBAction func mySegAction(_ sender: UISegmentedControl) {
switch mySeg.selectedSegmentIndex
{
case 0:
print("0")
case 1:
print("1")
let indexPath = IndexPath()
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CategoryCollectionViewCell
cell.cateLabel.text! = nameArray2[indexPath.row]
default:
break;
}
}
Table view cell controller:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CategoryCollectionViewCell
cell.cateLabel.text! = nameArray[indexPath.row]
// I tried this code but fail
if AddViewController().mySeg?.selectedSegmentIndex == 1 {
cell.cateLabel.text! = nameArray2[indexPath.row]
}
return cell
}
I want to change cateLabel.text! = nameArray[indexPath.row] to nameArray2[indexPath.row] when the Segment is changed, How to do that?
Calling tableView.reloadData() on Action and collectionView.reloadData() on cellForRowAt
#IBAction func mySegAction(_ sender: UISegmentedControl) {
switch mySeg.selectedSegmentIndex{
case 0:
segment = 0
UserDefaults.standard.setValue(segment, forKey:"segment")
UserDefaults.standard.synchronize()
case 1:
segment = 1
UserDefaults.standard.setValue(segment, forKey:"segment")
UserDefaults.standard.synchronize()
default:
break;
}
self.tableView.reloadData()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 1{
let cell1 = tableView.dequeueReusableCell(withIdentifier: "AddViewCell2", for: indexPath)
return cell1
}else if indexPath.row == 2{
let cell2 = tableView.dequeueReusableCell(withIdentifier: "AddViewCell3", for: indexPath)
return cell2
}else if indexPath.row == 3{
let cell3 = tableView.dequeueReusableCell(withIdentifier: "AddViewCell4", for: indexPath)
return cell3
}else if indexPath.row == 4{
switch segment {
case 0:
let cell4 = tableView.dequeueReusableCell(withIdentifier: "AddViewCell5", for: indexPath) as! AddViewCell
cell4.collectionView.reloadData()
return cell4
case 1:
let cell4 = tableView.dequeueReusableCell(withIdentifier: "AddViewCell5", for: indexPath) as! AddViewCell
cell4.collectionView.reloadData()
return cell4
default:
break
}
}
let cell = tableView.dequeueReusableCell(withIdentifier: "AddViewCell1", for: indexPath)
return cell
}
On the table view cell class:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CategoryCollectionViewCell
let segment = UserDefaults().value(forKey: "segment") as! Int
//print("segment here")
//print(segment)
switch segment {
case 0:
cell.cateLabel.text! = nameArray[indexPath.row]
cell.cateImg.image = imageName[indexPath.row]
case 1:
cell.cateLabel.text! = nameArray2[indexPath.row]
cell.cateImg.image = imageName[indexPath.row]
default:
break;
}
return cell
}
I have 4 different collectionViews in one controller, and in the first collectionView I want to have 3 different cells display. In the below code the app does not crash, but in the first indexPath.item of 0 only case 0: ("cellId") loads. It does not load the other 2 cases. Thanks in advance for any help!
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
switch indexPath.item {
case 0:
return collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath)
default:
return collectionView.dequeueReusableCell(withReuseIdentifier: "cellId4", for: indexPath)
}
} else if indexPath.item == 1 {
return collectionView.dequeueReusableCell(withReuseIdentifier: "cellId2", for: indexPath)
} else if indexPath.item == 2 {
return collectionView.dequeueReusableCell(withReuseIdentifier: "cellId3", for: indexPath)
} else {
let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath)
return myCell
}
}
// 2 collectionView Cells - only 1 section in each cell
// cell 1 - withReuseIdentifier "cellId" in collectionViewController
class TravelGuideHomeCellForStats: BaseHomeCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
cv.dataSource = self
cv.delegate = self
return cv
}()
let cellId = "cellId"
override func setupViews() {
super.setupViews()
collectionView.register(BaseTravelGuideHomeCell.self, forCellWithReuseIdentifier: cellId)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! BaseTravelGuideHomeCell
return cell
}
}
// cell 2 - withReuseIdentifier "cellId4" in collectionViewController
class TravelGuideCommentsCell: BaseCommentsCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
cv.dataSource = self
cv.delegate = self
return cv
}()
let cellId = "cellId"
override func setupViews() {
super.setupViews()
collectionView.register(BaseTravelGuideCommentsCell.self, forCellWithReuseIdentifier: cellId)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! BaseTravelGuideCommentsCell
return cell
}
}
Do you mean to be filtering by section first, instead of by item in your view controller?
i.e. like this:
if indexPath.section == 0 {
switch indexPath.item {
...
}
} else if indexPath.section == 1 {
return collectionView.dequeueReusableCell(withReuseIdentifier: "cellId2", for: indexPath)
} else if indexPath.section == 2 {
return collectionView.dequeueReusableCell(withReuseIdentifier: "cellId3", for: indexPath)
} else {
let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath)
return myCell
}
Though I would have done this as nested switches:
switch indexPath.section {
case 0:
switch indexPath.item {
...
}
case 1:
return collectionView.dequeueReusableCell(withReuseIdentifier: "cellId2", for: indexPath)
case 2:
return collectionView.dequeueReusableCell(withReuseIdentifier: "cellId3", for: indexPath)
default:
return collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath)
}
I have a UICollectionView and there user can select on cell to use the Function in it. So and i need it to set the first cell to selected like in the First Screenshot. But the Problem there is when the cell is selected and user want to select ah other cell the first cell don´t unselect. I have try it to reload the tableview in did select item but that don´t run or with this func in viewdidload LabelCollectionView.selectItem(at: IndexPath(row: 1, section: 0), animated: true, scrollPosition: .bottom)
Have anyone an idea what i can make there ? thanks for Help:)
First Cell Selected
Here you see the Problem in the screenshot
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "StoryLabelCell", for: indexPath) as! StoryLabelCell
let labels = Labels[indexPath.row]
cell.backgroundColor = UIColor.lightGray
cell.layer.cornerRadius = 10
cell.Title.text = labels
if indexPath.row == 0 {
cell.backgroundColor = darkgrau
cell.layer.borderColor = black.cgColor
cell.layer.borderWidth = 2.0
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! StoryLabelCell
if indexPath.row == indexPath.row {
cell.backgroundColor = darkgrau
cell.layer.borderColor = black.cgColor
cell.layer.borderWidth = 2.0
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! StoryLabelCell
if indexPath.row == indexPath.row {
cell.backgroundColor = lightGray
cell.layer.borderWidth = 0
}
}
What are you trying to do with checking for indexPath.row == indexPath.row? Basically you can try with cell.selected = true when it is selected and set it to false when deselected. Try below code and let me know if it works.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "StoryLabelCell", for: indexPath) as! StoryLabelCell
let labels = Labels[indexPath.row]
cell.backgroundColor = UIColor.lightGray
cell.layer.cornerRadius = 10
cell.Title.text = labels
if indexPath.row == 0 {
cell.backgroundColor = darkgrau
cell.layer.borderColor = black.cgColor
cell.layer.borderWidth = 2.0
cell.selected = true
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! StoryLabelCell
cell.backgroundColor = darkgrau
cell.layer.borderColor = black.cgColor
cell.layer.borderWidth = 2.0
cell.selected = true
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! StoryLabelCell
cell.backgroundColor = lightGray
cell.layer.borderWidth = 0
cell.selected = false
}
u should use the Cells selected state instead of comparing two values that are the same.
In you custom cell you react on cell selections!
override func setSelected(_ selected: Bool, animated: Bool) {
// change your cell like you want
if selected {
// make me dark
} else {
// make me light
}
// not tested, but somehow call the super
super.setSelected(selected:selected, animated: animated)
}
you can delete your didSelect and didDeselct func.
In the function cellForItem ... you can check if a cell is selected, if not select the one with indexPath.row == 0
For checking a selected cell use
collectionView.indexPathForSelectedItems
this is list, so you can select more than one cell. I am not sure, but i think u can setup the collectionView with multiple selections ore only one selection.
I hope this helps you so far.