I am writing a "tweetie 2" like swipe functionality, but have run into what I hope is the last stumbling block.
When a user swipes across a table row the "controls" view animates as expected, but when the row is selectd, didSelectRowAtIndexPath is fired off. The desired result is for when the "controls" view is visible to disable the didSelectRowAtIndexPath method or for a lack of a better phrase...stop the responder chain for continuing past the "controls" view.
The uitouch delegate methods are used/being called in the custom uitablviewcell.
What about setting/unsetting the value of allowsSelection in UITableView as needed?
A bit of logic should do the job here. Let's say you add this property to your UITableViewController subclass:
NSIndexPath *indexPathForCellInUtilityMode;
When the user triggers the cell's utility view, your cell does this:
NSIndexPath *cellIndexPath = [parentViewController.tableView indexPathForCell:self];
parentViewController.indexPathForCellInUtilityMode = cellIndexPath;
Then:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([indexPath compare:indexPathForCellInUtilityMode] != NSOrderedSame) {
//Do whatever you're normally doing in this method.
}
So you'll disable selection for the affected cell while still allowing the user to interact with other visible cells.
Related
I'm creating a Form with UITableView, I want that when i click over any part of my row, get the focus on the textfield so I can write on it. Right now to focus on the textfield (yellow space) i have to click only over the textfield (see image). Name is the title (not editable)
Thanks a lot
Say you have a textField named myTextField that you want to have focused when selecting the first row in the table.
Do this in your didSelectRowAtIndexPath: method
if (indexPath.row == 0) {
[myTextField becomeFirstResponder];
}
In -didSelectRowAtIndexPath of the row, call [correspondingTextField becomeFirstResponder];
You can make the textbox as "becomefirstreponder" in didselectrow in case you have it in a table view.
If you set your delegate properly, this is the delegate method which is fired when user click over any rows.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
So, in this method you are going to use becomeFirstResponder method to get the focus.
[yourTextField becomeFirstResponder ];
I have a table view in which the cells are built differently depending on whether the table is editing or not. Specifically, the selection style is none when in edit mode and blue when not in edit mode.
When I transition from one to the other, I noticed that some of the cells are not updated. A quick bit of logging tells me that even though the cells' appearance changes quite drastically (accessory views are added/removed correctly for example) the table view does not refresh the selection style (nor for that matter the text).
What is going on here? Are only some attributes of the cell updated when setEditing is called? Presumably only those with a specific method allowing allocation of a separate view style (for example the EditingAccessoryType)? I guess I would benefit from a EditingSelectionStyle.
How should I resolve it? By customizing setEditing to change the selectionStyle for each cell? I'm not even sure how I would iterate through the table view to do this. reloadData isn't an option because of some animation that I am using.
I found that customizing setEditing: to iterate through the visible cells and setting the selectionStyle for each to work ok.
- (void)setEditing:(BOOL)editing animated:(BOOL)animated{
[super setEditing:editing animated:animated];
for (UITableViewCell *cell in [self.tableView visibleCells]) {
NSIndexPath *path = [self.tableView indexPathForCell:cell];
cell.selectionStyle = (self.editing && (path.row > 1 || path.section == 0)) ? UITableViewCellSelectionStyleNone : UITableViewCellSelectionStyleBlue;
}
}
If you look at the UITableViewDelegate documentation you will see a that there are five methods to customize the editing behavior. There is also the method
- (BOOL)tableView:(UITableView *)tableView
canEditRowAtIndexPath:(NSIndexPath *)indexPath
in the UITableViewDataSource documentation that will be called on each cell before you go into editing mode. The same is true for
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView
editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
that will get called for all cells that are editable. If you want to change the way the cells look you could do it in either of these. (Not implementing canEditRow.. assumes all rows are editable.)
Also note that there may be other ways to enter editing mode such as swiping on a cell, in which case
- (void)tableView:(UITableView *)tableView
willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
will be called for the cell that you swiped on:
When entering this "swipe to delete" editing mode, the table view sends a tableView:willBeginEditingRowAtIndexPath: message to the delegate to allow it to adjust its user interface.
This works on Swift 2.3, just overwriting the setEditing method in your custom cell subclass:
class MyCell: UITableViewCell {
override func setEditing(editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
//Place your code here...
}
}
In my app if user press on any cell in UITableView then accessoryType of cell will be set to check mark like following
-(void)Check:(UITableView *)tableView Mark:(NSIndexPath *)indexPath
{
[self.tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
buttonCount++;
[selectedCellArray addObject:indexPath];
}
and if user press the same cell then uncheck will happen as follows
-(void)UnCheck:(UITableView *)tableView Mark:(NSIndexPath *)indexPath
{
[self.tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
buttonCount--;
if (buttonCount == 0) {
[selectedCellArray removeAllObjects];
}
}
and i am calling this
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if([tableView cellForRowAtIndexPath:indexPath].accessoryType == UITableViewCellAccessoryCheckmark)
{
[self UnCheck:tableView Mark:indexPath];
}
else
{
[self Check:tableView Mark:indexPath];
}
Problem is when i am pressing on 1st cell it call Check method and mark the cell to but when i am scroll down i find 2-3 more cheked cell ...even i did not select those cell...i dont know why and how it checked automatically ...
i hope some one know where is the problem
thank you very much
Because cells will be reused by the tableview. Also set the checkmark/accessory type in your tableView:cellForRowAtIndexPath: method.
ya.. I see this happen a lot.
the correct pattern to manage the UITableViewCell state is NOT to directly manipulate the TableViewCell to update the UI, and always set, draw and create the correct state from cellForRowAtIndexPath (or tableView:willDisplayCell:forRowAtIndexPath:)
Meaning, if the user clicks on the cell and you need to update the UI of that cell, store the new state of that cell in an array or dictionary (I find that an NSMutableDictionary with the NSIndexPath as the key works very well).
then call the reloadRowsAtIndexPaths:withRowAnimation: or just [tableView reloadData] so that the cellForRowAtIndexPath reads that array or Dictionary and correctly draws the cell.
otherwise, the cellForRowAtIndexPath will constantly overwrite your changes and use recycled cells that hold incorrect state.
one exception to this rule would be if you would like a nice animation between the two states... if that is the case, save off your new state, perform your animation right there on the cell, then when the animation completes, call the same reloadRowsAtIndexPaths:withRowAnimation or reloadData so that the cell is redrawn in its new state.
Here’s my scenario:
I’m showing a UITableViewController in a UINavigationController, and I’m drawing the cells myself in a subclass. In order to keep the cells looking as close to possible like native cells, I have a flag that indicates whether it is in a transitional state or not, in order to prevent the text color from visibly flashing when the user moves back up the stack from a detail view to the table view.
Currently, I set my transitioning flag in -tableView:didSelectRowAtIndexPath:, like so:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// (stuff for pushing the detail view on to the navigation stack)
((MyCustomTableViewCell *) [self.tableView cellForRowAtIndexPath: indexPath]).transitioning = YES;
}
This works rather well, with one caveat: Immediately before the list animates off-screen, the transition is clearly visible to anyone looking for it, as the cell text changes to black (on blue) from white (on blue.)
My question: Is there any way to get the currently selected cell from the table view, after it has transitioned off-screen, and send it a message? (assuming it isn’t being deallocated, simply unloaded)
Or am I simply going about this whole thing the wrong way?
(For anyone considering saying that nobody will notice it, keep in mind that it’s acceptable to me the way that it is, I’m simply wondering if there’s a way for me to make it better. Good iOS applications are all about the little things.)
What do you mean by "prevent the text color from visibly flashing"? By default iOS table cells don't appear to do that, at least in an unpleasant way. Perhaps you can revisit your UITableViewCell implementation and determine if you are incorrectly handling -setSelected:animated: and -setHighlighted:animated
UITableView does not keep a publicly-accessible list of all the cells in the table.
In order to access all the cells (including ones out of the screen) you need to maintain a separate array of the cells you generate.
#interface MyViewController : UIViewController
{
NSMutableArray* tableCells;
}
#implementation MyViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableCells == nil)
tableCells = [[NSMutableArray alloc] init];
UITableViewCell* cell;
if (indexPath.row < [tableCells count])
{
// Return a cell from the cached list
cell = (UITableViewCell*)[tableCells objectAtIndex:indexPath.row];
}
else
{
// Create a new cell
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"identifier"];
// Customize and fill the cell with content anyway you wish
// ...
}
return cell;
}
Now that you have a list of all the cells in the table, you can send them any message, anytime you want.
In my application I have this requirement that first tap on custom cell of uitableview with a label in it should expand it and second should contract it. I'm able to expand and contract cell and expand label inside cell, but not able to contract the label on second tap.
I'm using this function
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
if( selected == YES ) {
[self expandRow];
}
else {
[self contractRow];
}
height = [lblFeed frame].size.height + 75;
}
expandRow expands the label and contractRow contracts it. I'm perplexed as for how many rows this function gets called. It doesn't get called only for the cell tapped, it gets called more number of times for single tap on single cell may be for other cells but I'm not getting which rows.
This' really urgent.
Can anybody please help?
Tapping a selected row doesn't cause it to be deselected. When a cell gets selected, it stays selected until deselectRowAtIndexPath:animated: gets called on its table. That's why your method isn't getting called for the second tap.
In an MVC architecture like UIKit, it's recommended that you handle user interactions in your controller classes. It would be appropriate to override -[UITableViewCell setSelected:animated:] if all you were doing was customizing the way the view represents a selected cell, but in this case your expand/contract toggle behavior would require a change in the way UITableView selects and deselects its cells.
You could subclass UITableView and implement this toggle behavior yourself, or you can leave UITableView alone and handle it all at the UIViewController level by doing something like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([self.expandedIndexPath isEqual:indexPath]) {
[(YourCustomCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath] contractRow];
self.expandedIndexPath = nil;
}
else {
if (self.expandedIndexPath) {
[(YourCustomCell *)[self tableView:tableView cellForRowAtIndexPath:self.expandedIndexPath] contractRow];
}
[(YourCustomCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath] expandRow];
self.expandedIndexPath = indexPath;
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
I would suggest that you don't add your functionality on top of the selected property of the cell, which has slightly different behaviour than you expect.
Just add your own BOOL expanded property, and see how that works. You should probably call it from the UITableView delegate methods, too.