How can I highlight selected UICollectionView cells? (Swift) - ios

I have a UICollectionView, and the user is able to select multiple cells. It's a bit difficult to keep track of which cells have been selected, so I need some way to go about highlighting/creating a border when the cell is tapped.
Code:
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
addToList.append(objectsArray[indexPath.row])
return true
}

you can use border change on didSelectItemAtIndexPath override event like the below code and assign new settings on the cell.
Swift 3.x:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
addToList.append(objectsArray[indexPath.row])
let cell = collectionView.cellForItem(at: indexPath)
cell?.layer.borderWidth = 2.0
cell?.layer.borderColor = UIColor.gray.cgColor
}

Here is my solution, and I'm sure it works.
My solution includes 3 highlight effects, UICollectionCell's selectedBackgroundView, cell.contentView.backgroundColor, or your your own specialHighlightedArea; just feel free to choose the one you need, and feel free to add more effects as your App's Designer requires.
How to use? Just inherit BaseCollectionViewCell. If needed, configure in cell's init or collectionView's delegate methods.
If you don't need highlight effect, just find a method named 'shouldHighlightItemAtIndexPath' in UICollectionViewDelegate and return false or just set cell.shouldTintBackgroundWhenSelected = false.
extension UIColor {
convenience init(rgb: Int, alpha: CGFloat = 1.0) {
self.init(red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0, green: CGFloat((rgb & 0xFF00) >> 8) / 255.0, blue: CGFloat(rgb & 0xFF) / 255.0, alpha: alpha)
}
}
/// same with UITableViewCell's selected backgroundColor
private let cellHighlightedColor = UIColor(rgb: 0xD8D8D8)
class BaseCollectionViewCell: UICollectionViewCell {
var shouldTintBackgroundWhenSelected = true // You can change default value
var specialHighlightedArea: UIView?
// make lightgray background show immediately(on touch)
// (使灰背景在手指触到 cell 时立即出现)
override var isHighlighted: Bool {
willSet {
onSelected(newValue)
}
}
// keep lightGray background from selected until unselected
// (保留灰背景直至取消选中)
override var isSelected: Bool {
willSet {
onSelected(newValue)
}
}
func onSelected(_ newValue: Bool) {
// selectedBackgroundView is defined by UICollectionViewCell
guard selectedBackgroundView == nil else { return }
if shouldTintBackgroundWhenSelected {
contentView.backgroundColor = newValue ? cellHighlightedColor : UIColor.clear
}
if let sa = specialHighlightedArea {
sa.backgroundColor = newValue ? UIColor.black.withAlphaComponent(0.4) : UIColor.clear
}
}
}

SWIFT
Add this code to your UICollectionViewCell subclass:
override var isSelected: Bool {
didSet{
if self.isSelected {
UIView.animate(withDuration: 0.3) { // for animation effect
self.backgroundColor = UIColor(red: 115/255, green: 190/255, blue: 170/255, alpha: 1.0)
}
}
else {
UIView.animate(withDuration: 0.3) { // for animation effect
self.backgroundColor = UIColor(red: 60/255, green: 63/255, blue: 73/255, alpha: 1.0)
}
}
}
}
This will set the color of a single selected cell, and remove the selected color from any previous selected cells. I've added a smooth animation to it. I think it's nice, but it's optional.

Use
collectionView.reloadItemsAtIndexPaths([indexPath])
to reload current cell, or
collectionView.reloadData()
to reload all cells in shouldSelectItemAtIndexPath
Then in cellForItemAtIndexPath set your border or background color if the cell is marked as checked (you may need a new array for checked cells with preferably indexPaths.

You can create a customized collcetionViewCell, and override:
class MyCell: UICollectionViewCell {
override var isHighlighted: Bool {
didSet {
if self.isHighlighted {
print("yes")
// Your customized animation or add a overlay view
} else {
print("no")
// Your customized animation or remove overlay view
}
}
}
}
This way, you can create similar result like the highlight effect on UITableViewCell.
Without subclassing:
If you don't want to create your own collectionViewCell. you can use the delegate method:
func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath)
func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath)
You can do the same thing with it.

