I want to implement a method to be able to reorder the rows of a tableview.
In each section i only have one row. When the editing-state of the tableview is enabled, the move indicators appear as expacted. But when i drag one of the cell to another position, the cell immediatly (after approximatly 1 s) pops back to the original position. This is weired to me, because i implemented such a reorder functionality already. The difference between those two projects are that the new project implements a bunch of gesture recognizers (eg UILongPressGestureRecognizer, UIPanGestureRecognizer, UIPinchGestureRecognizer,..). I already thought about the possibility that one of the gesture recognizers is blocking the drag action of the tableviewcell but its not.
You can see the code below:
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath {
//return [NSIndexPath indexPathForRow:0 inSection:3];
return nil;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
NSLog(#"%#", destinationIndexPath);
}
Can anyone help me pls?
UPDATE:
I found out that the moved tableviewcell is beeing moved to the original destination index path. Does that indicate, that a reloadData on the table view has happened?
2nd UPDATE:
Because it was menthioned by Andrei Shender why i return nil in the tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath: method you can find my updated code below.
- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath {
return proposedDestinationIndexPath;
//for testing purpose only
//return nil;
}
Btw when changing my code, i found out, that this method never gets called. Regarding to Apples documents about reordering tableviewcells it should get called.
Thanks to this posting I figured out, what the problem was. Combining a UIPanGestureRecognizer and a UITableView with the reorder/move funcationality is a tricky thing.
When i edit the tableview, i remove the gesture recognizer (of the viewcontroller's view) and when the editing is disabled again, i add the gesture recognizer again.
Be sure to update your data source in the method :
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
Related
I am facing a problem while developing Drag&Drop iOS11 feature to allow send and receive data between apps.
In the tableView of a view controller, with it's dragDelegate and dropDelegate assigned on viewDidLoad to the view controller itself, I implement the
- (NSArray<UIDragItem *> *)tableView:(UITableView *)tableView itemsForBeginningDragSession:(id<UIDragSession>)session atIndexPath:(NSIndexPath *)indexPath method, but it is never being called.
The drop behavior is working correctly and the table view can consume items from other apps such Photos or Notes. But when I tap a table view row for a couple of seconds, nothing happens. The lift animation doesn't occur and the drag delegate isn't fired.
Any thoughts? Am I missing anything required?
On iPhone, besides setting the dragDelegate you must also set dragInteractionEnabled on the UITableView to true. On iPad, this property is set to true by default.
Please try below delegate methods for re-ordering of cell.
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleNone;
}
Implement the following UITableView datasource method in your project.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
[dataArray exchangeObjectAtIndex:sourceIndexPath.row withObjectAtIndex:destinationIndexPath.row];
[self.tableView reloadData];
}
I need to keep my first cell always located at top of tableview when i move others cell.I spent a lot of time and many ways button i haven't figure out that how to solve this problem.
This is my code:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//...do something to custom first cell design from xib file
//...do some thing to custom normal cells(cells at below of first cell)
[firstcell setEditing:NO animated:YES];
firstcell.userInteractionEnabled=NO;
if (indexPath.row==0)
{
return firstcell;
}
else
{
return cell;
}
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0) // Don't move the first row
return NO;
return YES;
}
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
// i just change datasource for tableview at here
}
And there is my tableview when I move cell (normal cell).
I wanna keep first cell (blue cell) always be at top and not be interact by others cell.
You need to implement one more delegate method:
- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath {
if (proposedDestinationIndexPath.row == 0) {
// Don't allow a row to be moved to the first row position
return [NSIndexPath indexPathForRow:1 inSection:0];
} else {
return proposedDestinationIndexPath;
}
}
This code assume you only have one section in your table view.
The point of this method is to tell the table view that if the proposed destination for the row being moved isn't appropriate, the returned value should be used. As written here, any attempt to move a row to the top will result in it being moved just below the top row.
Basically the problem I'm seeing seems like an Apple bug. The problem is after scrolling the table, the first tap on any row only highlights it. A second tap is needed to actually select or deselect it. I noticed that this problem happens most of the time. Very few times it will work as expected, but I haven't noticed any pattern as to when it works.
This issue only happens if theTableView.bounces = NO; otherwise, it works perfectly.
I verified this by implementing the appropriate delegate methods.
First tap on any row after scrolling, I get these call backs
-(BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath
-(void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath
-(void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath
Subsequent taps on the same or a different row after scrolling, I get these call backs
-(BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath
-(void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath
-(void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath
//then
-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
//or
-(NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
I have seen other similar questions, but all of them are using a table inside another scroll view, which isn't the case here.
Has anyone found a fix or a workaround for this? I tried on iOS 7.0 ... 8.2 and the problem is present on all of them.
Thanks!
After a lot more testing, I found that the problem happens when the table's contentOffset.y reaches maximum value or 0. So I created my own "bouncing effect" but on a much smaller scale by implementing scrollViewWillEndDragging:withVelocity:targetContentOffset: in the table view's delegate as follows:
-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
targetContentOffset->y = MAX(targetContentOffset->y -1, 1);
}
With that in place, the problem disappeared. It's a workaround but works like a charm!
Swift 4 syntax of this comment:
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
targetContentOffset.pointee.y = max(targetContentOffset.pointee.y - 1, 1)
}
I have a UITableView in my Objective-C iOS app, of which I need to have a specific cell displaying information while all the others cells should move freely.
With my current code, I prevented said cell to be moved by other cells, but it is still possible to move underneath it.
This cell is not part of my Data model so I really need to keep this in the controller layer.
In other words, I'm looking for a way to prevent the default behaviour.
Below is my current code:
-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
if (destinationIndexPath.row == [tableView numberOfRowsInSection:0]-2) {
//prevent the move
}
else {
[[BNRItemStore sharedStore] moveItemAtIndex:sourceIndexPath.row toIndex:destinationIndexPath.row];
}
}
What should I change in order to obtain the desired functionality?
Did you try to implement this delegate method?
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath;
Use targetIndexPathForMoveFromRowAtIndexPath as below,
// Allows customization of the target row for a particular row as it is being moved/reordered
- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath {
if( proposedDestinationIndexPath is ok ) {
return proposedDestinationIndexPath;
} else {
return sourceIndexPath;
}
}
A question for those who have used a UITableView in the past
I am currently using a UITableView in edit mode and I have the following data source / delegate methods implemented
(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
//which tells me where the cell has been moved to
(NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
//which tells me where it may be moved
What I want to achieve:
when I pick a cell and reorder it, the cell should move up and down in the table view only while the finger is within the table view. Which means if I start from within the table view and move outside it, the cell should snap back in position and not let me reorder the cell.
The problem is that there is no way to find out (that I know of) where my finger is while the reorder is being performed. If I can find out where the finger is then it might be possible to achieve this.
Any ideas?
Take a look at the (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath function. HTH.