When selecting multiple cells in my tabeview the cells out of view are being selected too. I understand that this is because i am reusing the cell and its maintaining its selection as i scroll down. I have found a few people with similar issues but cant translate their solutions across to resolve my issue. I have tried not dequeing a cell and just use:
let cell = NewBillSplitterItemCell()
but get:
unexpectedly found nil while unwrapping an Optional value
on the line:
cell.currentSplitters.text = splitterList
in the following code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
fetchBillItems()
let cell: NewBillSplitterItemCell = tableView.dequeueReusableCellWithIdentifier("NewBillSplitterItemCell") as! NewBillSplitterItemCell
let item = allItems[indexPath.row]
let numberOfSplitters = item.billSplitters?.count
if numberOfSplitters == 0 {
cell.currentSplitters.text = "No one is paying for this item yet."
} else {
var splitterList = "Split this item with "
let itemSplitters = item.billSplitters?.allObjects as! [BillSplitter]
for i in 0...Int((numberOfSplitters)!-1) {
if numberOfSplitters == 1 {
splitterList += "\(itemSplitters[i].name!)"
} else {
splitterList += ", \(itemSplitters[i].name!)"
}
}
cell.currentSplitters.text = splitterList
}
cell.name.text = item.name
cell.price.text = "£\(item.price!)"
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
if cell.accessoryType == .Checkmark
{
cell.accessoryType = .None
selectedItems.removeAtIndex(selectedItems.indexOf(allItems[indexPath.row])!)
} else {
cell.accessoryType = .Checkmark
selectedItems.append(allItems[indexPath.row])
}
}
}
I dont quite understand what to do and any help would be great. Thanks
In addition to what #Mike said, inside of cellForRowAtIndexPath you need an additional check because cells get reused.
Something along the line
let isSelected = selectedItems[indexPath.row].selected
if isSelected{
cell.accessoryType = .Checkmark
} else {
cell.accessoryType = .None
}
Same thing inside of didSelectRowAtIndexPath you should update the data source instead of relying on the UI of your cell for that condition.
Assuming your cell is nil, you should use
let cell = tableView.dequeueReusableCellWithIdentifier("..." forIndexPath:indexPath) as! NewBillSplitterItemCell
instead of
let cell= tableView.dequeueReusableCellWithIdentifier("...") as! NewBillSplitterItemCell
This ensures that cell will never be nil.
Also, I would check if the correct identifier is being used in all of your .xib .storyboard files.
Related
I have a tableViewCell that uses a checkmark in accessoryType of cell. I have a function that puts the contents of the cell into textField and similarly removes the text from the text field when it is unchecked.
It seems to work fine but if I check a cell and want to check a cell thats not visible (IOW) I need to scroll the tableView, the cell that was checked (is now not visible) seems to uncheck itself (Only when I check a new visible cell).
The multi select works with visible cells only.
Here is my code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath)
let row = indexPath.row
cell.textLabel?.text = painArea[row]
cell.accessoryType = .None
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//selectedRow = indexPath
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let row = indexPath.row
let cell = tableView.cellForRowAtIndexPath(indexPath)
if cell!.accessoryType == .Checkmark {
cell!.accessoryType = .None
} else {
cell!.accessoryType = .Checkmark
}
populateDescription()
print(painArea[row])
}
var painDescription = ["very sore"]
func populateDescription() {
painDescription.removeAll()
severityText.text = ""
for cell in tableView.visibleCells {
if cell.accessoryType == .Checkmark {
painDescription.append((cell.textLabel?.text)! + " ")
}
var painArea = ""
var i = 1
while i <= painDescription.count {
painArea += painDescription[i-1] + "~"
i = i + 1
}
severityText.text = painArea
}
I hope I am explaining myself adequately. I don't want the non visible cells to be unchecked and thus removed from my text field unless I uncheck it.
Any ideas would be most appreciated.
Kind regards
Wayne
It is happing because of reusability of Cell. Instead of setting Checkmark in didSelect try to set in the cellForRowAtIndexPath. Also you need to create model class like this to solve your problem
class ModelClass: NSObject {
var isSelected: Bool = false
//Declare other property that you are using cellForRowAtIndexPath
}
Now check this isSelected in cellForRowAtIndexPath like below.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath)
let row = indexPath.row
let modelClass = painArea[row]
cell.textLabel?.text = modelClass.name
if modelClass.isSelected {
cell.accessoryType = .Checkmark
}
else {
cell.accessoryType = .None
}
return cell
}
Now change your didSelect like this
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var modelClass = painArea[indexPath.row]
modelClass.isSelected = !modelClass.isSelected
self.tableView.reloadData()
populateDescription()
print(painArea[row])
}
Hope this will help you.
It's because whenever u scroll the cell out of view and scroll back, it will call the cellForRow again and set everything back to default, what you have to do is create a properly dataSource, whenever a cell got checked, you update the dataSource with a Bool indicate it has been checked, then set it back in the cellForRow or cellWillDisplay
am having this strange issue while am selecting any of the row from TableView another row is also get selected , say i have a tableView with multiple rows in my case its 11 and multiple selection with the accessory of tick mark is enabled (when i select a row a tick is marked on the selected row ) so when am selecting my first row , then row number 8 is also got selected (i can see the tick mark in the row number 8 but i selected only the row number 1 ) when i select another row number 2 my row number 9 is also get selected dont know why this is happening if anybody knows anything about this behaviour then please let me know it'll be so helpful for me , below is the code of didSelectRowAtIndexPath :
var selectedTextLabels = [NSIndexPath: String]()
var selectedTextLabelsName = [NSIndexPath: String]()
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
let cell = tableView.cellForRowAtIndexPath(indexPath) as! UsersTableViewCell
if (cell.accessoryType == UITableViewCellAccessoryType.Checkmark){
cell.accessoryType = UITableViewCellAccessoryType.None;
selectedTextLabels[indexPath] = nil
selectedTextLabelsName[indexPath] = nil
}else{
cell.accessoryType = UITableViewCellAccessoryType.Checkmark;
if (cell.accessoryType == UITableViewCellAccessoryType.Checkmark){
if let Id = cell.channelIDLbl?.text {
selectedTextLabels[indexPath] = Id
}
if let channelName = cell.lblChannelname?.text{
selectedTextLabelsName[indexPath] = channelName
}
}
}
cellForRowAtIndexpath:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UsersTableViewCell
if isFavoritesTabActive == true {
let object : RChannels = self.favoritesArray.objectAtIndex(indexPath.row) as! RChannels
cell.lblChannelname.text = object.channelName
let favorite = object.isFavorite
if favorite == "true" {
cell.favIcon.image = UIImage(named: "Favourite")
return cell }
else {
let object : RChannels = self.noteObjects.objectAtIndex(indexPath.row) as! RChannels
cell.lblChannelname.text = object.channelName
let favorite = object.isFavorite
if favorite == "true" {
cell.favIcon.image = UIImage(named: "Favorite")
}else {
cell.favIcon.image = UIImage(named: "unFavorite") }
return cell
}
checking inside the channelIDLbl array for existence of the cell's id at cellforRowAtIndexPath did the job
if ((selectedTextLabels[indexPath]?.containsString("\(cell.channelIDLbl)")) != nil){
cell.accessoryType = .Checkmark
}else {
cell.accessoryType = .None
}
for more detail please check this same question
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).
All,
Can you give me a source code of my below problem?
I have 100 user array and load that array value(username) in tableview.
Scenario:
When I click any tableviewcell, i store that value in new array( userArray) and also i have displayed right side discloser (checkmark).
But my problem is when i click again to deselect that user from tablecell my app has been crashed due to index out of bound.
I know problem is out of index, but how can i resolve that issue so if i click again on that tablecell it will remove user from userArray and i get new fresh userArray?
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let tagValue = 100 + indexPath.row
let cell: UITableViewCell = tableView.viewWithTag(tagValue) as UITableViewCell
if cell.accessoryType == UITableViewCellAccessoryType.None
{
cell.accessoryType = UITableViewCellAccessoryType.Checkmark
userToSendPost.append(self.users2[indexPath.row])
} else {
cell.accessoryType = UITableViewCellAccessoryType.None
let tempString = userToSendPost[indexPath.row] //tagvalue
let objcArray = userToSendPost as NSArray
let indexOfObject = objcArray.indexOfObject(tempString)
userToSendPost.removeAtIndex(indexPath.row)
}
}
sorry for my bad english.
You are approaching this incorrectly.
On the tableview set the property tableView.allowsMultipleSelection = true.
Then when you want to get the selected users...
let selectedIndexPaths = tableView.indexPathsForSelectedRows()
Then you can use the row property from those index paths to find the users.
var selectedUsers: [User] = []
for indexPath in selectedIndexPaths {
let user = theUsers[indexPath.row]
selectedUsers.append(user)
}
The multiple selection and the check mark is handled automatically by the tableview.
To follow your approach, I think this could help you ;)
But the approach of #Fogmeister is better !
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let tagValue = 100 + indexPath.row
let cell: UITableViewCell = tableView.viewWithTag(tagValue) as UITableViewCell
if cell.accessoryType == UITableViewCellAccessoryType.None
{
cell.accessoryType = UITableViewCellAccessoryType.Checkmark
userToSendPost.append(self.users2[indexPath.row])
} else {
let tempUserString = self.users2[indexPath.row] // get your user string
if contains(userToSendPost, tempString) { // check if exist on your array
userToSendPost.removeObject(tempString) // remove it
}
cell.accessoryType = UITableViewCellAccessoryType.None
}
}
// If you want to use Array, add this method
extension Array {
mutating func removeObject<U: Equatable>(object: U) {
var index: Int?
for (idx, objectToCompare) in enumerate(self) {
if let to = objectToCompare as? U {
if object == to {
index = idx
}
}
}
if(index != nil) {
self.removeAtIndex(index!)
}
}
}
When a cell is selected, I want it to be checked. I also want any other cell that is checked to have no accessory. Unfortunately, I get the error message which says I can't change the accessoryType. Is that because I am using the visibleCells() to get the cells? How to I overcome this error?
I have tried not using constants; I got the same error. I looked on the UITableView & UITableViewCell class reference, but I didn't see anything that could help me. My backup plan is to use a Picker View, but I'd prefer to use a Table View because of it's design.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath)
let cells = tableView.visibleCells()
for oldCell in cells {
if(oldCell.accessoryType == UITableViewCellAccessoryType.Checkmark) {
oldCell.accessoryType = .None //Cannot assign to 'accessoryType' in 'oldCell'
}
}
cell!.accessoryType = .Checkmark
day = cell!.textLabel!.text!
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
Looking at the API documentation, visibleCells() returns [AnyObject]. Due to Swifts type safety, it won't let you set accessoryType on AnyObject - it must be a UITableViewCell.
Try the following...
if let cells = tableView.visibleCells() as? [UITableViewCell] {
for oldCell in cells {
if oldCell.accessoryType == .Checkmark {
oldCell.accessoryType = .None
}
}
}