UITableView didSelectRowAtIndexPath , different value from what is displayed in section rows! - iphone

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
Directory *aDirectory = [appDelegate.directories objectAtIndex:indexPath.row];
_XMLDetailsView.aDirectory = aDirectory;
if (indexPath.section == 0) // First section
{
_XMLDetailsView.aDirectory = aDirectory;
}
else if(indexPath.section == 1) // Second Section
{
Directory *aDirectory = [appDelegate.directories objectAtIndex:indexPath.row +[listOfItems count]];
_XMLDetailsView.aDirectory = aDirectory;
}
else if(indexPath.section == 2) // Third Section
{
Directory *aDirectory = [appDelegate.directories objectAtIndex:indexPath.row +[listOfItems count] +3];
_XMLDetailsView.aDirectory = aDirectory;
}
else if(indexPath.section == 3) // Fourth Section
{
Directory *aDirectory = [appDelegate.directories objectAtIndex:indexPath.row +[listOfItems count] +9];
_XMLDetailsView.aDirectory = aDirectory;
}
}
is this the correct way to select certain row of value from certain section? because when i didSelectRowAtIndexpath for e.g section "C" , it is suppose to display the value that starts from array (4) . but instead at section "C" , it displays the value from array(0) again. i'm stuck for so long on this , anyone with any suggestion or help?

else if(indexPath.section == 2) // Third Section
{
if(indexPath.row >4)
{
Directory *aDirectory = [appDelegate.directories objectAtIndex:indexPath.row +[listOfItems count] +3];
_XMLDetailsView.aDirectory = aDirectory;
}
}
Try this, I think it helps you.

Directory *aDirectory = [appDelegate.directories objectAtIndex:indexPath.row];
statement at the start of the method chooses the directory using indexPath.row which will not be from the correct section. Try indexPath.section for this.
Directory *aDirectory = [appDelegate.directories objectAtIndex:indexPath.section];

Related

Resizing tableview's first visible section header height

I created a UITableView that contains custom section header views. Now, I want it to display a bit more data on the uppermost current visible section. I plan to use the event scrollViewDidEndDecelerating to update the section headers. Currently, the problem is that I cannot set the section header height for a specific section number.
I did try using heightForHeaderInSection beforehand, but the app just crashes with the following output:
'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
I was using the code:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if (tableView == self.tableView)
{
NSArray *visibleCells = [self.tableView visibleCells];
NSMutableArray *visibleSections = [[NSMutableArray alloc] init];
for (NSInteger index = 0; index < [visibleCells count]; index++)
{
UITableViewCell *currentCell = [visibleCells objectAtIndex:index];
NSIndexPath *currentPath = (NSIndexPath *)[self.tableView indexPathForCell:currentCell];
if (![visibleSections containsObject:[NSNumber numberWithInt:currentPath.section]])
{
[visibleSections addObject:[NSNumber numberWithInt:currentPath.section]];
NSLog([NSString stringWithFormat:#"%ld", (long)[visibleSections count]]);
[visibleSections sortedArrayUsingDescriptors:[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:nil ascending:YES]]];
}
}
if (visibleSections == nil)
{
return 42.0;
}
else if ([[visibleSections objectAtIndex:0] integerValue] == section)
{
return 58.0;
}
else
{
return 42.0;
}
}
}
I couldn't quite work out what went wrong in my heightForHeaderInSection method, but I knew it had something to do with the NSMutableArray, visibleSections.
Any hints or answers as to how I can go about changing the height for a specific section header view outside of heightForHeaderInSection and/or how I can fix my code above would be really helpful.
Edit:
Just to make the solution to my crashing problem a bit clearer, if (visibleSections == nil) should not be used in place of if ([visibleSections count] < 1) or if ([visibleSections count] == 0).
I think you could also do it like this, if you want the first section header to be taller when the table first appears (topSection is an NSInteger property):
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
self.topSection = ((NSIndexPath *)[self.tableView indexPathsForVisibleRows][0]).section;
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:self.topSection] withRowAnimation:NO];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
if (self.topSection == section)
{
return 58.0;
}
else
{
return 42.0;
}
}
OK so it turns out this is a harder problem than it first seems. The best I have come up with so far is
Don't treat the header that is for the top section any different and populate them all with the extra data.
You can show and hide different parts by being clever with positioning the "additional" items so that they will be outside of the parent view's bounds when it is smaller and making the parent view clipToBounds.
Failing that you can make a custom UIView subclass and do some manipulation in layoutSubviews
The end implementation I was settling on was this
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSArray *indexPaths = [self.tableView indexPathsForVisibleRows];
self.topSection = [indexPaths count] ? [indexPaths[0] section] : -1;
if (indexPaths.count > 1) {
self.topSection = [indexPaths[1] section];
}
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
{
if (section <= self.topSection) {
return 60;
} else {
return 20;
}
}
It's by no means perfect but it looked semi reasonable and could be tweaked.
Things to note:
You may need to assess if there is too much work going on in scrollViewDidScroll: but it didn't appear to cause any lag for me (I've not really tested properly)
I set the top section using the second indexPath if available as it looked slightly more pleasing/less clunky
I use section <= self.topSection because the header's before are all of screen so there is no point in reducing the size of them which causes really clunky animation.
So after trying this you may need to dig deeper or want to rethink your design a little
You cannot directly refer to the arrays first object by calling objectAtIndex:0, you gotta stay defensive so change this:
else if ([[visibleSections objectAtIndex:0] integerValue] == section)
{
return 58.0;
}
To
else if([visibleSections count]>0)
{
if ([[visibleSections objectAtIndex:0] integerValue] == section)
{
return 58.0;
}
}
Try changing this line:
NSMutableArray *visibleSections = [[NSMutableArray alloc] init]
to:
NSMutableArray *visibleSections = [NSMutableArray array];
Which initializes the array.