For multiple selection of cell, you can do it as follow:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath){
if let currentCell = collectionView.cellForItem(at: indexPath) as? QuestionnaireCollectionViewCell {
// Your selection logic, you can change it according to your requirement
if currentCell.selectedImage.isHidden == true{
currentCell.selectedImage.isHidden = false
}
else{
currentCell.selectedImage.isHidden = true
}
}
}
}
For single selection you can use isSelected in your collectionviewcell class as follow:
override var isSelected: Bool{
didSet{
if self.isSelected
{
//This block will be executed whenever the cell’s selection state is set to true (i.e For the selected cell)
}
else
{
//This block will be executed whenever the cell’s selection state is set to false (i.e For the rest of the cells)
}
}
}

Try to make the borders thick enough to cover the entire cell
Code:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
addToList.append(objectsArray[indexPath.row])
let cell = collectionView.cellForItem(at: indexPath)
cell?.layer.borderWidth = 200.0
cell?.layer.borderColor = UIColor.init(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.4).cgColor
}

Try this code to highlight collection view cell
func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.contentView.backgroundColor = #colorLiteral(red: 1, green: 0.4932718873, blue: 0.4739984274, alpha: 1)
}
}
func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.contentView.backgroundColor = nil
}
}

With ternary operator
override var isSelected: Bool {
didSet {
UIView.animate(withDuration: 0.3) {
self.backgroundColor = self.isSelected ? .systemGray4 : .systemGray6
}
}
}

Related

UICollectionView | Cell reusable

SO, UICollectionView is being a real pain for me right now. Consider I have a UIViewController which has a UICollectionView embedded in it. Well each cell of the CollectionView is almost the entire width of the UIViewController. And each cell contains some buttons and images. When I select one button and tend to make the button retain its state, the CollectionView reuses the cell and kind of duplicates the cell states across other cells as well. However when I try to put the cells in an array and kind of want to check the states of cells in that array, the cellForItemAt method overwrites those cells. I am so confused. Please help. Even prepareForReuse in UICollectionViewCell isn't helping. Here is some code:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! AddressCollectionViewCell
cell.shopAddressDetailLbl.text = ""
cell.addressObj = addresses[indexPath.row]
cell.configureCellForAddress(cell.addressObj)
cell.cellTag = indexPath.row
cell.cellDelegate = self
if addressCells.contains(cell) == false {
addressCells.append(cell)
} else {
if cell.isAddressConfirmed == true {
cell.confirmAddress.setTitle("CONFIRMED", for: .normal)
cell.confirmAddress.isEnabled = false
cell.confirmAddress.backgroundColor
= UIColor(red: 0, green: 100/255, blue: 0, alpha: 1)
addressCells[indexPath.row] = cell
}
}
return cell
}
extension AddressesCollectionViewController: AddressCollectionViewCellDelegate {
func confirmBtnPressed(confirmAddressObj: Address, cell:AddressCollectionViewCell) {
for cellTemp in addressCells {
if cellTemp == cell && cellTemp.isAddressConfirmed == false {
if let dele = addressCollectionViewDelegate {
cellTemp.isAddressConfirmed = true
dele.configureCellsAccordingToChanges(cell: cellTemp)
}
}
}
}
}
override func prepareForReuse() {
super.prepareForReuse()
cellTag = 0
confirmAddress.setTitle("Confirm Address", for: .normal)
confirmAddress.backgroundColor = APP_UNIVERSAL_COLOR
confirmAddress.isEnabled = true
}
Any help is more than appreciated.
🙌 #Vadian, #Abu Ul Hassan 👍
Pretty slick! To others who need help in this regard. Vadian suggested in comments that I just need to update and monitor my model and thats exactly what I did. SO here it goes:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! AddressCollectionViewCell
cell.shopAddressDetailLbl.text = ""
cell.addressObj = addresses[indexPath.row]
cell.configureCellForAddress(cell.addressObj)
cell.cellTag = indexPath.row
cell.cellDelegate = self
if addresses[indexPath.row].isConfirmed! == true {
cell.confirmAddress.setTitle("CONFIRMED", for: .normal)
cell.confirmAddress.isEnabled = false
cell.confirmAddress.backgroundColor = UIColor(red: 0, green: 100/255, blue: 0, alpha: 1)
} else {
cell.confirmAddress.setTitle("Confirm Address", for: .normal)
cell.confirmAddress.isEnabled = true
cell.confirmAddress.backgroundColor = APP_UNIVERSAL_COLOR
}
return cell
}
extension AddressesCollectionViewController: AddressCollectionViewCellDelegate {
func confirmBtnPressed(confirmAddressObj: Address, cell:AddressCollectionViewCell) {
if confirmAddressObj.isConfirmed! == false {
if let dele = addressCollectionViewDelegate {
cell.isAddressConfirmed = true
dele.configureCellsAccordingToChanges(cell: cell)
}
}
}
}
And its ALIVE :D

