UITableView disable rowEditActionForRowAtIndexPath for certain rows - ios

I want to implement the "swipe to edit" function for certain rows. If all conditions are fulfilled, the swipe function should be enabled. If not, the row should remain static without any action rowEditAction assigned.
My code looks something like this:
func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? {
var noAction = UITableViewRowAction()
var logoutAction = UITableViewRowAction(style: .Default, title: "Log Out ") { (action: UITableViewRowAction!, indexPath: NSIndexPath!) -> Void in
if(indexPath.row == 0){
//do stuff when button pressed
self.tableView.reloadData()
}
else if(indexPath.row == 1){
//do stuff when button pressed
self.tableView.reloadData()
}
}
}
if(indexPath.row == 0 && conditionsFulfilled()){
return [logoutAction]
} else if(indexPath.row == 1 && conditionsFulfilled()){
return [logoutAction]
}
return [noAction]
}
This code does not disable the edit function, it just shows an empty UITableViewRowAction for rows that should not view the logoutAction. How do I get this done properly?

The UITableViewDataSource has a method entitled canEditRowAtIndexPath that according to Apple:
The method permits the data source to exclude individual rows from being treated as editable. Editable rows display the insertion or deletion control in their cells. If this method is not implemented, all rows are assumed to be editable...
So you can do something like this for example:
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// in this case I only allow edit from the third cell hereinafter.
return indexPath.row > 3 ? true : false
}
I hope this help you.

Related

UITableView sections with single and multiple selection

How do I make an UITableView which allows sections to either have single or multiple selection?
I have a single UITableView with multiple sections. I want some sections to allow multiple selections and some to only allow a single selection. This is what I currently have:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
// Get the current question
if var question = self.questions[indexPath.section] as? MultipleChoiceQuestion {
// If question allows multiple selection set a checkmark and update question with selected answer
if question.hasMultiValue {
cell?.accessoryType = .checkmark
question.givenAnswer = question.answers[indexPath.row]
} else {
// Question is single selection only. This entire part goes wrong.
if let givenAnswerIsEmpty = question.givenAnswer {
cell?.accessoryType = .none
} else {
self.questions[indexPath.section].givenAnswer = question.answers[indexPath.row]
cell?.accessoryType = .checkmark
}
}
}
}
My objects all have the property hasMultiValue which indicates if a section should allow multiple selections or not. They also have the property givenAnswer which could be seen as a "isSelected" flag. The code above only works for multi selection.
I've been searching around for a solution. There are a few questions like this one but the solution involves using the delegate method didDeselectRowAt. This method won't get called unless I physically deselect the current cell which is not what I want.
What I do want is for example:
If you select row1 and change your mind to row2, you should be able to select row2 which automatically deselects row1.
You didn't try willSelectRowAt?
You can try this code, this is tested on a table view controller
var selectedItemInSection1: Int?
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
// Just return the same index path if the section was not the second one
guard indexPath.section == 1 else { return indexPath }
// Get the previously selected item, and deselect it
if let prev = selectedItemInSection1 {
tableView.deselectRow(at: IndexPath.init(row: prev, section: 1), animated: true)
}
// Save the newly selected item index, to be deselected when other is selected in the same section
selectedItemInSection1 = indexPath.row
// Select the item
return indexPath
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
guard indexPath.section == 1 else { return }
// If it is in the second section, indicate that no item is selected now
selectedItemInSection1 = nil
}

swift **willSelectRowAtIndexPath** enable and disable options

I have Options such as:
None
Had once
Every time
SomeTime
now if none is selected i want other options to be disabled and disable none if any of the options or all are selected.
I have done
func tableView (tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
// first 3 rows in any section should not be selectable
if indexPath.row <= 1 {
return nil
}
return indexPath
}
This lets me to select only others except none. How can i achieve my requirements. Any ideas will be really appericiated.
Thanks.
Take a global variable to keep track of the current selected row and modify the code in your willSelectRowAtIndexPath method as follows :
var selectedRow = -1 //Global Variable
public func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath?
{
if(indexPath.row>0 && selectedRow == 0) //When "None" option row is selected & user tries to select any other row.
{
return nil
}
else if(indexPath.row == 0 && selectedRow > 0) //When any row other than "None" is selected and user tries to select "None" option row
{
return nil
}
else //When no row is selected or rows other than "None" are selected and user tries to select any row other than "None" row.
{
selectedRow = indexPath.row
return indexPath
}
}
Tested the above code and its working as per your requirements. Do let me know if thats what your were looking for. Cheers.

