Enable editing mode just for a section in UITableView - ios

I have a tableView in which there is a section that is editable.
If I enable editing on the entire tableView, other cells while in editing mode are not selectable, so I need to enable the editing mode only on a specific section so that while other cells are selectable, the section is editable.
The reason that I need to set editing is those red square minus buttons that appear next to deletable cells.
Summary:
I need those red minus buttons next to cells, so I need to set editing as true, but if I do so, other cells won't be selectable thus I need to either set editing as true for a specific section, or add those red minus buttons without the editing mode.

You can implement canEditRowAtIndexPath method something like,
Obj-C
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 1) {
return YES;
}
return NO;
}
Swift :
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
if indexPath.section == 1 {
return true
}
return false
}
To enable selection during editing you need to set,
Obj-C
self.yourTableView.allowsSelectionDuringEditing = YES;
Swift
self.yourTableView.allowsSelectionDuringEditing = true

Swift 4/5
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
if indexPath.section == 1 {
return true
}
return false
}

more nice to have like this
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return indexPath.section == 0 ? true : false
}

Related

Cannot work out vertically expandable UITableViewCells to show buttons

I have my UITableViewCells working fine. I want to be able to click the menu button to expand the height of the cell to reveal a set of buttons as per the image:
I have code as follows:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == Globals.selectedRowIndex {
return 335
} else {
return 125
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if indexPath.row == Globals.selectedRowIndex {
Globals.selectedRowIndex = -1
} else {
Globals.selectedRowIndex = indexPath.row
}
tableView.reloadData()
}
The buttons all get squished together on the smaller size though. I thought they would just be cropped out of the view. I think I can get around this by having them hide on click. I haven't tried this yet.
I also cannot set a height for them as my constraints seem to not be available for anything inside the cell. I don't know why. I can set autoresizing but not constraints.
I think though there's probably a completely better way to do this which I'm missing. Any pointers will be greatly appreciated.
First iterate upto tableCount and assign false. For Example,
for i=0;i<tablecount;i++
{
self.isSelected.append(false)
}
For Example, If you have 5 cells means,You will be having Values like this:
isSelected = [false, false, false, false, false]
Then, While Expanding Cells Assign True.Otherwise assign False
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedIndex[indexPath.row] = indexPath.row;
if isSelected[indexPath.row]
{
isSelected[indexPath.row] = false
self.tableView.reloadRowsAtIndexPaths([tableView.indexPathForSelectedRow!], withRowAnimation: .Fade)
}
else
{
isSelected[indexPath.row] = true
self.tableView.reloadRowsAtIndexPaths([tableView.indexPathForSelectedRow!], withRowAnimation: .Fade)
}
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
if isSelected[indexPath.row]
{
return 275.0
}
else
{
return 110.0
}
}

tableView.addGestureRecognizer for one section of the tableViewController

I'm new to iOS, swift. I have two sections in my tableView. I want to be able to do a longPressGesture on the second section, and not the first, enabling the user to reorder tableview cells in the second section. How would I do that in swift? Would anyone kindly provide a simple sample code in Swift?
Thanks for your help, much appreciated!
If you just want to reorder move the cell for the particular you may add some button/action to enable/disable reorder , there is delegate which you can use
Your code can be like this:
//enable editing in the tableview to true when you want to enable reorder in your case may on the UILongPressGestureRecognizer action
//In viewDidLoad()
tblView.editing = true//set it to false to complete the reorder
The delegate methods can be use like this:
func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle {
return UITableViewCellEditingStyle.None
}
func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
//get the reorder change in the path, you can do operation on the array
let itemToMove:String = arrData[fromIndexPath.row]//get the old path of item
arrData.removeAtIndex(fromIndexPath.row)//remove item from old path
arrData.insert(itemToMove, atIndex: toIndexPath.row)//at item at new path in array
}
func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
//write code to allow reorder in the particular section/indexpath
if indexPath.section == 0 {
return false
} else {
return true
}
// Return false if you do not want the item to be re-orderable.
}
func tableView(tableView: UITableView, targetIndexPathForMoveFromRowAtIndexPath sourceIndexPath: NSIndexPath, toProposedIndexPath proposedDestinationIndexPath: NSIndexPath) -> NSIndexPath {
//check if the reorder is allow in the particular section/indexpath before the reorder is done, return the old path if you don't want to move at Proposed path
if sourceIndexPath.section != proposedDestinationIndexPath.section {
return sourceIndexPath
} else {
return proposedDestinationIndexPath
}
}
the UILongPressGestureRecognizer can be implemented on the tableview or the tableview cell based on the requirements
let longpress = UILongPressGestureRecognizer(target:self, action:#selector(HomeScreenTableViewController.longPressGestureRecognized))
tblView.addGestureRecognizer(longpress)
func longPressGestureRecognized() {
NSLog("Detected")
tblView.editing = true
}
or in tableview cell with same method as above
let longpress = UILongPressGestureRecognizer(target:self, action:#selector(HomeScreenTableViewController.longPressGestureRecognized))
cell.addGestureRecognizer(longpress)

How to hide UITableviewCell if it cannot be edited?

I have a UITableView show as below. All cells in the tableview can be edited except the last one.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
if indexPath.row == tableView.numberOfRowsInSection(0) - 1 {
return false
}
return true
}
The first picture shows the tableview in normal mode; the second shows the tableview in editing mode. I do not want the last cell to be editable, so I return false in the function. But I do not want the last cell to appear when the tableview is in editing mode. How to do that?
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.[TableViewName].dequeueReusableCellWithIdentifier("MainOfThecell") as? UITableViewCell
if [tableViewName].editing == true {
if cell.editing == false {
cell.hidden = true
}
}
return cell
}
Explanation :
[tableViewName].editing => This will return a bool on whether the tableView is in editing mode
cell.editing => This will return a bool on whether the cell is in edit editable state or not If the return is false then cell.hidden = true will hide the cell from the tableView.
NOTE:
When the user clicks in the button to edit the table execute [tableViewName.reloadData()] to force cellForRowAtIndexPath to be executed
If you want only the last cell to be hidden then check for the indexPath.row of the cell to determine it's position

