How reorder row in customize UITableViewCell - objective-c

So far I have customized my UITableViewCell with the icon reorder row.
I want my cell can be reordered when long press on the reorder icon and then move it.
The image below is what I have expected.
I have read many document they provided only long press on cell and move it but I don't want it because long press on cell I have my another gesture is renaming it; and also I don't want to use the edit/done of UITableView default from OS; so I decided to put reorder icon but I can not do, because I'm new in objective c.
Can anyone help by providing sample code here?
Thank for reading.

Override two methods of UITableViewDataSource
For reordering to work you will first need to bring the Table in editing mode. Which you can either do it from viewDidLoad or from a IBAction method.
-(void) viewDidLoad{
[super viewDidLoad];
self.myTableView.editing = YES;
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath{
//Manipulate your data array.
}
By Default editing style is UITableViewCellEditingStyleDelete. So if you don't override the below method it will show both rearrange as well delete icon.
-(UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewCellEditingStyleNone;
}
Since, you have long gesture of your cell causes issue with the long gesture of the re-ordering of cell. You can add a UIView to the cell. Add all your elements in this UIView. Now, attach a UILongPress Gesture to this UIView (not to the cell.).

Related

Make 'edit' button trigger edit-mode on UITableView

This might be a very stupid question, but I have very little knowledge about Xcode, Objective-C and iOS development in general.
What I have is a UITableViewController, with a number of cells. I've gotten the 'add' and delete (swipe to delete) functions to work, but I cannot figure out how to connect the 'edit' button to a function that will trigger the editing mode of that TableView, so I can rearrange the order of the elements.
I have un-commented these functions:
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
XYZToDoItem *itemToMove = [self.toDoItems objectAtIndex:fromIndexPath.row];
[self.toDoItems removeObjectAtIndex:fromIndexPath.row];
[self.toDoItems insertObject:itemToMove atIndex:toIndexPath.row];
[self.tableView reloadData];
}
As you can see I have also made a few additions to the moveRowAtIndexPath method following the documentation.
In addition there are these functions;
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[self.toDoItems removeObjectAtIndex:indexPath.row];
[self.tableView reloadData];
}
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
As previously stated the "swipe to delete" function works, I don't know how, but I simply just uncommented these last two methods and edited one a bit to fit my code.
To be more specific, what I'm asking is how can I link the 'edit' button I have in my Navigation Controller to trigger the edit mode for the TableView, thereby showing the three lines (?) known from the TableViews editing mode (and letting me drag the elements to an order I want).
Thanks in advance for all your help! :)
If this is a UITableViewController you have two options. Either use the built-in edit button, which will give you the automatic Edit/Done button change. Use self.editButtonItem in your navigation bar:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
Or you could use setEditing:animated to change editing state of the tableViewController:
// self is a UITableViewController
[self setEditing:YES animated:YES];
If your viewController is not a subclass of UITableViewController you can set the editing mode of the tableView:
[self.tableView setEditing:YES animated:YES];
If you have a UITableViewController you should use the first way because it is most convenient.

Cannot make table re-order to work

From the UITableViewCell showsReorderControl docs:
For the reordering control to appear, you must not only set this property but implement the UITableViewDataSource method tableView:moveRowAtIndexPath:toIndexPath:. In addition, if the data source implements tableView:canMoveRowAtIndexPath: to return NO, the reordering control does not appear in that designated row.
I've got the both in my tableviewController :
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
NSLog(#"move from:%d to:%d", fromIndexPath.row, toIndexPath.row);
//just for test
}
My cell properties including re-order controls :
And yet I can't see the re-order control, what am I missing?
Have you put your UITableView into editing mode via
[tableView setEditing:YES animated:YES];
?
The first method you posted is needless, by the way, as that is the default behavior.
I think you need to actually get into edit mode for the table. Either try it in code when your view appears or create a button that will do it.
[tableView setEditing:YES animated:YES];