Two custom tableViewCells in UITableView

I am trying to create a contacts page where you can see all your contacts with a friend request cell showing up when you receive a friend request, but not there when you do not have any. At the moment, both custom cells work fine. The issue I have is that the contactRequestTableViewCell overlaps the first cell of the contactListTableViewCell.
I have researched other questions about two custom tableviewcells and none are quite having the same issues that I am facing.
Here is my executing code at the moment, I am returning 2 sections in the table view.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! ContactListTableViewCell
let requestCell = tableView.dequeueReusableCellWithIdentifier("requestCell", forIndexPath: indexPath) as! ContactRequestsTableViewCell
let user = OneRoster.userFromRosterAtIndexPath(indexPath: indexPath)
if (amountOfBuddyRequests > 0) {
if (indexPath.section == 0) {
requestCell.hidden = false
cell.hidden = false
requestCell.friendRequestLabel.text = "test"
} else if (indexPath.section >= 1) {
cell.contactNameLabel!.text = user.displayName;
cell.contactHandleLabel!.text = "# " + beautifyJID(user.jidStr)
cell.contactHandleLabel!.textColor = UIColor.grayColor()
OneChat.sharedInstance.configurePhotoForImageView(cell.imageView!, user: user)
}
return cell;
}
else { // if buddy requests == 0
requestCell.hidden = true
cell.contactNameLabel!.text = user.displayName;
cell.contactHandleLabel!.text = "# " + beautifyJID(user.jidStr)
cell.contactHandleLabel!.textColor = UIColor.grayColor()
print ("This is how many unreadMessages it has \(user.unreadMessages)")
// If there is unread messages for a person highlight it blue
// However this feature isn't working right now due to unreadMessages bug
if user.unreadMessages.intValue > 0 {
cell.backgroundColor = .blueColor()
} else {
cell.backgroundColor = .whiteColor()
}
OneChat.sharedInstance.configurePhotoForCell(cell, user: user)
return cell;
}
}
This is the current output that I have right now, my cells that have "test" are covering up other contactListTableViewCells.
The function tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell should always return one and the only one TableViewCell you want at indexPath, so you don't want to always return cell of type ContactListTableViewCell.
According to documentation, the cellForRowAtIndexPath tableView method asks for the cell at the indexPath, which means literally there can only be one cell at certain row of a certain section, so returning two cells is not an option.
I suggest you use two arrays to store the requests and contacts information. For example, you have arrays requests and contacts. Then you can tell the tableView how many rows you want:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return requests.count + contacts.count
}
and then in cellForRowAtIndexpath you do something like:
override func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row < requests.count {
// return a request cell
}
else {
// return a contact cell
}
}
I'm only using one tableView section here. If you still want two sections you can simply return 2 in numberOfSections function and add if statements in cellForRowAtIndexPath for indexPath.section.
Hope this helps.
It turns out that the issue was dealing with the data sources. My data sources were not pointing to the correct tableviewcell. This resulted in them pointing to an incorrect cell. This issue was fixed by remaking the data sources system that was in place. This issue will not affect most as the data sources will point to the correct tableviewcell by default.
Contrary to what another poster said, you can in fact display two or more custom cells in a single table. This is how I fixed the tableView display issues:
var friendRequests = ["FriendRequest1", "FriendRequest2"]
var contacts = ["User1","User2","User3","User4"]
var amountOfBuddyRequests = 1
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if (amountOfBuddyRequests > 0) {
return 2
}
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (amountOfBuddyRequests > 0) {
if (section == 0) {
return friendRequests.count
}
}
return contacts.count
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if (amountOfBuddyRequests > 0) {
if (indexPath.section == 0) {
let requestCell = tableView.dequeueReusableCellWithIdentifier("requestCell") as! ContactRequestsTableViewCell
requestCell.friendRequestLabel.text = friendRequests[indexPath.row]
requestCell.onButtonTapped = {
self.friendRequests.removeAtIndex(indexPath.row)
self.tableView.reloadData()
}
requestCell.addButtonTapped = {
self.addUser(self.friendRequests[indexPath.row])
self.friendRequests.removeAtIndex(indexPath.row)
self.tableView.reloadData()
}
return requestCell
}
}
let friendCell = tableView.dequeueReusableCellWithIdentifier("FriendCell") as! ContactListTableViewCell
friendCell.contactNameLabel.text = contacts[indexPath.row]
return friendCell
}

