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

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.

Related

How reorder row in customize UITableViewCell

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.).

Assigning a separate user interface for every table view item

Can someone tell me how to assign a interface to a table view element, using storyboards? I'm making a medical calculator that has different calculators for every equation, and I need help making code that points a element to push to another interface. This is because for every equation, there are different fields to fill out (such as age, oxygen levels, whether someone has diabetes or not, height, etc.) Not every equation needs the same fields.
I have tried doing this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Deselect row
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// Declare the view controller
UIViewController *anotherVC = nil;
// Determine the row/section on the tapped cell
switch (indexPath.section) {
case 0:
switch (indexPath.row) {
case 0: {
// initialize and allocate a specific view controller for section 0 row 0
anotherVC = [[BmiViewController alloc] init];
break;
}
case 1: {
// initialize and allocate a specific view controller for section 0 row 1
/anotherVC = [[AaOxygenGradientViewController alloc] init];
break;
}
}
break;
}
}
But after doing this, it refers back to what was originally in the storyboard document (which is empty because I have created the interface programmicatally), instead of showing my test alert popup.
Also, is it possible to maybe make a bunch of table view cells, then have every one segue to every other view controller in the storyboard?
Thanks a lot in advance!
First, you are running deselectCellAtIndexPath? What is the reason for this? If you are just trying to remove the blue highlight then it's better to change the UITableViewCellSelectionStyle (or something like this) of the cell.
I'm not sure what you're asking for the first part but for the segue part then yes.
In Storyboard set up the segues from the tableViewController to each other VC that you want to segue to and give them all sensible identifiers.
i.e. medicalCalulatorSegue
graphSegue
userDetailsSegue
etc...
Then in your didSelect method you will have something like...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//some stuff...
switch(indexPath.row) {
case 0:
[self performSegueWithIdentifier:#"medicalCalulatorSegue"];
break;
case 1:
[self performSegueWithIdentifier:#"graphSegue"];
break;
case 2:
[self performSegueWithIdentifier:#"userDetailsSegue"];
break;
}
}
This will then segue to each of the different view controllers depending on which cell is selected.
The reason for not deselcting the cell is that in your method prepareForSegue you can then still access the indexPath of the selected cell...
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];

UITableView in editing mode cell still selectable even when using

I have a UITableView that has AllowsMultipleSelectionDuringEditing set to YES. Selecting multiple rows is a big part of my app, and it works great.
The problem is, that on one of my tableView's I don't want the user to be able to select the first 2 rows when in editing mode, so I have implemented the following:
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0 || indexPath.row ==1) {
return NO;
}//end
return YES;
}//end
This works how I thought it would, and doesn't show the red checkbox graphic or the option to re-sort the rows. BUT, I can still select those rows that are not editable, and call the indexPathsForSelectedRows method and return the indexPaths of those rows.
How can I prevent the user COMPLETELY from being able to select those rows while in editing mode, and prevent touches on those from being returned when calling indexPathsForSelectedRows? Why isn't canEditRowAtIndexPath: doing this for me?
One way to do this would be to implement – tableView:willSelectRowAtIndexPath: method and check if tableView.editing == YES then return nil for first two cells.
Something like,
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView.editing == YES && indexPath.row < 2 )
return nil;
return indexPath;
}
You can also set the selectionStyle of these two cells as UITableViewCellSelectionStyleNone in editing mode in cellForRowAtIndexPath method.
Did you try the following code?
tableView.allowsSelectionDuringEditing = NO;
or
tableView.allowsMultipleSelectionDuringEditing = NO;

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];

NSTableView not updating like I think it should

I have the following code:
- (void)updateServerList {
[crazyStuff reloadData];
NSLog(#"Hi");
}
- (int)numberOfRowsInTableView:(NSTableView *)tableView
{
NSLog(#"Numbers have Changed");
return [serverBrowser.servers count];
}
- (id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)tableColumn
row:(int)row
{
NSLog(#"Starting updates for table");
NSNetService* server = [serverBrowser.servers objectAtIndex:row];
return [server name];
}
- (IBAction)tableViewSelected:(id)sender
{
//row = [sender selectedRow];
NSLog(#"the user just clicked on row ");
}
This is part of a chat program that I'm trying to expand. It was designed for the iOS and I'm working to get it to work on my laptop and chat with my iPad. I know that updateServerList is called correctly from my log statement. I also now that numberOfRowsInTableView: is called on startup but not anytime else. I would like to have my Table (the outlet crazyStuff) updated everytime updateServerList is called. How would I trouble shoot to see if it is or is not? I am not seeing the data show up in the table that "should" be there
I take it then that at startup, when numberOfRowsInTableView is called, it returns 0? If not, is tableView:objectValueForTableColumn:row: ever called then?
My first guess would be the "crazyStuff" is, in fact, not connected to anything or connected to something other than the table view you intend. Might want to log the value of crazyStuff in updateServerList.
Within updateServerList you should [crazyStuff noteNumberOfRowsChanged]. I thought reloadData causes the table to re-ask its dataSource for the numberOfRowsInTableView:, though...