Unable to move rows in a UITableView even when grabber are displaying

I have a somewhat weird issue, and I can't quite figure out what am I missing. I have a UITableView which is editable in place (i.e. when my UI is loaded, I send my table the setEditing:YES animated:YES message). The last row in the table is intended to be the "Add New" row. All rows except the last row in my table can be moved around. None of the rows can be deleted.
The rows show up correctly, and the grabbers shows up on the right side of all rows except the last row (as intended). The problem is that I am unable to move the rows. When I tap on the grabber to move the row, it kind of jiggles in place, but I can't drag it up or down. Here's the relevant snippet of code:
- (UITableViewCellEditingStyle)tableView:(UITableView *)aTableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleNone;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == [self.itemArray count] ) {
return NO;
}
return YES;
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == [self.itemArray count]) {
return NO;
}
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
Item *item = [self.itemArray objectAtIndex:fromIndexPath.row];
[self.itemArray removeObjectAtIndex:fromIndexPath.row];
[self.itemArray insertObject:item atIndex:toIndexPath.row];
}
- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath {
if ([proposedDestinationIndexPath row] < [self.itemArray count]) {
return proposedDestinationIndexPath;
}
NSIndexPath *betterIndexPath = [NSIndexPath indexPathForRow:[self.itemArray count]-1 inSection:0];
return betterIndexPath;
}
On trying to debug, it seems that the tableView:moveRowAtIndexPath: gets called almost immediately even as I am holding on to the grabber (i.e. I have not lifted my finger yet). Moreover, my tableView:targetIndexPathForMoveFromRowAtIndexPath:proposedDestinationIndexPath: does not get invoked at all.
Any thoughts on what am I doing wrong? Any suggestions on what I should try to fix this issue?
I had the same problem, and #wilsontgh & #Andy responses took me in the right direction. However I couldn't afford removing the needed PanRecognizer from the superview, nor disabling it in certain views.
What worked for me was to set this on the main view recognizer, which avoids cancelling touches handled to other views/recognizers.
panRecognizer.cancelsTouchesInView = NO;
I had this problem too. Check if you have a UIGestureRecognizer (for me it was a UIPanGestureRecognizer) that is attached to the UITableView's superviews. I removed it and the reordering worked.
In my case, it is the "hide nav bar on swipe" feature that caused the re-order to fail. Therefore make sure it is turned off if you need the UITableView re-order feature.
navigationController?.hidesBarsOnSwipe = false
Note that navigation controller is shared across all view controllers of the same navigation stack, therefore one screen can affect all other ones even after pushing/pop-ing operations.
I recently had the same problem and this thread pointed me in the right direction, but the better approach is to use the gesture recognizer's delegate method gestureRecognizerShouldBegin: to determine if you are in a situation where you want to process it or not.
In my situation I had a swipe gesture on a main view controller and was displaying a table in a child view controller. In gestureRecognizerShouldBegin: I return a NO when the child view controller is active, which allows the table to see every gesture and the container view controller doesn't handle anything.
The method tableView:moveRowAtIndexPath:toIndexPath: does get called immediately upon touching the control. From the UITableViewDataSource Protocol Reference:
The UITableView object sends this message to the data source when the user presses the reorder control in fromRow.
So this is expected behavior.
I believe the problem is in your tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath:. Do you have a specific reason to include this method? Have you tried leaving it out and going with the proposed position?
For example, it seems that ([proposedDestinationIndexPath row] < [self.itemArray count]) will always return true...
In my case, the problem was in the keyboardDismissMode property.

Allow a user to edit the text in a UITableView cell

