Identify unique CollectionViewCells - ios

I want each CollectionViewCell to show an image and hide a label if it is tapped. But if the user scrolls the image suddenly is displayed in other cess that haven't been touched. How can I identify certain cells?
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
println("user tapped on door number \(indexPath.row)")
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! MyCollectionViewCell
if (cell.myLabel.text == "1") {
one = true
if (cell.myLabel.hidden) {
cell.myLabel.hidden = false
cell.MyImageView.image = nil
}
else {
cell.myLabel.hidden = true
cell.MyImageView.image = UIImage(named:"1")!
}
}

That's because cells are reused so instead of changes each cell, change the data source at the index of the cell that was tapped.

Related

Present ViewController by Clicking CollectionViewCell inside TableView(with several sections)

I'm making a music app recently, and I'd like to know how to pass data from CollectionView to TableView which has several sections. Here is the home page of my project, and what I want to do is when user tap the image, it will precent another ViewController with the information about the picture. I already know how to present a ViewController by clicking CollectionViewCell inside TableView by delegate, but only if there's only one section.
The thing is that I have 5 sections in this page, and I also have 5 different models for encoding the JSON from API. So how I show the pictures is to send the image urls(from 5 models) to TableViewCell in each section like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: HomeTableViewCell.identifier, for: indexPath) as? HomeTableViewCell else { return UITableViewCell() }
cell.delegate = self
switch homeSections[indexPath.section] {
case .newReleases:
if let newReleases = self.newReleases?.albums.items.map({$0.images[0].url}) {
cell.getPictures = newReleases
}
return cell
case .followSingers:
if let currentlyFollowing = self.currentlyFollowing?.artists.items.map({$0.images[0].url}) {
cell.getPictures = currentlyFollowing
cell.isCircle = true
}
return cell
case .catrgories:
if let categories = self.musicCategory?.playlists.items.map({$0.images[0].url}) {
cell.getPictures = categories
}
return cell
case .artists:
if let playlist = self.relatedArtists?.artists.map({$0.images[0].url}){
cell.getPictures = playlist
}
return cell
case .recentlyPlayed:
if let recentlyPlayed = self.recentlyPlayed?.items?.map({$0.track.album.images[0].url}) {
cell.getPictures = recentlyPlayed
}
return cell
}
}
However, when I want to pass the information which the user tap, there's nothing I can pass but indexPath. I've tried to declare the 5 different model types in TableViewCell, but I still don't know which section did the user tap. Does anyone can help? Thanks a lot!
Update:
To make the question more clearly, please refer to the information below.
In this page, I have a TableView and embed a CollectionView in the TableViewCell, and there's only one ImageView in the CollectionViewCell.
The "New Releases", "Currently Following" and "Categories" are the header of TableView. The "New Releases" is the first section, and the "Currently Following" is the second section, and so on.
Here is how I set cellForItem in CollectionView Delegate. It basically converts String to URL, and display the picture on the screen.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeCollectionViewCell.identifier, for: indexPath) as? HomeCollectionViewCell else { return UICollectionViewCell() }
guard let url = URL(string: getPictures[indexPath.row]) else { return UICollectionViewCell() }
cell.myImageView.getImages(url: url)
if isCircle == true {
cell.myImageView.layer.cornerRadius = cell.myImageView.frame.width/2
}
return cell
}
When the user taps the image, it will only trigger the didSelectItemAt in CollectionView. And I can only pass indexPath.row so far.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate?.sendIndexPath(index: indexPath.row)
}

CollectionView Cell Moving when Selected