Disable selection of a cell without UserInteractionEnabled in Swift

I have a cell with a button and setting UserInteractionEnabled to false disables the button too. Now if I use cell.selectionStyle = UITableViewCellSelectionStyle.None. Although the cell doesn't get highlighted but I loose the previous selected cell. Any way to overcome this?
Implement the UITableView delegate method
tableView(_ tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath?
Return nil for the row you don't want to be able to be selected.
... and for Swift 3, the delegate method is:
public func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if indexPath.row == 0 {
return nil
} else {
return indexPath
}
}

UITableView disable swipe to delete, but still have delete in Edit mode?

I want something similar as the Alarm app, where you can't swipe delete the row, but you can still delete the row in Edit mode.
When commented out tableView:commitEditingStyle:forRowAtIndexPath:, I disabled the swipe to delete and still had Delete button in Edit mode, but what happens when I press the Delete button. What gets called?
Ok, it turns out to be quite easy. This is what I did to solve this:
Objective-C
- (UITableViewCellEditingStyle)tableView:(UITableView *)aTableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Detemine if it's in editing mode
if (self.tableView.editing)
{
return UITableViewCellEditingStyleDelete;
}
return UITableViewCellEditingStyleNone;
}
Swift 2
override func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle {
if tableView.editing {
return .Delete
}
return .None
}
Swift 3
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
if tableView.isEditing {
return .delete
}
return .none
}
You still need to implement tableView:commitEditingStyle:forRowAtIndexPath: to commit the deletion.
Just to make things clear, swipe-to-delete will not be enabled unless tableView:commitEditingStyle:forRowAtIndexPath: is implemented.
While I was in development, I didn't implement it, and therefore swipe-to-delete wasn't enabled. Of course, in a finished app, it would always be implemented, because otherwise there would be no editing.
You need to implement the CanEditRowAt function.
You can return .delete in the EditingStyleForRowAt function so you can still delete in editing mode.
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
if tableView.isEditing {
return true
}
return false
}
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
return .delete
}
Swift Version:
override func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle {
if(do something){
return UITableViewCellEditingStyle.Delete or UITableViewCellEditingStyle.Insert
}
return UITableViewCellEditingStyle.None
}
Basically, you enable or disable editing using the methods
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
If editing is enabled, the red deletion icon appears, and a delete conformation requested to the user. If the user confirms, the delegate method
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
is notified of the delete request. If you implement this method, then swipe to delete is automatically made active. If you do not implement this method, then swipe to delete is not active, however you are not able to actually delete the row. Therefore, to the best of my knowledge, you can not achieve what you asked for, unless using some undocumented, private APIs. Probably this is how the Apple application is implemented.
On C#:
I had the same issue where it was needed to enable/disable rows with Delete option on swipe. Multiple rows needed to be swiped left and get deleted, keep them in another colour. I achieved using this logic.
[Export("tableView:canEditRowAtIndexPath:")]
public bool CanEditRow(UITableView tableView, NSIndexPath indexPath)
{
if (deletedIndexes.Contains(indexPath.Row)){
return false;
}
else{
return true;
}
}
Note that deletedIndexes are a list of indexes which are deleted from the table without duplicates. This code check whether a row is deleted, then it disables swipe or vice versa.
The equivalent delegate function is Swift is canEditRowAtIndexPath.
I came across this problem either and fixed with codes below. hope it will help you.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
BOOL deleteBySwipe = NO;
for (UIGestureRecognizer* g in tableView.gestureRecognizers) {
if (g.state == UIGestureRecognizerStateEnded) {
deleteBySwipe = YES;
break;
}
}
if (deleteBySwipe) {
//this gesture may cause delete unintendedly
return;
}
//do delete
}
}

Resources