different methods on one standard headCell in func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{

I have 2 tableviews in the same view controller: "studentsTable" and "professorsTable". I wanted to create a standard head cell for both tables. I subclassed the UItableViewCell and created headCell type. Then assigned one headCell to both if indexPath.row==0. Everything went smooth, however, there is 1 problem. Whenever I click the "addingButton" I want "addStudent" function to be called if it was on "studentsTable" and "addProfessor" if it was on "ProfessorsTable". Obviously I can just create two headCells, one for each of the tables but I wonder if there is a more professional way of handling this with writing less code.
Here is the code I suppose may work if the right "if" condition is given.
Thanks in advance
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
var actionType:String=String()
if(/*WHAT CONDITION SHOULD I PUT?*/)
{actionType="addStudent"}
else
{actionType="addProfessor"}
if(indexPath.row==0)
{
var theHeadcell : headCell = tableView.dequeueReusableCellWithIdentifier("headCell") as headCell
// cell setup
if(actionType != "")
{
addButton.addTarget(self, action: NSSelectorFromString(actionType), forControlEvents: UIControlEvents.TouchUpInside)
}
return theHeadCell
}
else
{
if (tableView==self.studentsTable)
{
var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("studentsCell") as UITableViewCell
// cell setup
return cell
}
else
{
var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("ProfessorsCell") as UITableViewCell
// cell setup
return Cell
}
}
}
if tableView == self.studentsTable
{actionType = "addStudent"}
else if tableView == self.professorsTable
{actionType = "addProfessor"}

didDeselectRowAtIndexPath only called after pressing another cell [duplicate]

This question already has an answer here:
Table view didSelectRowAtIndexPath, only works after selecting second click
(1 answer)
Closed 7 years ago.
So here's the thing:
I implemented a UITableViewController with a custom cell..
Then i replaced the automatic segue cell to viewcontroller (storyboard) with viewcontroller to viewcontroller.
Code from my UITableViewController:
// MARK: - Tableview managment
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var lobbycell = tableView.dequeueReusableCellWithIdentifier("lobbycell") as! LobbyTableViewCell
let row = indexPath.row
if let channel = lobbies[row].lobbyName {
lobbycell.lobbynameLabel.text = channel
}
if let usercount = lobbies[row].users?.count {
lobbycell.usercountLabel.text = "Anzahl Spieler: \(usercount)"
}
if let host = lobbies[row].host?.username {
lobbycell.hostnameLabel.text = "Host: \(host)"
}
return lobbycell
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lobbies.count
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
let selectedLobby = lobbies[indexPath.row]
var loading = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
PFLobby.joinLobbyInBackgroundWithBlock(selectedLobby, user: PFUser.currentUser()!) { (success, error) -> Void in
loading.hide(true)
if success {
self.performSegueWithIdentifier("joinLobby", sender: self)
} else {
}
}
}
Now here's the problem:
If I click on a cell it's going to get gray (like selected) but won't call the didDeselectRowAtIndexPathfunc..
Only If I click on another cell it's going to call the didDeselectRowAtIndexPathwith the gray (like selected) cell's indexPath.
Are you confused with didSelectRowAtIndexPath and didDeselectRowAtIndexPath?
The latter will only be called if it's deselected, hence why it's called after you select another cell.
If you want to call the didDeselectRowAtIndexPath, then add the method deselectRowAtIndexPath in didSelectRowAtIndexPath and it will deselect right way.
This is expected behavior. tableView(_:didDeselectRowAtIndexPath:) will only be called after another cell is selected.
If you want, it is possible to intercept the selection event in tableView(:_willSelectRowAtIndexPath:):
override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
if let selectedIndex = tableView.indexPathForSelectedRow() where selectedIndex == indexPath {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
return nil
}
return indexPath
}
The index path returned by this method will be selected. If you return nil, nothing will get selected.

Resources