I have created grouped UITableView with custom cells.in custom cell i have added UITextfield and text field did begin editing moving tableview scroll to top to display textfield above keyboard.Its working fine in ios 6.0 but in ios 7.0 tableview moving down so keyboard hides textfield
My tried code is below
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
UITableViewCell *cell;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")) {
cell = (UITableViewCell*) [[[[textField superview] superview] superview]superview];
}
else
{
cell = (UITableViewCell*) [[[textField superview] superview]superview];
}
[self.TableView setContentOffset:CGPointMake(0, CGRectGetMinY(cell.frame)) animated:YES];
}
Please help me if anyone know.Thanks n advance.
Add this method in your ViewController
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
yourtableviewname.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
}
i hope this code useful for you.
Related
Inside each UITableViewCell of my UITableView, I have a UIScrollView. The scroll view is setup so that when the user swipes right a menu will appear. This is similar to the behavior of the cells in the iPhone Twitter app. When a user swipes upon another cell I iterate over all visible cells to tell the UIScrollView to scroll back to the cell content (i.e. its initial position). The iteration is done in the scrollViewWillBeginDragging method with the following code:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
if( [scrollView tag] == 90 ) {
NSLog(#"Dragging a scroll view inside a cell!");
for (UITableViewCell *cell in self.tableView.visibleCells) {
[(UICellContentScrollView *)[cell viewWithTag:90] scrollRectToVisible:CGRectMake(0.0f, 0.0f, 320.0f, [cell frame].size.height) animated:YES];
}
}
}
In the method viewDidDisappear I iterate again over all cells to reset various things like so:
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
for( NSUInteger section = 0; section < [[self tableView] numberOfSections]; section++ ) {
for( NSUInteger row = 0; row < [[self tableView] numberOfRowsInSection:section]; row++ ) {
UITableViewCell *cell = [[self tableView] cellForRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:section]];
// resetting cell here
}
}
}
My question is if I am (a) going about this the right way and (b) does anyone have any recommendations on a better solution considering the table view may be storing up 50 (no more than 100) items.
Check out the NSNotification documentation. You could register all of your UITableViewCell objects to receive a notification you could call something like "cellWasSwiped" or "needToResetCells" or whatever. Then whenever you want to reset the cells you just post the notification. All of your UITableViewCell objects that are registered to receive it will get the notification and can then call whatever method you need.
I have a UITableView with 10 rows. Each cell has a UITextField in it.
The problem is, when I click a UITextField in a cell at the bottom, the keyboard pops up and blocks the cell. How can I deal with this? Do I need to animate the tableview up or something?
Updated Code:
- (void)textFieldDidBeginEditing:(UITextField *)textField{
self.vitalsTableView.contentInset=UIEdgeInsetsMake(0,0,200,0);
NSIndexPath *path = [self.vitalsTableView indexPathForSelectedRow];
[self.vitalsTableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
use - (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated method of UITableView
example:
[tableView scrollToRowAtIndexPath:cellIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
will scroll your tableView to the lowermost possible offset that maintains the cell visible. If your cell does not have enough cells under, you can set a contentInset for your table to allow the scroll offset.
maybe your UITextField delegate is the table view controller. You can make the call on - (void)textFieldDidBeginEditing:(UITextField *)textField
In the case of the bottom cell:
- (void)textFieldDidBeginEditing:(UITextField *)textField{
tableView.contentInset=UIEdgeInsetsMake(0,0,200,0);
[tableView scrollToRowAtIndexPath:cellIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField{
tableView.contentInset=UIEdgeInsetsMake(0,0,0,0);
}
In case anyone runs into this same problem in iOS 7: I found that this problem was happening to me simply because I forgot to call the super class' implementation of viewWillAppear: in my own viewWillAppear: implementation. One line fixed the problem:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Do whatever you need to
}
I have a UITableView with several UITableViewCells and inside these cells are UITextFields. I implemented a UIToolbar for switching between the different textfields. I can go to the next textfield in the next cell or to the previous textfield. This works fine until a cell should become first responder which is currently not visible in the tableview.
I figured out that
[self.tableview cellForRowAtIndexPath:indexPath]
returns nil and that manually scrolling to the cell with the textfield, which should become first responder, removes the problem. Therefore I tried scrolling to the cell before changing it to become first responder.
I tried that with
[[self.tableView scrollToRowAtIndexPath:newIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
but unfortunately the tableview does not scroll to this cell but to all other cells. When I add a breakpoint to this line the problem doesn't occur. It also looks like the tableview is scrolling to the cell but then scrolls down again.
Here is the code of cellForRowAtIndexPath:
UITableViewCell *cell;
static NSString *AttributeCellIdentifier = #"AttributeCellIdentifier";
cell = [tableView dequeueReusableCellWithIdentifier:AttributeCellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"AttributeCell" owner:self options:nil];
cell = attributeCell;
self.attributeCell = nil;
UITextField * textField = (UITextField *)[cell viewWithTag:1];
[textField setKeyboardType:UIKeyboardTypeDecimalPad];
[textField setInputAccessoryView:[self inputAccessoryView]];
}
UITextField * textField;
textField = (UITextField *)[cell viewWithTag:1];
textField.placeholder = #"C(Probe)/mol/l";
return cell;
Here is the code for switching to the previous textfield:
UIView * currentResponder = [self.view findFirstResonder];
UITextField * newFocusTextField;
UITableViewCell * cell = (UITableViewCell*)[[currentResponder superview] superview];
NSIndexPath* indexPath = [self.tableView indexPathForCell:cell];
newFocusTextField = (UITextField*)[[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]] viewWithTag:1];
[currentResponder resignFirstResponder];
[newFocusTextField becomeFirstResponder];
UITableViewCell * newCell = (UITableViewCell*)[[newFocusTextField superview] superview];
NSIndexPath * newIndexPath = [self.tableView indexPathForCell:newCell];
[self.tableView scrollToRowAtIndexPath:newIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
Hope this helps.
The code on your post and the problem definition is not hundred percent clear, but there was a bug/glitch in scroll methods' working for iOS 3.0 (i guess specifically 3.0, as far as i remember). I couldnot figure out the reason for it to happen, but it was crashing all the time, although it was OK for iOS 4.x.
The fix was adding a
[table reloadData]
before the scroll line. I know it doesnt make sense, and I know it is not good practice especially if the cell rendering is expensive, but it did solve the problem for that case. I dont know if your problem stems from the same issue, but you may just give it a try...
I tried several thinks and everything works now fine. I removed:
[currentResponder resignFirstResponder];
And after removing that line the tableview scrolled fine and I can directly set the pointer to the new cell with the newFocusTextField and make it firstResponder with:
[newFocusTextField becomeFirstResponder];
I have a nextTextField method which works fine with
[currentResponder resignFirstResponder];
so I don't really understand why the problem occurs but I think scrolling to the cell and then just setting the new firstResponder is better then resigning the currentResponder first.
Thank you for helping anyway ;)
I have a table view where each cell has a button accessory view. The table is managed by a fetched results controller and is frequently reordered. I want to be able to press one of the buttons and obtain the index path of that button's table view cell. I've been trying to get this working for days by storing the row of the button in its tag, but when the table gets reordered, the row becomes incorrect and I keep failing at reordering the tags correctly. Any new ideas on how to keep track of the button's cell's index path?
If you feel uncomfortable relying on button.superview, this method should be a little more robust than some of the other answers here:
UIButton *button = (UIButton *)sender;
CGRect buttonFrame = [button convertRect:button.bounds toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonFrame.origin];
This stopped working with iOS 7; check out Mike Weller's answer instead
- (IBAction)clickedButton:(id)sender {
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *)button.superview;
UITableView *tableView = (UITableView *)cell.superview;
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
}
Or shorter:
- (IBAction)clickedButton:(id)sender {
NSIndexPath *indexPath = [(UITableView *)sender.superview.superview indexPathForCell:(UITableViewCell *)sender.superview];
}
Both are untested!
Crawling up view hierarchies with .superview (like all of the existing answers demonstrate) is a really bad way to do things. If UITableViewCell's structure changes (which has happened before) your app will break. Seeing .superview.superview in your code should set off alarm bells.
The button and its handler should be added to a custom UITableViewCell subclass and layed out there. That's where it belongs.
The cell subclass can then delegate out the button event through a standard delegate interface, or a block. You should aim for something like this:
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCustomCell *cell = ...;
// ...
cell.onButtonTapped = ^{
[self buttonSelectedAtIndexPath:indexPath];
}
// OR
cell.delegate = self;
// ...
}
(Note: if you go the block route, you will need to use a __weak self reference to prevent retain cycles, but I thought that would clutter up the example).
If you take the delegate route you would then have this delegate method to implement:
- (void)cellButtonPressed:(UITableViewCell *)cell
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
// ...
}
Your code now has full access to the appropriate context when it handles the event.
Implementing this interface on your cell class should be straightforward.
I don't know why I need to call the method superview twice to get the UITableViewCell.
Update:
Thank for Qiulang, now I got it.
"That's because SDK now has added a private class called UITableViewCellContentView for UITableViewCell, which is button's superview now." – Qiulang
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *)button.superview.superview;
UITableView *curTableView = (UITableView *)cell.superview;
NSIndexPath *indexPath = [curTableView indexPathForCell:cell];
I had this same issue also and built a simple recursive method that works no matter how many views deep you triggering control is.
-(NSIndexPath*)GetIndexPathFromSender:(id)sender{
if(!sender) { return nil; }
if([sender isKindOfClass:[UITableViewCell class]])
{
UITableViewCell *cell = sender;
return [self.tableView indexPathForCell:cell];
}
return [self GetIndexPathFromSender:((UIView*)[sender superview])];
}
-(void)ButtonClicked:(id)sender{
NSIndexPath *indexPath = [self GetIndexPathFromSender:sender];
}
I have created one Method for getting indexPath, Hope this will help you.
Create Button Action (aMethod:) in cellForRowAtIndexPath
-(void) aMethod:(UIButton *)sender
{
// Calling Magic Method which will return us indexPath.
NSIndexPath *indexPath = [self getButtonIndexPath:sender];
NSLog(#"IndexPath: %li", indexPath.row);
NSLog(#"IndexRow: %li", indexPath.section);
}
// Here is the Magic Method for getting button's indexPath
-(NSIndexPath *) getButtonIndexPath:(UIButton *) button
{
CGRect buttonFrame = [button convertRect:button.bounds toView:groupTable];
return [groupTable indexPathForRowAtPoint:buttonFrame.origin];
}
Use this Perfect working for me.
CGPoint center= [sender center];
CGPoint rootViewPoint = [[sender superview] convertPoint:center toView:_tableView1];
NSIndexPath *indexPath = [_tableView1 indexPathForRowAtPoint:rootViewPoint];
NSLog(#"%#",indexPath);
SWIFT 2 UPDATE
Here's how to find out which button was tapped
#IBAction func yourButton(sender: AnyObject) {
var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(position)
let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath!)! as
UITableViewCell
print(indexPath?.row)
print("Tap tap tap tap")
}
In my iPhone project I'm using a UITableview with UITableViewCells containing UITextfields. I have seen in many apps that it is possible to use a next button to jump to the next textfield in the next cell. What is the best way to accomplish this?
My idea is to get the indexPath of the cell with the textfield that is being editing and then get the next cell by cellForRowAtIndexPath. But how can I get the indexPath of the cell I'm currently editing?
Thanks!
Keep references to the UITextField instances in your table view.
Assign unique tag values to your UITextField instances.
In your last text field, you might set its Return key type, which changes the keyboard's Return key label from "Next" to "Done": [finalTextField setReturnKeyType:UIReturnKeyDone];
In the UITextField delegate method -textFieldShouldReturn:, walk through the responders:
- (BOOL) textFieldShouldReturn:(UITextField *)tf {
switch (tf.tag) {
case firstTextFieldTag:
[secondTextField becomeFirstResponder];
break;
case secondTextFieldTag:
[thirdTextField becomeFirstResponder];
break;
// etc.
default:
[tf resignFirstResponder];
break;
}
return YES;
}
Assuming the UITextField was added to the UITableViewCell like below
UITableViewCell *cell;
UITextField *textField;
...
textField.tag = kTAG_TEXTFIELD;
[cell.contentView addSubview:textField];
...
You can get the current index path via
-(BOOL)textFieldShouldReturn:(UITextField *)textField {
if([textField.superView.superView isKindOfClass:[UITableViewCell class]]) {
UITableViewCell *cell = (UITableViewCell *)textField.superView.superView;
NSIndexPath *indexPath = [self.myTableView indexPathForCell:cell];
}
...
The next row's UITextField would then be
NSIndexPath *indexPathNextRow = [NSIndexPath indexPathForRow:(indexPath.row+1) inSection:indexPath.section];
UITableViewCell *cellNextRow = (UITableViewCell *)[self.myTableView cellForRowAtIndexPath:indexPathNextRow];
UITextField *textFieldNextRow = (UITextField *)[cellNextRow.contentView viewWithTag:kTAG_TEXTFIELD];
Hope it helps!