I have a UITableView with a UITextField inside of each cell. A model object that stores the index of the cell that is currently being edited. If the cell scrolls off-screen, my app takes away first-responder status. (Failing to do so may cause problems). Now, suppose a cell (possibly the same one, or possibly a different one) corresponding to that index is about to scroll back onto the screen. I want to make that cell's textField the firstResponder. My delegate does receive a call
tableView: willDisplayCell: forRowAtIndexPath:
corresponding to the new cell. However, calling becomeFirstResponder: at that point does not help as the cell won't accept firstResponder status until it has been displayed.
Short of using a timer, any ideas for how to call becomeFirstResponder: at a point when the cell is in fact able to become the first responder?
EDIT: cellForRowAtIndexPath: is always called before willDisplayCell:. So no help there.
I haven't tried this, but the first thing I'd try is in cellForRowAtIndexPath...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// standard stuff to build cell and setup it's state
if ([indexPath isEqual:self.myModel.indexPathOfTextFieldBeingEdited]) {
// you probably have a handle to the text field from the setup above
UITextField *textField = (UITextField *)[cell viewWithTag:SOME_TAG];
[textField performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.0];
}
return cell;
}
You have to show cell on the screen to make it as first responder. Do at first:
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:NO];
and then call first responder on it's label/textField.
Here's what I did in MonoTouch - it's important that you do not animate the ScrollToRow() - i.e. "animated:NO" as shown in the answer by edzio27 (thanks edzio27 :) ).
var newCell = (UIOrderLineCell)tableView.CellAt(newIndexPath);
if (newCell == null)
{
tableView.ScrollToRow(newIndexPath, UITableViewScrollPosition.Middle, false);
newCell = (UIOrderLineCell)tableView.CellAt(newIndexPath);
}
Related
I'm trying to understand why awakeFromNib is being called twice in my code. I currently have a tableview that has a special compressible cell that appears once at the end of the table. The first awakeFromNib is being called when the tableview is scrolled to the special cell at the end (which is fine I believe,as the tableview is reusing cells). However, whenever I tap the cell to expand the cell, the awakeFromNib is being called again.
Could anyone explain to me why awakeFromNib is being called twice? And how I could only make it only be called once?
Thanks
EDIT** Code people have requested
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section >= (NSInteger)[self.trip.destinations count]) {
GuestCell *cell = (GuestCell*)[tableView dequeueReusableCellWithIdentifier:GuestCellIdentifier forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell setupCellForGuests:self.trip.guests];
cell.guestExpanded = NO;
NSLog(#"RETURNING CELL");
return cell;
}
// For all other sections
return [self prepareCardCellForIndexPath:indexPath forHeightCalc:NO];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section >= (NSInteger)[self.trip.destinations count]) {
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
You're animating the reload of the expanding row. The table view implements this by creating another cell for the same index path, and animating a transition from the old cell to the new cell. It creates a second instance of your cell prototype, so the second instance also receives the awakeFromNib message. If you log self, you'll see that the address is different the second time.
I don't think you can avoid the creation of a second cell instance (and thus a second awakeFromNib) unless you get rid of the animation. Even then I'm not sure it will reuse the old cell.
If the cell with that nib is only one in the table, then my guess is that it has something to do with animations. I didn't check how tableview handles cells during animation, but for tableview header it asks for another instance and then performs animation (for example fade) - so the old instance is faded out and the new is faded in. At least that's what I think has the highest probability, if you are handling cells correctly.
I have a simple project that contains a UITableView with a custom cell, in this I cell have a UITextField, So I have a button that calls a function 'addField', When This function is called I increment +1 value for the variable numberOfRows, then I call the command responsible for updating the table, as you can see bellow:
-(void)AddField{
numberOfRows++;
[tableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return numberOfRows;
}
Doing so creates a new table row with a new textField to enter another value.
Inside My cell I connect the UITextField with the Outlet,:
IBOutlet UITextField *myFields;
Inside awakeFromNib I have a code who checks whether the user is typing:
- (void)awakeFromNib
{
[campoTexto addTarget:self
action:#selector(change:)
forControlEvents:UIControlEventEditingChanged];
}
-(void)change:(UITextField *)textField{
NSLog(#"What The textField I'm Typing? -> %ld",(long)textField.tag);
}
All I would do is try to find some way to differentiate which text field I'm typing, as well UITableView can differentiate their rows (0 .. n), I think I can differentiate this UITextField (0 ... n).
Can anyone give me some help, or maybe know a tutorial on the web that explain this kind of thing?
Thanks.
The best way to Identify custom controls in a tableView cell, or rather what I would prefer is to set a tag for the custom control in your tableView datasource method cellForRowAtIndexPath:.
cell.yourTextField.tag = indexPath.row;
You can then get the indexPath from the corresponding tag in your method as follows:
-(void)change:(UITextField *)textField
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:textField.tag inSection:0];
}
Hope this helps :) !
I currently have a table with 8 rows that each have a label on the right side and a button on the left. I was hoping that I could have all the buttons hidden until the user presses an "edit" button in the top right corner and then they would appear allowing the user to interact with each table cell. I don't know if this is possible, because they are in UITableViewCells or if there is an easier method to summoning a button for each cell
UPDATE
okay so I have placed in all the hidden properties and there seem to be no errors, but the app doesn't recognize any of it. The buttons remains unhidden despite the fact that they are set to be initially hidden. Here is my code
Here is my Table Cell code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"BlockCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = #"Free Block";
UIButton*BlockButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
BlockButton.frame = CGRectMake(225.0f, 5.0f, 75.0f, 35.0f);
[BlockButton setTitle:#"Change" forState:UIControlStateNormal];
[BlockButton addTarget:self action:#selector(Switch:) forControlEvents:UIControlEventTouchUpInside];
Blockbutton.backgroundColor = [UIColor colorWithRed:102/255.f
green:0/255.f
blue:51/255.f
alpha:255/255.f];
Blockbutton.hidden = YES;
[cell addSubview:BlockButton];
return cell;
}
and here is my method code:
- (IBAction)Editmode:(UIButton *)sender
{
Blockbutton.hidden = !Blockbutton.hidden;
[self.tableView reloadData];
}
any thoughts or ideas as to what might be the issue?
You'll need to create a UITableViewCell subclass if you don't already have one. In that class, override setEditing:animated: and if the new value is YES, then enable/add/unhide the button.
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
if (editing) {
// add your button
someButton.hidden = NO;
} else {
// remove your button
someButton.hidden = YES;
}
}
It would be optional, but you are encouraged to animate the change if animated is YES.
Note: this assumes you have the edit button already hooked up the change the editing mode of the UITableView. If you don't, call setEditing:animated: on the UITableView in the button action. This will automatically call setEditing:animated: on each visible table cell.
The trick here is to keep in mind that a table's cells are determined by cellForRowAtIndexPath:. You can cause that method to be called all over again by sending the table reloadData:.
So, just keep a BOOL instance variable / property. Use the button to toggle that instance variable and to call reloadData:. If, at the time cellForRowAtIndexPath: is called, the instance variable is YES, set the button's hidden to YES; if NO, to NO.
take a BOOL variable which defines the whether to show delete button or not, use this BOOL var to for btnName.hidden = boolVar, initially make boolVar = NO, when user taps on edit toggle bool var and reload the tableview.
Another option is to test if you are in edit mode in the cellForRowAtIndexPath method.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = //(obtain your cell however you like)
UIButton *button = cell.button; //(get button from cell using a property, a tag, etc.)
BOOL isEditing = self.editing //(obtain the state however you like)
button.hidden = !isEditing;
return cell;
}
And whenever you enter editing mode, reload tableView data. This will make the table view ask for the cells again, but in this case the buttons will be set not to hide.
- (void)enterEditingMode {
self.editing = YES;
[self.tableView reloadData];
}
I have a basic UITableView which contains basic UITableViewCells. When I add a new cell to the table I'd like to:
1) scroll to the new row
2) select all of the text in the new UITableViewCell so that the keyboard becomes visible and the user can immediately edit the cell's text.
The next time through the event loop (after calling -reloadData on the table view) I do:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:wordIndex inSection:0];
[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:(UITableViewScrollPositionNone)];
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
[cell.textLabel becomeFirstResponder];
cell.highlighted = YES;
The scrolling is correct but the text in the cell is not selected.
A UILabel can't become a first responder because it returns NO from canBecomeFirstResponder. To give the illusion of a label that is edited, you could try using a UITextField with a borderStyle of UITextBorderStyleNone.
Be careful with the timing of the becomeFirstResponder call, since a control can't be first responder if it is not a subview of a window. This can happen if you are scrolling to a row that is very far off screen and immediately trying to call becomeFirstResponder before it is added as a visible row.
As in previous answer you can create custom child of UITableViewCell, add UITextField on that.
#interface MyTableViewCell : UITableViewCell
#property(nonatomic, retain) IBOutlet UITextField *editableTextField;
#end
Than you can catch didSelectRowAtIndexPath in your controller and set it's textfield to become first responder
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
MyTableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
[cell.editableTextField becomeFirstResponder];
}
UPD: as a trouble resolve for Not-in-window-cell trouble, described by upper answer, you must call selectCellAtIndexPath in method
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
I have a UITextField with a tag inside a prototype cell. The UITextField is set to become first responder when the UITableView is being built and a UISwitch in the cell above is turned on.
This works if the app starts from scratch or if it was closed and restarted. Once it's loaded the [tableView reloadData] doesn't trigger the becomeFirstResponder anymore.
If the UITextField becomes first responder by touching the textfield later on, I can trigger the becomeFirstResponder event with buttons, with pop ups,...
But not with my switch any more.
Any pointers as to what I can try?
Currently, I use the didSelectRowAtIndexPath method to trigger a pop up. A nice side effect is, that when I pull up the number keypad, I can provide an ok and cancel button within the pop up instead of having to fiddle with separate buttons on the keypad. But it just seems so obviously to be a workaround.
This is how I call firstresponder when building the UITableView (which works every time the app starts from scratch):
if ([settings doubleForKey:#"limitDouble"]==0 && [settings boolForKey:#"limitBool"]==YES) {
[dailyLimitEntry becomeFirstResponder];
}
dailyLimitEntry is a UITextField which is strong so it stays around.
Just for fun I added a button and connected it to my code like this:
UITextField *tmp = (UITextField *)[self.view viewWithTag:35];
[tmp becomeFirstResponder];
This works, too. When I use it with the switch, it's only called once the app is freshly loaded in the memory. Otherwise, my UITextField doesn't respond to the switch.
After the first comments, I found a method to check whether or not the UITableView has finished reloading
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if([indexPath row] == ((NSIndexPath*)[[settingsTableView indexPathsForVisibleRows] lastObject]).row){
//end of loading
//for example [activityIndicator stopAnimating];
NSLog(#"finished reload");
NSLog(#"%#",dailyLimitEntry);
if ([settings boolForKey:#"limitBool"]==YES) {
UITextField *tmp = (UITextField *)[self.view viewWithTag:35];
[tmp becomeFirstResponder];
}
}
}
Fun thing though is, become first responder is only triggered the first time the switch is used after the app loaded. Initially, the switch is off. So the cell containing the UITextField is not drawn yet. Once I switch to ON, the UITextField gets drawn in the second cell and becomes first responder. If I switch OFF and ON again, I still get my log "finished reload", but the UITextField doesn't become first responder. It's driving me nuts....
Must be something about the life cycle of the app.
Rather than checking the indexPath, check the cell's class.
Here's what I do to bring up the keyboard for a title cell that's blank (when I create a new item):
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([cell isKindOfClass:[FDSSampleTitleCell class]]) {
FDSSampleTitleCell *titleCell = (FDSSampleTitleCell *)cell;
if (titleCell.titleField.text.length == 0) {
[titleCell.titleField becomeFirstResponder];
}
}
}