I have a navigation tab bar and each tab contains a picture of array, see picture below. My question is. Why does the enum only see the top case "mens", it doesn't switch to women, arts and saved. I tried to delete the case mens, then it can only see the case "women", whatever is on top, thats only thing cellForItemAtIndexPath can see. Everything is already connected I tried to get them one by one to see if they're working and they're all working. But it can only see what's on top. I dont understand what's missing in here,
class StreamDetailController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate{
private enum Tabs: Int {
case mens
case women
case arts
case saved
}
var menImage = [Men]()
var artsImage = [Arts]()
var womenImage = [Women]()
var savedImage = [SavedPhotos]()
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
guard let tab = Tabs(rawValue: section) else {
assertionFailure()
return section
}
switch tab {
case .mens:
return menImage.count
case .women:
return womenImage.count
case .arts:
return artsImage.count
case .saved:
return savedImage.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let tab = Tabs(rawValue: indexPath.section) else {
assertionFailure()
return UICollectionViewCell()
}
switch tab {
case .mens:
let cell = extraCells.dequeueReusableCell(withReuseIdentifier: "Cells", for: indexPath) as! ExtraViewCell
let men = menImage[indexPath.item]
cell.boxImage.image = men.image
return cell
case .women:
let cell = extraCells.dequeueReusableCell(withReuseIdentifier: "Cells", for: indexPath) as! ExtraViewCell
let women = womenImage[indexPath.item]
cell.boxImage.image = women.image
return cell
case .arts:
let cell = extraCells.dequeueReusableCell(withReuseIdentifier: "Cells", for: indexPath) as! ExtraViewCell
let art = artsImage[indexPath.item]
cell.boxImage.image = art.image
return cell
case .saved:
let cell = extraCells.dequeueReusableCell(withReuseIdentifier: "Cells", for: indexPath) as! ExtraViewCell
let save = savedImage[indexPath.item]
cell.boxImage.image = save.image
return cell
}
}
Setting
cases
Tab Bar
The problem is you are using section and not row to identify the row. Most likely you only have one section and multiple rows. That's why the section is always zero and points to the first element of your enum. You should change your code to the following:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let tab = Tabs(rawValue: indexPath.row) else {
assertionFailure()
return UICollectionViewCell()
}
switch tab {
case .mens:
let cell = extraCells.dequeueReusableCell(withReuseIdentifier: "Cells", for: indexPath) as! ExtraViewCell
let men = menImage[indexPath.item]
cell.boxImage.image = men.image
return cell
case .women:
let cell = extraCells.dequeueReusableCell(withReuseIdentifier: "Cells", for: indexPath) as! ExtraViewCell
let women = womenImage[indexPath.item]
cell.boxImage.image = women.image
return cell
case .arts:
let cell = extraCells.dequeueReusableCell(withReuseIdentifier: "Cells", for: indexPath) as! ExtraViewCell
let art = artsImage[indexPath.item]
cell.boxImage.image = art.image
return cell
case .saved:
let cell = extraCells.dequeueReusableCell(withReuseIdentifier: "Cells", for: indexPath) as! ExtraViewCell
let save = savedImage[indexPath.item]
cell.boxImage.image = save.image
return cell
}
}
I have replaced indexPath.section with indexPath.row
Related
Try to hide and show cell and footer of a section in collection view when head supplementary view is tapped like image below. Try to find some code to do things like this but found none.
Can someone have the workaround for this problem? If possible show how to code it .
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print(collectionView.numberOfItems(inSection: indexPath.section))
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TenorInterestCell", for: indexPath) as? TenorInterestCell else {
fatalError()
}
let item = viewModel.itemsForRowAt(indexPath: indexPath)
cell.tenorText.text = "\(item?.tenorAvailable ?? "") Bulan"
cell.interestText.text = "Bunga : \(item?.interest ?? 0) %"
return cell
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionView.elementKindSectionFooter {
let item = viewModel.itemsForRowAt(section: indexPath.section)
guard let view = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "TenorFooterView", for: indexPath) as? TenorFooterView else {
fatalError()
}
view.tag = indexPath.section
let tapGestureRecognizer = UITapGestureRecognizer(target:self, action:#selector(tapDetected))
view.addGestureRecognizer(tapGestureRecognizer)
view.configure(with: item)
view.delegate = self
return view
}else{
guard let view = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "TenorInterestView", for: indexPath) as? TenorInterestView else {
fatalError()
}
let item = viewModel.itemsForRowAt(section: indexPath.section)
view.titleText.text = item.prdName
return view
}
}
I have an application where I have two sections the issue I have now is if I select an item in section 1, it automatically selects a cell in section 2 which is not suppose to be. I want Items to be selectable in section 1 without affecting section two.
below is my selection
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.section {
case 0:
showCustomDialog(subD: sub[indexPath.row])
case 1:
let cell = tableView.cellForRow(at: indexPath) as! VasListCell
cell.checkBox.setOn(true, animated: true)
default: break
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
switch indexPath.section {
case 1:
let cell = tableView.cellForRow(at: indexPath) as! VasListCell
cell.checkBox.setOn(false, animated: true)
default: break
}
}
where I am using the selected index
func selectedIndex(viewcontroller: UIViewController) {
let selectedRows = tableView.indexPathsForSelectedRows
guard let vasRow = selectedRows?.map ({ vas[$0.row] }) else { return }
selectedVasData = vasRow
let vasData = selectedVasData
let subData = selectedSubData
let vcr = viewcontroller as! CheckoutVC
vcr.vas = vasData
vcr.sub = subData
let tot1 = subData.compactMap {$0.price}
let tot2 = vasData.compactMap {$0.amount}
let tot = tot1 + tot2
let reduced = tot.compactMap(Double.init).reduce(0, +)
vcr.tableView.reloadData()
self.present(viewcontroller, animated: true, completion: nil)
print("CELL INDEX vas \(StaticFunc.convertDoubleToCurrency(amount: reduced))")
}
I am having some trouble with my cellForItem because I don't know how to load 3 collection views simultaneously. This is what I tried but I get the error "Missing return in a function expected to return 'UICollectionViewCell".
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.lostCollectionView {
let lostcell: LostCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "Lostcell", for: indexPath) as! LostCollectionViewCell
lostcell.set(post: posts[indexPath.row])
//Make TextView Clickable
lostcell.phoneLostTextView.isEditable = false;
lostcell.phoneLostTextView.dataDetectorTypes = UIDataDetectorTypes.phoneNumber
return lostcell
}
if collectionView == self.foundCollectionView {
let foundcell: FoundCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "Foundcell", for: indexPath) as! FoundCollectionViewCell
foundcell.set(postfound: postsfound[indexPath.row])
//Make TextView Clickable
foundcell.phoneFoundTextView.isEditable = false;
foundcell.phoneFoundTextView.dataDetectorTypes = UIDataDetectorTypes.phoneNumber
return foundcell
}
if collectionView == self.adoptionCollectionView {
let adoptioncell: AdoptionCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "Adopotioncell", for: indexPath) as! AdoptionCollectionViewCell
adoptioncell.set(postadoption: postsadoption[indexPath.row])
//Make TextView Clickable
adoptioncell.phoneAdoptionTextView.isEditable = false;
adoptioncell.phoneAdoptionTextView.dataDetectorTypes = UIDataDetectorTypes.phoneNumber
return adoptioncell
}
}
Your function has 3 ifs. If they all fail, the function does not return anything. That's the reason the Swift compiler is complaining.
You could add return UICollectionViewCell() at the bottom of the function.
Also, a switch statement is more appropriate for this situation.
Basically you will need to include your code inside a switch statement like this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch collectionView {
case self.lostCollectionView:
let lostcell: LostCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "Lostcell", for: indexPath) as! LostCollectionViewCell
lostcell.set(post: posts[indexPath.row])
//Make TextView Clickable
lostcell.phoneLostTextView.isEditable = false;
lostcell.phoneLostTextView.dataDetectorTypes = UIDataDetectorTypes.phoneNumber
return lostcell
case self.foundCollectionView:
let foundcell: FoundCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "Foundcell", for: indexPath) as! FoundCollectionViewCell
foundcell.set(postfound: postsfound[indexPath.row])
//Make TextView Clickable
foundcell.phoneFoundTextView.isEditable = false;
foundcell.phoneFoundTextView.dataDetectorTypes = UIDataDetectorTypes.phoneNumber
return foundcell
case self.adoptionCollectionView:
let adoptioncell: AdoptionCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "Adopotioncell", for: indexPath) as! AdoptionCollectionViewCell
adoptioncell.set(postadoption: postsadoption[indexPath.row])
//Make TextView Clickable
adoptioncell.phoneAdoptionTextView.isEditable = false;
adoptioncell.phoneAdoptionTextView.dataDetectorTypes = UIDataDetectorTypes.phoneNumber
return adoptioncell
default:
return UICollectionViewCell()
}
}
As mentioned by meaning-matters, this is due to the missing cell, in case none of the 3 ifs are satisfied.
You could add a final fallback cell at the bottom of the function as meaning-matters suggested.
Or, you can go with another similar approach which I would personally prefer:
// Inside your **cellForItemAt** function.
switch collectionView {
case lostCollectionView:
// Replace this with your corresponding code.
return LostCollectionViewCell()
case foundCollectionView:
// Replace this with your corresponding code.
return FoundCollectionViewCell()
case adoptionCollectionView:
// Replace this with your corresponding code.
return AdoptionCollectionViewCell()
default:
return UICollectionViewCell()
}
I personally find switch-case to be a neater solution for these kind of cases.
I have an iOS app, written in swift. It's a social platform where the users can post 9 different types of posts (text, image, video, link, audio, poll, chat, etc). I set those using an enum
enum PostType: String {
case image = "image"
case gif = "gif"
case video = "video"
case text = "text"
case link = "link"
case audio = "audio"
case poll = "poll"
case chat = "chat"
case quote = "quote"
}
I'm utilising FirebaseDatabase to store the data. In the DashboardViewController I query the database and get the posts in an array along with the corresponding users, ready to be displayed.
func loadPosts() {
activityIndicator.startAnimating()
Api.Feed.observeFeedPosts(withUserId: Api.Users.CURRENT_USER!.uid) {
post in
guard let userId = post.userUid else { return }
self.fetchUser(uid: userId, completed: {
self.posts.insert(post, at: 0)
self.activityIndicator.stopAnimating()
self.collectionView.reloadData()
})
}
Api.Feed.observeFeedRemoved(withUserId: Api.Users.CURRENT_USER!.uid) { (post) in
self.posts = self.posts.filter { $0.id != post.id } // removed all array elements matching the key
self.users = self.users.filter { $0.id != post.userUid }
self.collectionView.reloadData()
}
}
func fetchUser(uid: String, completed: #escaping () -> Void ) {
Api.Users.observeUsersShort(withId: uid) {
user in
self.users.insert(user, at: 0)
completed()
}
}
Whenever the user creates a new post, it stores PostType.text.rawValue (for example, it gives "text" String) on the database to differentiate between them (either video, photo, text, etc). Now, I have to use the PostType enum to figure out what the post type is and display the corresponding UICollectionViewCell. Now, if its a single cell, it's easy. I can do this and it works:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return posts.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellName.postTextCVC, for: indexPath) as! PostTextCVC
let user = users[indexPath.row]
let post = posts[indexPath.row]
cell.delegatePostTextCVC = self
cell.user = user
cell.dashboardVC = self
cell.post = post
return cell
}
The problem is, how to use the enum to display the appropriate cell?
Keep a variable PostType variable in your Post class. In cellForItemAt check post type of the post and dequeue respective cell.
Something like this.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let post = posts[indexPath.row]
let type: PostType = post.type // Get the post type eg. text, image etc.
switch type {
case .text:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellName.postTextCVC, for: indexPath) as! PostTextCVC
let user = users[indexPath.row]
cell.delegatePostTextCVC = self
cell.user = user
cell.dashboardVC = self
cell.post = post
return cell
case .image:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellName.postImageCVC, for: indexPath) as! PostImageCVC
return cell
case .video:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellName.postTextCVC, for: indexPath) as! PostVideoCVC
return cell
}
}
If you are using separate nib files for each collection view cell, make sure you register all possible nibs with collection view like this.
collectionView.register(UINib(nibName: "PostTextCVC", bundle: nil), forCellWithReuseIdentifier: CellName.postTextCVC)
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
}