I have a ViewController with a grouped tableview added. Some cells contain textfields. I would like to switch the first responder to the next textfield in the list of cells when a user presses the return key. However, I cannot get it to work and I can't tell if I have the incorrect cell selected or an incorrect textfield selected.
I am setting my tag in the cellForRowAtIndexPath with the following..
cell.tag = ((indexPath.section + 1) * 10) + indexPath.row;
this will create a tag with the tens place being a section value and the ones place being the row value. ie. tag 11 is section 0 row 1.
here is the code for my textFieldShould Return
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
UITableViewCell *cell = (UITableViewCell *)textField.superview;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
cell = [self tableView:_tableView cellForRowAtIndexPath:indexPath];
if (indexPath.section == 0)
{
if (indexPath.row == 0)
{
[[cell viewWithTag:((indexPath.section + 1) * 10) + (indexPath.row + 1)] becomeFirstResponder];
}
if (indexPath.row == 1)
{
[[cell viewWithTag:((indexPath.section + 2) * 10)] becomeFirstResponder];
}
}
return YES;
}
One final note, currently the increments for the new tag are hardcoded, but I would like to be able to go to the new tag without hardcoding the actual value every time. Is this possible?
If all of your cells contained 1 UITextField, I'd say you could subclass UITableViewCell and add a property that references the cell's text field, like I did here.
But you said that only some of your cells contain a text field, so another option would be to create an array of pointers to the UITextFields (I got the idea here). Then the user pressing Return would cycle through them like this:
- (BOOL) textFieldShouldReturn:(UITextField*)textField {
NSUInteger currentIndex = [arrayOfTextFields indexOfObject:textField] ;
if ( currentIndex < arrayOfTextFields.count ) {
UITextField* nextTextField = (UITextField*)arrayOfTextFields[currentIndex+1] ;
[nextTextField becomeFirstResponder] ;
}
else {
[textField resignFirstResponder] ;
}
}
Set tag to your textField instead of cell.
yourTextField.tag = ((indexPath.section + 1) * 10) + indexPath.row;
Related
In a plain UITableView with custom UIView's as section headers, is there a way to calculate:
When one of the Section is on the top, the distance between that section and the next one that would come?
I am expecting to calculate this here:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
You can find the number of rows in that section by calling the tableView:numberOfRowsInSection: method from the UITableViewDataSourceDelegate protocol. You can get the height for each row in the section with the tableView:heightForRowAtIndexPath: method from the UITableViewDelegate protocol. Add up the height for all the rows and you have the distance you want.
Your code would look something like this, assuming you have a reference to the tableview and the section.
float totalHeight = 0;
for (int i = 0; i < [tableViewDataSourceDelegate
tableView:tableView
numberOfRowsInSection:section]; i ++) {
totalHeight += [tableViewDelegate
tableView:tableView
heightForRowAtIndexPath:[NSIndexPath
indexPathForRow:i
inSection:section]];
}
Haven't had a chance to test this code, but it Should Work[tm].
Edit
This will only work if the header is at the top.
(assuming all rows are same height)
NSIndexPath *topCellIndexPath = [tableView indexPathsForVisibleRows][0];
UITableViewCell *topCell = [tableView cellForRowAtIndexPath: topCellIndexPath];
CGFloat distanceToNextSection = [tableView convertRect: [topCell frame] fromView: topCell.superview].origin.y - tableView.contentOffset.y + ([self tableView: tableView numberOfRowsInSection: topCellIndexPath.section] - topCellIndexPath.row)*tableView.rowHeight
I was able to solve this by doing the following:
Before creating an section header, check if you have the section header for a given section. If you do return it from the NSMutableArray. If not keep going.
When you create the section header, keep a reference to it in a NSMutableArray.
When you scroll in:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
Do the following:
// Get the toppest section
NSUInteger sectionNumber = [[self.tableView indexPathForCell:[[self.tableView visibleCells] objectAtIndex: 0]] section];
// Get a reference to it
SFBasicSectionHeader *topHeader = [arrayOfWeakHeaders objectAtIndex:sectionNumber];
SFBasicSectionHeader *bellowHeader;
// Check if it's Ok to get the bellow header
if (sectionNumber+1<[arrayOfWeakHeaders count] && [arrayOfWeakHeaders objectAtIndex:sectionNumber +1])
{
bellowHeader = [arrayOfWeakHeaders objectAtIndex:sectionNumber+1];
}
The difference between both will be:
CGFloat differenceBetweenTopAndBellowSection = bellowHeader.frame.origin.y - topHeader.frame.size.height - self.tableView.contentOffset.y;
Done.
I have a method selectAll to select all my cells in my UITableView. This method check a checkbox (UIButton). It's work very well just for the "visible" cells but not for the "invisible" cells!
Here my method:
- (IBAction)selectAll:(id)sender {
for (NSInteger s = 0; s < self.tableView.numberOfSections; s++) {
for (NSInteger r = 0; r < [self.tableView numberOfRowsInSection:s]; r++) {
CustomCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:r inSection:s]];
if(!cell.checkbox.selected){
cell.checkbox.selected = !cell.checkbox.selected;
cell.account.checked = cell.checkbox.selected;
}
}
}
}
From the documentation:
cellForRowAtIndexPath:
Return Value:
An object representing a cell of the table or nil if the cell is not visible or indexPath is out of range.
You can create an array that contains a list of booleans for checked or unchecked, and interrogate it when the cell is visible.
You need to check or uncheck your "selected" or "checked" state of your cells in the "cellForRowAtIndexPath" method. The underlying data source is another place where you can keep track of what should be the state of the data you're trying to represent in the cells.
Simply modifying the UITableViewCells via this function is only going to update the cells that are currently visible within the table view.
Check it in cellForRowAtIndexPath. Just check the if condition with the suitable indexPath.section
if (indexPath.section== add the section){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else{
cell.accessoryType = UITableViewCellAccessoryNone;
}
I'm getting a weird issue. I have a custom UITableViewCell and each cell has a UIButton and UITextField. When the button is clicked, it changes the textfield value to some constant.
Now in the cellForRowAtIndexPath method I have this:
folderTitleTextView.tag=indexPath.row;
[arrayOfTextFields insertObject:folderTitleTextView atIndex:indexPath.row];
NSLog(#"indexpath.row:%i", indexPath.row);
NSLog(#"text fields count %i", [arrayOfTextFields count]);
So if I have two cells, then every time I reload the table, it adds two more objects to the arrayofTextFields, even though it should replace the existing ones. So if I have two cells and I reload the table 3 times, then for some reason arrayOfTextFields count is 8.
This folderTitleTextView.tag=indexPath.row; is not a good idea because everything starts with a tag of 0, so when accessing views with viewWithTag:0 or when setting up the row 0, you will get weird results.
I would suggest also checking the number of items in arrayOfTextFields and use [arrayOfTextFields replaceObjectAtIndex:indexPath.row withObject:folderTitleTextView]; or [arrayOfTextFields insertObject:folderTitleTextView atIndex:indexPath.row]; depending on the current count for arrayOfTextFields
Try this:
folderTitleTextView.tag = (indexPath.row + 100);
if ([arrayOfTextFields count] <= indexPath.row) {
[arrayOfTextFields insertObject:folderTitleTextView atIndex:indexPath.row];
} else {
[arrayOfTextFields replaceObjectAtIndex:indexPath.row withObject:folderTitleTextView];
}
NSLog(#"indexpath.row:%i", indexPath.row);
NSLog(#"text fields count %i", [arrayOfTextFields count]);
The question is what are you trying to do?
Right now you add the textView to an array each time a cell is displayed.
If you have 1 cell you have 1 textView in the array because cellForRowAtIndexPath: was called 1 time.
If you add another cell so you have 2 total cell cellForRowAtIndexPath will be called another 2 times and it will add 2 textViews to the array that already has one -> 3
If you add another cell cellForRowAtIndexPath adds 3 more textViews to the 3 that are already there -> 6
So much for the explanation of your results.
My suggestion is to get rid of that array and get rid of the tag, most likely those are not needed at all.
you can access the cell with something like this:
- (IBAction)buttonPressed:(UIButton *)sender {
UIView *contentView = [sender superview];
UITableViewCell *cell = (UITableViewCell *)[contentView superview];
// you should assign a tag to the textField of your cell. Use the same tag for each textView in all cells.
UITextField *textField = (UITextField *)[cell viewWithTag:42];
textField.text = #"Foo";
}
I'm making an iPhone app with a Table View, and I'm trying to place a different icon / image next to each cell on a table.
I know that you set the image in (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath with code that looks like this:
UIImage *image = [UIImage imageNamed:#"image.png"];
cell.imageView.image = image;
What I'm trying to figure out is, how do I target a specific cell, so that it has a unique image? Like:
if (cell.number == 0) {
//Use a specific image
}
else if (cell.number == 1) {
//Use a different image
Thanks!
The indexPath variable contains information about the cell's position. Modifying your example:
if (indexPath.row == 0) {
// Use a specific image.
}
See the NSIndexPath Class Reference and NSIndexPath UIKit Additions Reference for more information. It's also important to note that cell numbers reset in each section.
Use the row (and possibly also section) properties in the NSIndexPath passed to your tableView:cellForRowAtIndexPath: method to identify which cell is being queried.
this function is passed an index path, which has a section and a row. indexPath.row will pass back an integer you can check.
When cellForRowAtIndexPath is executed you have access to the indexPath variable, so if you want to customize the cell style depending on the cell index you can do something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
// code for cell 0
}
else {
if (indexPath.row == 1) {
// code for cell 1
}
}
}
This is just an example, I don't think that customizing your cells by using if conditions is the best idea, but it shows you how to do what you need.
Remember that indexPath contains the section of the table too. If you are using a Grouped table view, you need to manager the section too. For example:
if (indexPath.section == 0) {
// section 0
if (indexPath.row == 0) {
// code for section 0 - cell 0
}
else {
if (indexPath.row == 1) {
// code for section 0 - cell 1
}
}
}
else {
if (indexPath.section == 1) {
// section 1
if (indexPath.row == 0) {
// code for section 1 - cell 0
}
else {
if (indexPath.row == 1) {
// code for section 1 - cell 1
}
}
}
}
For a slightly nicer looking approach I would put all the images you want to use into an array:
_iconArray = #[#"picture1.png", #"picture2.png", #"picture3.png"];
This means that when you come to the cellForRowAtIndex function you can say only:
cell.imageView.image = [UIImage imageNamed:_iconArray[indexPath.row]];
This is also easier if you have more than one section, this time you can make an array of arrays, each containing the required pictures for the different sections.
_sectionsArray = #[_iconArray1, _iconArray2, _iconArray3];
cell.imageView.image = [UIImage imageNamed:_sectionsArray[indexPath.section][indexPath.row];
This immediately makes it very easy to modify the pictures (as you are only dealing with the arrays. And much easier if you have more rows and sections (imagine doing it manually for 100 rows)
For some reason, I'm having trouble with making a textfield the first responder.
I have a UITableView with two rows. Each row has a label and a UITextField. The textfields are tagged kLoginRowIndex = 0 and kPasswordRowIndex = 1. As you might have guessed, I use this for setting login and password.
If the user taps on the return button when editing the login textfield, I want the password textfield to get the focus. Unfortunately, the password textfield doesn't accept the focus. Here is my code:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSLog(#"%s:(textField.tag:%d)", __FUNCTION__, textField.tag);
[textField resignFirstResponder];
if(textField.tag == kLoginRowIndex) {
UITableViewCell *cell = [self tableView:self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:kPasswordRowIndex inSection:0]];
UITextField *nextTextField = (UITextField *)[cell viewWithTag:kPasswordRowIndex];
NSLog(#"(nextTextField.tag:%d)", nextTextField.tag);
NSLog(#"canBecomeFirstResponder returned %d", [nextTextField canBecomeFirstResponder]);
NSLog(#"becomeFirstResponder returned %d", [nextTextField becomeFirstResponder]);
} else {
[self validate:textField];
}
return NO;
}
This is the log output:
-[SettingsViewController textFieldShouldReturn:]:(textField.tag:0)
(nextTextField.tag:1)
canBecomeFirstResponder returned 1
becomeFirstResponder returned 0
What I tried:
returning YES instead of NO
removing the call to canBecomeFirstResponder (which is just for debugging purposes)
Any hints are appreciated!
After playing with the suggestion of tmadsen, I found the error. The mistake is this line:
UITableViewCell *cell = [self tableView:self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:k
It returns a new cell, not the one currently on the screen. I replaced it with
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:kPasswordRowInde
and now it works as expected.
On a side note, I found out that 0 is the default value for the tag property, so it's probably not so clever to use it.
0 is the default value for the tag property so you'll probably want to use something other than 0, otherwise you will most likely return the superview when you call viewWithTag:
It's been a while since I developed for the iPhone, and I have never used the tag that you show in your code. But you can do what you want by making the textfields properties of your class. If you do that, and let's say you name those properties loginTextField and passwordTextField, then you can let the next textField be focused as follows:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if([self usernameTextField] == textField) {
return [[self passwordTextField] becomeFirstResponder];
}
else {
// your validating code...
}
return NO;
}
But as I said, it's been a while and I don't know this tag-thing you talk about, so maybe it's some new best practice, but the above code should be working