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];
}
}
}
Related
In an iOS apps I have a table view that has 2 rows with textfield embedded inside.
Textfield2 is a textfield with pickerview input that I set with something like this:
textField2.inputView = pickerView;
So the thing is , the textField 2 icon on the right is only an image embedded in the textfield2.rightview that caused the image to not trigger the pickerView input when tapped.
Did select table view method also wasn't triggered because I set the textfield to fully occupy the cell. Thus, after some searching I find that disabling the textField2 user interaction enabling the didSelect to be triggered.
textField2.userInteractionEnabled = false;
However, now I'm at lost on to how to trigger the input to textField2 pickerView via the didselect tableview method. I tried this line of code but that doesn't work.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Did select row %d",indexPath.row);
if(indexPath.row == 1){
[textField2 becomeFirstResponder];
}
}
I tried to search how to trigger input manually to textField and didn't find any clue.
Thanks before ! :)
After some debugging with a clear mind, I find that
textField2.userInteractionEnabled = false;
caused the textfield not able be the responder.
So a little workaround with this flag is enable it first in order to edit the text field and disable it again after we finished editing. Something like this :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Did select row %d",indexPath.row);
if(indexPath.row == 1){
[textField2.userInteractionEnabled = true;
[textField2 becomeFirstResponder];
}
}
- (void)donePicker {
// to enable the did select row again
[textField2.userInteractionEnabled = true;
}
In my application I have a TableView full of cells. Everything works just fine -- When I tap on a cell, it calls tableView:DidSelectRowAtIndexPath: right away and executes a segue, bringing me to the next screen. I also have a search bar, using UISearchDisplayController, allowing users to search through the items in the tableView. When I type some text into the search bar, the cells that match the search display in the table.
Now, my problem is when I tap on one of the cells displayed in this search results table... On one initial tap, the table view does not respond in any way. If the tap is held just briefly, the cell turns gray, as if it were selected, however tableView:DidSelectRowAtIndexPath: is still not called, and the cell turns back to normal after releasing the tap. But if I do a long press for a few seconds, then tableView:DidSelectRowAtIndexPath: is finally called, and I am brought to the correct screen.
Has anyone encountered this problem before? As far as I know I have implemented my UISearchDisplayController the same exact way as I always have, and have never had this problem.
Thank You, and let me know if I can give any additional information that may be helpful
EDIT
I am not certain wherein the problem lies exactly, so I'm not sure which methods to show, but here is some code...
I am bringing up the search bar upon clicking an icon in the UINavigationBar, then removing it from the superview once the editing has finished.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSArray *contentArray;
if (tableView == self.tableView) {
contentArray = self.postArray;
} else if (tableView == self.searchDisplayController.searchResultsTableView) {
contentArray = self.searchResults;
}
// This is pretty hackish, but it wasn't working before for some reason. So I send the PFObject I want as the sender
[self performSegueWithIdentifier:#"ShowPostDetails" sender:contentArray[indexPath.row]];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"PostCell";
PostTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[PostTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
[self updateSubviewsForCell:cell inTableView:tableView atIndexPath:indexPath];
/*
if (tableView == self.searchDisplayController.searchResultsTableView) {
[cell addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(cellTapped:)]];
} */
return cell;
}
#pragma mark - Search Bar Delegate
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"text contains[c] %#", searchText];
self.searchResults = [self.postArray filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
[self.view addSubview:self.searchDisplayController.searchBar];
self.searchDisplayController.searchBar.center = CGPointMake(self.view.window.center.x, 42);
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
[self.searchDisplayController.searchBar removeFromSuperview];
}
I found my problem...
The specific view controller that was experiencing this problem is subclassed from the view controller containing these delegate methods, and contains a UITextField for entering information. I watch for a keyboardDidAppear notification, and when it appears I add a UITapGestureRecognizer to the view to close the keyboard by resigning first responder of the UITextField when the view is tapped. When I added this I had not yet implemented the search feature, so I knew the only reason the keyboard would pop up is for the UITextField. The problem is that this extra TapGestureRecognizer added when the keyboard popped up for the search bar prevented the TapGestureRecognizer built into the UITableView cell from firing. I was looking in the wrong spot for the problem.
To fix the problem I simply made a check that the UITextField is indeed the first responder before adding the gesture recognizer. Now all works as it is supposed to.
So for anyone else experiencing a similar problem, I'd say go back and make sure you don't have any other UIGestureRecognizers that might be conflicting with the gesture recognizers of your tableView.
Thanks to everyone who commented. Helped lead me to where the problem was.
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 am working with the kind of notes app and I wanted to add the option to swipe-to-delete (like the iPhone's default notes app). I implemented the following two table view methods..
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
//Action to delete value of the cell
}
}
Everything works well except an animation. Showing the delete button on swipe action happens with an animation while hiding it (by tapping or scrolling table view) does not happens with the animation. The delete button was just disappeared immediately.
Can I show the hiding process of the delete button with animation?
For fully control on editing in your custom cell, you should override willTransitionToState method in your UITableViewCell subclass and check state mask
- (void)willTransitionToState:(UITableViewCellStateMask)state
{
NSString *logStr = #"Invoked";
if ((state & UITableViewCellStateShowingEditControlMask)
!= 0) {
// you need to move the controls in left
logStr = [NSString stringWithFormat:#"%#
%#",logStr,#"UITableViewCellStateShowingEditControlMask"];
}
if ((state & UITableViewCellStateShowingDeleteConfirmationMask)
!= 0) {
// you need to hide the controls for the delete button
logStr = [NSString stringWithFormat:#"%#
%#",logStr,#"UITableViewCellStateShowingDeleteConfirmationMask"];
}
NSLog(#"%#",logStr);
[super willTransitionToState:state];
}
EDIT:
Did you try:
setEditing:animated:
Toggles the receiver into and out of editing mode.
(void)setEditing:(BOOL)editing animated:(BOOL)animated
Parameters
editing
YES to enter editing mode, NO to leave it. The default value is NO .
animated
YES to animate the appearance or disappearance of the insertion/deletion control and the reordering control, NO to make the transition immediate.
Discussion
When you call this method with the value of editing set to YES, and the UITableViewCell object is configured to have controls, the cell shows an insertion (green plus) or deletion control (red minus) on the left side of each cell and a reordering control on the right side. This method is called on each visible cell when the setEditing:animated: method of UITableView is invoked. Calling this method with editing set to NO removes the controls from the cell.
Availability
Available in iOS 2.0 and later.
See Also
#property editing
Declared In
UITableViewCell.h
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);
}