I have a CollectionView issue, I have a video showing the problem detailed below. When I click one cell it moves in a weird manner.
Here is my code:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedFilter = indexPath.row
if filters[indexPath.row] != "Todo" {
filteredNews = news.filter { $0.category == filters[indexPath.row] }
} else {
filteredNews = news
}
tableView.reloadData()
collectionView.reloadData()
}
My Cell is moving, (Just the last cell, don't know why).
I think it might be related to collectionView.reloadData() But I need to do that for updating the green bar you can see on this Video when I select a Cell.
How can I make it not move? Someone had had a similar problem?
I noticed you reloaded a tableView during collectionView didSelectItemAt. If that tableView is a superView of your collectionView that will be the exact reason why you are having this abnormal behaviour.
If it were not, I can offer 3 solutions:
This library have a view controller subclass that can create the effect you want to show.
Manually create a UIView/UIImageView that is not inside the collectionView but update it's position during the collectionView's didSelectItemAt delegate method to but visually over the cell instead - this would require some calculation, but your collectionView will not need to reload.
You can attempt to only reload the two affected cells using the collectionView's reloadItem(at: IndexPath) method.
Know that when you reload a table/collection view, it will not change the current visible cell. However any content in each cell will be affected.
Finally I Solve it! I removed collectionView.reloadData() and added my code to change colors inside didSelectItemAt changing current selected item and old selected item (I created a Variable to see which one was the old selected item).
If someone interested, here is my code:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let oldSelectedFilter = selectedFilter
if selectedFilter != indexPath.row {
let oldIndexPath = IndexPath(item: oldSelectedFilter, section: 0)
selectedFilter = indexPath.row
if filters[indexPath.row] != "Todo" {
filteredNews = news.filter { $0.category == filters[indexPath.row] }
} else {
filteredNews = news
}
if let cell = collectionView.cellForItem(at: indexPath) as? FiltersCollectionViewCell {
cell.selectedView.backgroundColor = MainColor
}
if let cell = collectionView.cellForItem(at: oldIndexPath) as? FiltersCollectionViewCell {
cell.selectedView.backgroundColor = UIColor(red:0.31, green:0.33, blue:0.35, alpha:1.0)
}
tableView.reloadData()
}
}

Weird behavior when scrolling and selecting in UICollectionView

I am having issues with displaying a checkmark on the a custom cell in a UICollectionView. For the first few taps everything works as expected, but when I begin scrolling or tapping repeatedly or click on the already selected cell, the behavior becomes odd as shown in the gif. Perhaps I am going about this in an incorrect way? The .addCheck() and .removeCheck() are methods inside the custom UICollectionViewCell class I made and all they do is add a checkmark image or remove one from the cell view. The odd behavior shown here
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ColorUICollectionViewCell
// Configure the cell
let color = colorList[(indexPath as NSIndexPath).row]
cell.delegate = self
cell.textLabel.text = color.name
cell.backgroundColor = color.color
if color.selected {
cell.addCheck()
}
else {
cell.removeCheck()
}
return cell
}
// user selects item
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// set colors to false for selection
for color in colorList {
color.selected = false
}
// set selected color to true for selection
let color = colorList[indexPath.row]
color.selected = true
settings.backgroundColor = color.color
//userDefaults.set(selectedIndex, forKey: "selectedIndex")
collectionView.reloadData()
}
Below is what the addCheck() and removeCheck() functions in my custom cell look like.
func addCheck() {
// create check image
let checkImage = UIImage(named: "checkmark")
checkImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: bounds.size.height / 4, height: bounds.size.height / 4))
checkImageView.image = checkImage!.withRenderingMode(UIImageRenderingMode.alwaysTemplate)
checkImageView.tintColor = UIColor.white()
// add the views
addSubview(checkImageView)
}
func removeCheck() {
if checkImageView != nil {
checkImageView.removeFromSuperview()
}
}
first off, you can simplify your didSelect a bit:
override func collectionView(collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// set colors to false for selection
for (index, color) in colorList.enumerate() {
if index == indexPath.row {
color.selected = false
settings.backgroundColor = color.color
}
else {
color.selected = false
}
}
collectionView.reloadData()
}
Based on the language in your cellForItemAt method, I'm guessing you're adding a second check mark image when you tap on the same cell twice, and it's not being tracked properly so that cell just keeps getting rotated around overtime the collectionView's reloaded
Post your cell class, or at least the logic for addCheck and removeCheck and we might find the problem.
What I would recommend is permanently having an imageView with the check mark over the cell, when simple show/hide it based on the selection. This should speed up the collectionView as well.

tableViewCell accessory type issue in didSelectRowAtIndexPath