change the cell view color when it select. And change it back when selected another cell

I have an collection view cell.I each cell i have an view. As id now default, i set the border color of my view to lightGray.
So when ever i select any cell, i needs to change my view border color to red color. And again if i select any other new cell. My old cell view should change back to lightGray.And new cell view have to display as redcolor.
how can i do that :
in my cell :
#IBOutlet var baseView: UIView! {
didSet {
baseView.layer.cornerRadius = 5
baseView.layer.borderWidth = 1.0
baseView.layer.borderColor = UIColor.lightGray.cgColor
}
}
let datt = [["duration": "No", "price": "Rs. 100", "perMonth": "per month"],
["duration": "12", "price": "Rs. 55.20", "perMonth": "per month"],
["duration": "No", "price": "Rs. 1300", "perMonth": "one time"]]
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "KMSubscriptionsCell", for: indexPath) as! KMSubscriptionsCell
let subcription = subscriptions[indexPath.item]
cell.durationLabel.text = datt["duration"]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! KMSubscriptionsCell
cell.baseView.layer.borderColor = Utils.colorCode.selectedBorderColor.cgColor
cell.baseView.layer.borderWidth = 2.0
}
I tried some :
in my cell for row :
if indexPath.item != indexPath.item {
cell.baseView.layer.borderWidth = 1.0
cell.baseView.layer.borderColor = UIColor.lightGray.cgColor
}
its doesnt work.even i added in did select.No luck.Please help me out. How can i achive that.
One easy way is to use a property observer in the cell class:
class CollectionViewCell: UICollectionViewCell {
override func awakeFromNib() {
super.awakeFromNib()
layer.borderWidth = 1
layer.borderColor = borderColor
}
override var isSelected: Bool {
didSet {
layer.borderColor = borderColor
}
}
private var borderColor: CGColor {
return isSelected ? UIColor.red.cgColor : UIColor.lightGray.cgColor
}
}
Instead of the cell itself you can also apply the border to your baseView instead.

Passing data from custom UI cell to view controller