moving to next UITextField in different cells in grouped TableView

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;

Getting a cell number from cellForRowAtIndexPath

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)

Trouble with UITableViewCellAccessory (Checkmark/None toggle)

edit
Well now don't I feel like an idiot. Until now, I haven't been paying any attention to whether or not a cell gets a checkmark. Somehow, I had UITableViewCellAccessoryCheckmark and UITableViewCellAccessoryNone flipped, so it was turning off when I wanted it on and turning on when I wanted it off. Reading code properly really does help with debugging...
/edit
Below is the code I was having so much trouble with.
From tableView:didSelectRowAtIndexPath:
if (i.need == 0) { // item not needed - hide (#) and turn on checkmark
i.need = 1;
cell.textLabel.text = [NSString stringWithFormat:#"%#", i.name];
cell.accessoryType = UITableViewCellAccessoryNone;
} else if (i.need < 0) { // item not needed - hide (#) and turn on checkmark
i.need = -i.need;
cell.textLabel.text = [NSString stringWithFormat:#"%#", i.name];
cell.accessoryType = UITableViewCellAccessoryNone;
} else { // item not needed - show (#) and turn off checkmark
i.need = -i.need;
cell.textLabel.text = [NSString stringWithFormat:#"%# (%d)", i.name, -i.need];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
The following is what you get for making this mistake.
Tap row with checkmark: (#) hidden, checkmark removed
Tap row without checkmark: (#) shown, checkmark not set until next tap
I'm not sure how I made the mistake of not connecting comment and accessory, but there you go. This is what it should look like:
if (i.need == 0) { // item not needed - hide (#) and turn on checkmark
i.need = 1;
cell.textLabel.text = [NSString stringWithFormat:#"%#", i.name];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else if (i.need < 0) { // item not needed - hide (#) and turn on checkmark
i.need = -i.need;
cell.textLabel.text = [NSString stringWithFormat:#"%#", i.name];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else { // item not needed - show (#) and turn off checkmark
i.need = -i.need;
cell.textLabel.text = [NSString stringWithFormat:#"%# (%d)",
i.name, -i.need];
cell.accessoryType = UITableViewCellAccessoryNone;
}

indexPath.row is returning null in tableView didSelectRowAtIndexPath

indexPath.row returns null in tableView didSelectRowAtIndexPath:indexPath
I thought it was supposed to return the row selected.
When i look at indexPath in the debugger it (corectly) returns: "2 indexes [0, 0]"
Am I missing something?
Well null is 0. The row property is type int -- perhaps you're mistakenly using it as an object or pointer.
You mention that you want the actual "row" that was selected. If you mean you want the cell that was selected, here's how:
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
Note that if you somehow programmatically select a row that's not currently visible, this will return nil.
indexPath.section tells you what section it's asking for.
indexPath.row tells you which row in that section it wants.
Both start at 0.
You want to do something like this:
if (indexPath.section == 0) { // 1st section
if (indexPath.row == 0) return cell1;
if (indexPath.row == 1) return cell2;
if (indexPath.row == 2) return cell3;
} else if (indexPath.section == 1) { // 2nd section
if (indexPath.row == 0) return cell4;
if (indexPath.row == 1) return cell5;
if (indexPath.row == 2) return cell6;
}
return nil;
It returns the PATH to the row selected, not the row (or cell) itself.
indexPath(0,0) is the first cell of the first section. Depending on your test you are running, this may be the correct result.