I am creating a "favorite pages" menu in my tableview application, where users can save their favorite web pages and then navigate to them easier through the menu.
For ease of remember which link is which, I want to allow the user to click a button that says "edit," somehow select the cell that they want to edit the name of, and then type in the new name and have the cell rename itself to what the user typed in. I am currently using the built in settings application to save the link data.
I don't need to know every aspect of what I just asked. I just want to know if it is possible for the user to edit the cell text of a tableview, and what methods I would use to do so.
I've seen other questions that cover similar ground, but generally from a more programmatic basis.
The user cannot directly edit the cell text of a tableview. (Technically, it would be the cell.textLabel.text). However, if they go into an "edit" mode, you could easily display a UITextField in the cell (or in a modal view) which is prepopulated with the current value, let them edit it, save and then update the cell.textLabel.text value yourself.
Your UITableView cells are created here, usually
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
(one of the delegates for the UITableView).
So if you subclass UITableViewCell and expose a text property, you can do what you want, including holding a UITextField instance in the Cell. Make sure to use the dequeue stuff as you would normally.
Then, when the user touches the UITableViewCell you can give the UITextField the focus:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.thatTextFieldIMentioned becomeFirstResponder];
}
If you make the UITableViewCell subclass a UITextFieldDelegate and make it the delegate for the text field, you can easily handle these annoyingly hard-to-remember methods:
- (void)textFieldDidEndEditing:(UITextField *)textField {
    NSLog(#"yeah inform someone of my change %#", textField.text);
}
- (BOOL)textFieldShouldClear:(UITextField *)textField {
    return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [textField resignFirstResponder];
    return YES;
}
So,I think the best way is use tabelview cell editstyle.
You can use tableview delegate set cell editstyle. You can do something you want when user edit the cell.
Set cell style in:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
Do things when user click edit button in:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath

More control over updating UITableView and UITableViewCell

I am trying to implement expanding UITableViewCells where I have complete control over the animation of the transition from collapsed to expanded and back.
I have an array, cellDataList, that holds the state of my UITableViewCells, i.e. height, color, data, isExpanded etc.
I implement:
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath == selectedIndex)
return expandedHeight;
return collapsedHeight;
}
and I implement:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
/*
some logic that tests if the indexPath is already selected,
which will collapse the cell, if a previous cell needs to be collapsed before the
new expands etc.
*/
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationNone];
[tableView endUpdates];
}
My cell is a custom UITableViewCell where I build my content in drawRect:
The cell has a UIView (background) that serves as a custom background for the cell, this view I would like to
animate from collapsed to expanded and back, not just setFrame in one 'jump', as it does now.
I can't really figure out where to 'intercept' the tableView updater chain, to achieve this.
When I do the [tableView beginUpdates] - [tableView endUpdates] above, it starts to get a bit obscure what happens.
I assume
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
is called to update the cells
at the indexes specified. This in turn will ask heightForCellAtIndexPath which height to set the cell. If I set a breakpoint in my custom cells setFrame: method
I can see the caller is
[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:withIndexPath:] ().
This sets the new frame for the cell, then I assume [cell setNeedsDisplay] is called somehow so the cell can render for the frame provided.
The [cell setNeedsDisplay] will eventually call my custom drawRect: and render the content of the cell. This works as expected, but I can't figure
out where and how I will animate the frame for my background, also in a way so it is only done when clicked and not every time setFrame: is called (during scrolling etc).
When I do the above the tableView animates to make room for the new cell size, it is
this animation I would like to 'mimic' with the background view on my cell.
I see this effect in apps like Tweetbot and path but I can't seem to replicate it.
Hope some one can help me in the right direction, thanks:)
I am not totally sure that i understood your question exactly:) But you can use Key Value Observer (KVO) to check when you need to play your animation then call your table reload method.
- (void)reloadTable
{
if (self.isViewLoaded) {
[self.tableView reloadData];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
[self reloadTable];
}
The best way to do this is to add a dummy cell below the cell that was tapped. I explain this thorougly here.
Basically:
Keep track of what cells are been tapped in your didSelectRowAtIndexPath and update it appropriately. Which one needs to be deleted? Where will the new dummy cell be next?
Return the correct account whenever the dummy cell is present.
Account for the fact that the dummy cell is not part of your model. Offset the indexPath.