I am creating a pokedex app and the way I want it to work is basically there is a scroller at the top of the screen which allows you to select any pokemon and upon choosing the pokemon, underneath the scroller the entry for the pokemon will show up (bulbasaur will be there by default until a pokemon is selected because bulbasaur is the first pokemon with an ID of 1). To achieve this I have my view controller return two types of cells, the first being a "chooser cell" which is the scroller, and the second being a "description cell" which is the dex entry. I gave the view controller a data member called dex entry and return dex entry in the cellForItemAt function but the image of the cell is not changing (from bulbasaur to whichever pokemon I select). I print to the console what is the value of dex entry's pokemon every time a pokemon is selected so I am sure that the dex entry is being directly changed but I don't know why the image is not changing as well. Below are relevant parts of my code.
view controller (only part of it):
import UIKit
class PokeDexController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
var dexEntry = DescriptionCell()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "PokeDex 386"
collectionView?.backgroundColor = UIColor(red: 52/255.0, green: 55/255.0, blue: 64/255.0, alpha: 1.0)
//collectionView?.backgroundColor = UIColor.white
collectionView?.register(chooserCell.self, forCellWithReuseIdentifier: cellID)
collectionView?.register(DescriptionCell.self, forCellWithReuseIdentifier: descID)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if (indexPath.row == 0)
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! chooserCell
return cell
}
else{
let descCell = collectionView.dequeueReusableCell(withReuseIdentifier: descID, for: indexPath) as! DescriptionCell
dexEntry = descCell
return dexEntry
}
}
descriptionCell class:
import UIKit
class DescriptionCell: UICollectionViewCell
{
private var pokemon : Pokemon?
{
didSet
{
if let id = pokemon?._id
{
imageView.image = UIImage(named: String(id))
print("Pokemon with the id of " + String(id))
}
}
}
override init(frame: CGRect)
{
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setPokemon(poke: Pokemon)
{
self.pokemon = poke
}
func getPokemon() -> Pokemon
{
return pokemon!
}
let imageView: UIImageView =
{
let iv = UIImageView()
iv.image = UIImage(named: "1")
iv.contentMode = .scaleAspectFill
return iv
}()
func setupViews()
{
backgroundColor = UIColor(red: 52/255.0, green: 55/255.0, blue: 64/255.0, alpha: 1.0)
addSubview(imageView)
imageView.frame = (CGRect(x: frame.width/6, y: frame.height/30, width: frame.width/4, height: frame.height/4))
}
}
choosercell class (specifically the didSelectItemAt):
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath){
let poke = pokemon[indexPath.row]
print("Selected " + poke._name)
let vc = PokeDexController()
vc.dexEntry.setPokemon(poke: poke)
let name = vc.dexEntry.getPokemon()._name
print(name ?? "nothing there")
}
image of the app and the console output
any help is appreciated, thanks.
You need to change the dexEntry when you select a cell and reload the collection view cell.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath){
let poke = pokemon[indexPath.row]
print("Selected " + poke._name)
let cell = collectionView.cellForItem(at: IndexPath(row: 1, section: 0) as! DescriptionCell
cell.setPokemon(poke: poke)
collectionView.reloadItems(at: IndexPath(row: 1, section: 0))
}
Hope this helps.
I haven't solved my problem but I realize that the cell that I am returning in my viewController is independent of dexEntry so as far as I can cell, once that cell is set, it is set, so I now i will figure out how to reload things when a cell is selected so the cell that is returned has an image of a different pokemon.

How to access buttons in a UICollectionView from a target function set (Swift 3)

In my View Controller, I have a collection view that, when rendered, displays 3 cells, each of which has a label, and a button. The label displays the name of a color, and the button has a background image that displays a color swatch.
I want it so that whenever you click on one of the buttons, that button gets a dark border around it, while the other buttons get a light border on them, to indicate the clicked-on button as being "selected". Alternately, I could probably do this by changing the image out based on the selected state of the image - but my question remains the same.
How do I access the other two buttons, to toggle their properties?
I have a script implemented that allows me to add a border to the button that somebody clicked on - but I cannot figure out how to access the other buttons, in the other cells of the CollectionView to alter their border properties as well.
Here is my source code (with irrelevant/unrelated bits stripped out)
class trimSelectorVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var trimSelector: UICollectionView!
struct trimObject {
var trimName: String
var trimButton: String
var trimID: Int
}
var trimArray: [trimObject] = []
override func viewDidLoad() {
super.viewDidLoad()
trimArray.append(trimObject(trimName: "Chrome", trimButton: "chrome-swatch", trimID: 0))
trimArray.append(trimObject(trimName: "Gold", trimButton: "gold-swatch", trimID: 1))
trimArray.append(trimObject(trimName: "Gun Metal", trimButton: "gunmetal-swatch", trimID: 2))
trimSelector.delegate = self
trimSelector.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return trimArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! trimSelectionCell
//Set the label text
cell.trimLabel.text = trimArray[indexPath.item].trimName
//Set the image for the button
cell.trimButton.setImage(UIImage(named: trimArray[indexPath.item].trimButton), for: UIControlState.normal)
//Sets a target function for the button
cell.trimButton.addTarget(self, action: #selector(selectedSwatch), for: .touchUpInside)
return cell
}
func selectedSwatch(sender: UIButton) {
//These set the "selected" border to the button you clicked on.
sender.layer.borderWidth = 2
sender.layer.borderColor = UIColor(red: 83/255, green: 71/255, blue: 65/255, alpha: 1.00).cgColor
}
}
Can anybody please tell me how to access the other buttons in my "selectedSwatch" function?
There are various ways you can handle this. A UICollectionView view has a method visibleCells() that returns an array of it's visible cells. You could use that to get pointers to your cells. You would need a way to figure out which one is which. You could use indexPath(for: UICollectionViewCell) to figure out the index path of each cell, for example.
I don't know if this might help, but what about if you store the IndexPath on your struct on cellForItemAt method?
You will have:
struct trimObject {
var trimName: String
var trimButton: String
var trimID: Int
var idx : IndexPath
}
Then on:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! trimSelectionCell
....
trimArray[indexPath.item].idx = indexPath
....
}
And in your selectedSwatch method:
func selectedSwatch(sender: UIButton) {
//These set the "selected" border to the button you clicked on.
sender.layer.borderWidth = 2
sender.layer.borderColor = UIColor(red: 83/255, green: 71/255, blue: 65/255, alpha: 1.00).cgColor
if let cell = (sender.superview as? UICollectionViewCell) {
//Cell with the button selected:
let idx = collectionView.indexPath(for: cell)
//array of the other objects:
let allOtherObjects = trimArray.filter { ($0 as! trimObject).idx != idx }
allOtherObject.forEach({ (trimObj) in
let cell = collection.cellForItem(at: trimObj.idx)
//Do whatever yo need to do...
//cell.trimButton.layer
})
}
}
Its may be to late but still useful for somebody
Swift 4 version:
You can use sender superview as UiCollectionViewCell
* Consider hierarchy of sender in collection view cell
func selectedSwatch(sender: UIButton) {
let cell = sender.superview?.superview as! trimSelectionCell
//cell.yourbtn
}
Try this,
class trimSelectorVC: UIViewController, UICollectionViewDelegate,
UICollectionViewDataSource {
#IBOutlet weak var trimSelector: UICollectionView!
struct trimObject {
var trimName: String
var trimButton: String
var trimID: Int
var isSelected : String
}
var trimArray: [trimObject] = []
override func viewDidLoad() {
super.viewDidLoad()
trimArray.append(trimObject(trimName: "Chrome", trimButton: "chrome-swatch", trimID: 0,isSelected : "0"))
trimArray.append(trimObject(trimName: "Gold", trimButton: "gold-swatch", trimID: 1,isSelected : "0"))
trimArray.append(trimObject(trimName: "Gun Metal", trimButton: "gunmetal-swatch", trimID: 2,isSelected : "0"))
trimSelector.delegate = self
trimSelector.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return trimArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! trimSelectionCell
//Set the label text
cell.trimLabel.text = trimArray[indexPath.item].trimName
//Set the image for the button
cell.trimButton.setImage(UIImage(named:
trimArray[indexPath.item].trimButton), for: UIControlState.normal)
if(trimArray[indexPath.item].isSelected == "0"){
// button not clicked
// change shadow color of button
}
else
{
// button clicked
cell.trimButton.layer.borderWidth = 2
cell.trimButton.layer.borderColor = UIColor(red: 83/255, green:
71/255,blue: 65/255, alpha: 1.00).cgColor
}
// set tag to the button
cell.trimButton.tag = indexPath.item
//Sets a target function for the button
cell.trimButton.addTarget(self, action:#selector(selectedSwatch),
for: .touchUpInside)
return cell
}
func selectedSwatch(sender: UIButton) {
//These set the "selected" border to the button you clicked on.
let index = sender.tag
for obj in trimArray {
obj.isSelected = "0"
}
trimArray[index].isSelected = "1"
collectionView.reloadData()
}
}