I have a tableView with list of persons. I want to select multiple cells. I've created a dictionary to store selected cells (screenshot).
var checkedSubjects: [Person: Bool] = [Person: Bool]()
Then when I select a cell it shows a checkmark near the cell and save it in my array (screenshot).
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: SearchTableViewCell = tableView.dequeueReusableCellWithIdentifier("CELL", forIndexPath: indexPath) as! SearchTableViewCell
cell.tintColor = UIColor(hex: 0x3f51b5)
cell.subjectNameLabel.text = subjects[indexPath.row].name
cell.subjectDescriptionLabel.text = "(\(subjects[indexPath.row].type))"
let person = Person(id: subjects[indexPath.row].id, name: subjects[indexPath.row].name, type: subjects[indexPath.row].type)
if checkedSubjects[person] != nil {
cell.accessoryType = checkedSubjects[person]! ? .Checkmark : .None
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
let index = indexPath.row
let person = Person(id: subjects[index].id, name: subjects[index].name, type: subjects[index].type)
if tableView.cellForRowAtIndexPath(indexPath)!.accessoryType == .Checkmark {
tableView.cellForRowAtIndexPath(indexPath)!.accessoryType = .None
checkedSubjects[person] = false
counter--
} else {
tableView.cellForRowAtIndexPath(indexPath)!.accessoryType = .Checkmark
checkedSubjects[person] = true
counter++
}
if counter > 0 {
saveBtn.enabled = true
let text = counter == 1 ? "Add \(counter) person" : "Add \(counter) persons"
saveBtn.setTitle(text, forState: UIControlState.Normal)
} else {
saveBtn.enabled = false
saveBtn.setTitle("Choose persons", forState: UIControlState.Normal)
}
}
But when I press this cell one more time I want it to return to default view. It removes checkmark but the text doesn't take empty space (screenshot). The trailing constraint of the label is set to container margin.
I've tried to reloadData() of tableView in didSelectRowAtIndexPath but it doesn't helped.
Any ideas how I can solve this issue?
I think the problem here is that you're trying to manipulate the physical cell in your didSelectRow implementation. This is the wrong way to go about things. Do not attempt to change or even read the accessory type of a cell in your didSelectRow implementation! Instead, operate there entirely on the model (checkedSubjects) and reload the affected row, so that the view picks up changes to the model (because cellForRow will be called).

swift table view adding custom checkmark to cell and remove checkmark from previous cell

I have a Image view in my cell which displays a checkmark icon.
What I want to do is when you touch a cell the checkmark should appear. I got this working, but my problem now is that I can't remove the checkmark from the previous selected cell. - only one cell should be able to be selected.
I've tried to get this working in didSelectRowAtIndexPath but I can't get it right so I am stuck right now.
Update:
var selectedRow: NSIndexPath?
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: searchCityTableViewCell!
if (indexPath.section == 0) {
cell = tableView.dequeueReusableCellWithIdentifier("staticCityCell") as! searchCityTableViewCell
cell.titleLabel?.text = "Within \(stateName)"
}else if (indexPath.section == 1) {
cell = tableView.dequeueReusableCellWithIdentifier("cityCell") as! searchCityTableViewCell
let state = cities[indexPath.row]
cell.configureWithStates(state)
if indexPath == selectedRow {
cell.cityImage.select()
} else {
cell.cityImage.deselect()
}
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
let paths:[NSIndexPath]
if let previous = selectedRow {
paths = [indexPath, previous]
} else {
paths = [indexPath]
}
selectedRow = indexPath
tableView.reloadRowsAtIndexPaths(paths, withRowAnimation: .None)
}
Keep track of which row is currently selected. Add a property to your ViewController:
var selectedRow: NSIndexPath?
In didSelectRowAtIndexPath:
let paths:[NSIndexPath]
if let previous = selectedRow {
paths = [indexPath, previous]
} else {
paths = [indexPath]
}
selectedRow = indexPath
tableView.reloadRowsAtIndexPaths(paths, withRowAnimation: .None)
In cellForRowAtIndexPath:
if indexPath == selectedRow {
// set checkmark image
} else {
// set no image
}
An important thing to note is that the state of which row is selected should be stored in the model and not in the cell. The table should reflect the state of the model. In this case, the model can simply be the indexPath of the selected row. Once the model is updated (selectedRow is set), the affected rows should reload their state.

Resources