Swift: Segmented control behaves in a weird way in UITableView Cell

Anytime I tap segmented control in UICell, immediately some other cell gets this segmented control in the same position. It looks like segmented control recognizes that not only this particular one was tapped but also some other one in other cell.
Have you ever encountered issue like this?
this is my custom cell implementation:
class QuestionYesNoCustomCellTableViewCell: UITableViewCell {
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var segmentControl: ADVSegmentedControl!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
segmentControl.items = ["TAK", "NIE"]
segmentControl.font = UIFont(name: "Avenir-Black", size: 12)
segmentControl.borderColor = UIColor.grayColor()
segmentControl.selectedIndex = 1
segmentControl.selectedLabelColor = UIColor.whiteColor()
segmentControl.unselectedLabelColor = UIColor.grayColor()
segmentControl.thumbColor = UIColor(red: 46.0/255.0, green: 204.0/255.0, blue: 113.0/255.0, alpha: 1.0)
segmentControl.addTarget(self, action: "segmentValueChanged:", forControlEvents: .ValueChanged)
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func segmentValueChanged(sender: AnyObject?){
if segmentControl.selectedIndex == 0 {
segmentControl.thumbColor = UIColor(red: 231.0/255.0, green: 76.0/255.0, blue: 60.0/255.0, alpha: 1.0)
segmentControl.selectedLabelColor = UIColor.whiteColor()
segmentControl.unselectedLabelColor = UIColor.grayColor()
}else if segmentControl.selectedIndex == 1{
segmentControl.thumbColor = UIColor(red: 46.0/255.0, green: 204.0/255.0, blue: 113.0/255.0, alpha: 1.0)
segmentControl.selectedLabelColor = UIColor.grayColor()
segmentControl.unselectedLabelColor = UIColor.whiteColor()
}
}
Also, I think it is worth to provide my tableView delegate methods implemented
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (dict2 as NSDictionary).objectForKey(dictKeysSorted[section])!.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: QuestionYesNoCustomCellTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell") as! QuestionYesNoCustomCellTableViewCell
cell.questionLabel.text = (dict2 as NSDictionary).objectForKey(dictKeysSorted[indexPath.section])![indexPath.row] as? String
if indexPath.row % 2 == 0 {
cell.backgroundColor = UIColor(red: 245.0/255.0, green: 245.0/255.0, blue: 245.0/255.0, alpha: 1.0)
}
else {
cell.backgroundColor = UIColor(red: 225.0/255.0, green: 225.0/255.0, blue: 225.0/255.0, alpha: 0.7)
}
return cell
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return dictKeysSorted[section]
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableCellWithIdentifier("CellHeader") as! CustomHeaderCell
headerCell.backgroundColor = UIColor(red: 20.0/255.0, green: 159.0/255.0, blue: 198.0/255.0, alpha: 1.0)
headerCell.headerLabel.text = dictKeysSorted[section]
return headerCell
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 70.0
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return dictKeysSorted.count
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 110.0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
To recap what the problem actually is: In every tableView cell there is a segment control. When I change the position of the one located in first row, I scroll down and see that segment control in row 5 also has been moved despite the fact it should be in the default position.
Thanks in advance
EDIT:
I recognized one of the biggest problem in solutions below - they are good as long as you don't use section in tableView. The thing is, from what I have discovered right now, in each sections the rows are counted over from 0.
This might be the cause when you are using reusing the cells, when you scroll the cell you changed will be shown again for another row.
To avoid this when you reuse cell make sure you reset the data in it also
In your case you have to check if the segmented value is changed then change the segmented control value also in cellForRowAtIndexPath
Please let me know if you need more explanation.
Here is a sample project for you sampleTableReuse
It's because of reusable nature of UITableViewCells. You must keep track in your datasource selected segment index for each row. Then in cellForRowAtIndexPath you must set it properly for each cell.
example
define somewhere an enum with possible Answers:
enum Answer {
case Yes
case No
case None
}
then define and init your answers' array:
var answer = [Answer](count: numberOfQuestions, repeatedValue: .None)
in your cell's implementation add a method to configure a cell with Answer
func setupWithAnswer(answer: Answer)
{
var selectedIdex = UISegmentedControlNoSegment
switch answer {
case .Yes: selectedIdex = 0
case .No: selectedIdex = 1
default: break
}
self.segmentedControl.selectedSegmentIndex = selectedIdex
}
and finally, in your cellForRowAtIndex do after dequeuing
cell.setupWithAnswer(answer: self.answers[indexPath.row])

Resources