UITableViewCell - check mark one row per section

2019-06-11 21:18发布

问题:

I have 2 sections and 3 rows each as follow:

  1. SEARCH DISTANCE

    • 250 feet

    • 1000 feet

    • 4000 feet

  2. MAP TYPE

    • Standard

    • Satellite

    • Hybrid

I want to have one check mark for a row per section but my current code will uncheck all visible cells for the entire table view and leave one selected row with check mark. In other word, I will have one check mark for the entire table (2 sections). Here I posted my entire code. I have google'd many but there seems none to resolve my problem. Anyone, please help correct my code. Thanks in advance.

#import "PAWSettingsViewController.h"
#import "PAWAppDelegate.h"
#import <Parse/Parse.h>

@interface PAWSettingsViewController ()

- (NSString *)distanceLabelForCell:(NSIndexPath *)indexPath;
- (PAWLocationAccuracy)distanceForCell:(NSIndexPath *)indexPath;


- (NSString *)maptypeLabelForCell:(NSIndexPath *)indexPath;
- (PAWMaptypeSelect)maptypeForCell:(NSIndexPath *)indexPath;

@property (nonatomic, assign) CLLocationAccuracy filterDistance;

@end



typedef enum {
    kPAWSettingsTableViewDistance = 0,
    kPAWSettingsTableViewMaptype,
    kPAWSettingsTableViewNumberOfSections
} kPAWSettingsTableViewSections;


typedef enum {
    kPAWSettingsTableViewDistanceSection250FeetRow = 0,
    kPAWSettingsTableViewDistanceSection1000FeetRow,
    kPAWSettingsTableViewDistanceSection4000FeetRow,
    kPAWSettingsTableViewDistanceNumberOfRows
} kPAWSettingsTableViewDistanceSectionRows;

typedef enum {
    kPAWSettingsTableViewMaptypeSectionStandardRow = 0,
    kPAWSettingsTableViewMaptypeSectionSatelliteRow,
    kPAWSettingsTableViewMaptypeSectionHybridRow,
    kPAWSettingsTableViewMaptypeNumberOfRows
} kPAWSettingsTableViewMaptypeSectionRows;


@implementation PAWSettingsViewController

@synthesize tableView;
@synthesize filterDistance;


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
        PAWAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
        self.filterDistance = appDelegate.filterDistance;
    }
    return self;
}


#pragma mark - Custom setters

// Always fault our filter distance through to the app delegate. We just cache it locally because it's used in the tableview's cells.
- (void)setFilterDistance:(CLLocationAccuracy)aFilterDistance {
    PAWAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    appDelegate.filterDistance = aFilterDistance;
    filterDistance = aFilterDistance;
}

#pragma mark - View lifecycle

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

#pragma mark - Private helper methods

- (NSString *)distanceLabelForCell:(NSIndexPath *)indexPath {
    NSString *cellText = nil;
    switch (indexPath.row) {
        case kPAWSettingsTableViewDistanceSection250FeetRow:
            cellText = @"250 feet";
            break;
        case kPAWSettingsTableViewDistanceSection1000FeetRow:
            cellText = @"1000 feet";
            break;
        case kPAWSettingsTableViewDistanceSection4000FeetRow:
            cellText = @"4000 feet";
            break;
        case kPAWSettingsTableViewDistanceNumberOfRows: // never reached.
        default:
            cellText = @"The universe";
            break;
    }
    return cellText;
}

- (PAWLocationAccuracy)distanceForCell:(NSIndexPath *)indexPath {
    PAWLocationAccuracy distance = 0.0;
    switch (indexPath.row) {
        case kPAWSettingsTableViewDistanceSection250FeetRow:
            distance = 250;
            break;
        case kPAWSettingsTableViewDistanceSection1000FeetRow:
            distance = 1000;
            break;
        case kPAWSettingsTableViewDistanceSection4000FeetRow:
            distance = 4000;
            break;
        case kPAWSettingsTableViewDistanceNumberOfRows: // never reached.
        default:
            distance = 10000 * kPAWFeetToMiles;
            break;
    }

    return distance;
}

- (NSString *)maptypeLabelForCell:(NSIndexPath *)indexPath {
    NSString *cellText = nil;
    switch (indexPath.row) {
        case kPAWSettingsTableViewMaptypeSectionStandardRow:
            cellText = @"Standard";
            break;
        case kPAWSettingsTableViewMaptypeSectionSatelliteRow:
            cellText = @"Satellite";
            break;
        case kPAWSettingsTableViewMaptypeSectionHybridRow:
            cellText = @"Hybrid";
            break;
        case kPAWSettingsTableViewMaptypeNumberOfRows: // never reached.
        default:
            cellText = @"?";
            break;
    }
    return cellText;
}

- (PAWMaptypeSelect)maptypeForCell:(NSIndexPath *)indexPath {
    PAWMaptypeSelect maptype = nil;
    switch (indexPath.row) {
        case kPAWSettingsTableViewMaptypeSectionStandardRow:
            maptype = @"Standard";
            break;
        case kPAWSettingsTableViewMaptypeSectionSatelliteRow:
            maptype = @"Satellite";
            break;
        case kPAWSettingsTableViewMaptypeSectionHybridRow:
            maptype = @"Hybrid";
            break;
        case kPAWSettingsTableViewMaptypeNumberOfRows: // never reached.
        default:
            maptype = nil;
            break;
    }

    return maptype;
}

#pragma mark - UINavigationBar-based actions

- (IBAction)done:(id)sender {
    [self.presentingViewController dismissModalViewControllerAnimated:YES];
}

#pragma mark - UITableViewDataSource methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return kPAWSettingsTableViewNumberOfSections;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    switch ((kPAWSettingsTableViewSections)section) {
        case kPAWSettingsTableViewDistance:
            return kPAWSettingsTableViewDistanceNumberOfRows;
            break;

        case kPAWSettingsTableViewMaptype:
            return kPAWSettingsTableViewMaptypeNumberOfRows;
            break;

        case kPAWSettingsTableViewNumberOfSections:
            return 2;
            break;
    };
}

- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *identifier = @"SettingsTableView";
    if (indexPath.section == kPAWSettingsTableViewDistance) {
        UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:identifier];
        if ( cell == nil )
        {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier];

        }

        // Configure the cell.
        cell.textLabel.text = [self distanceLabelForCell:indexPath];

        if (self.filterDistance == 0.0) {
            NSLog(@"We have a zero filter distance!");
        }

        PAWLocationAccuracy filterDistanceInFeet = self.filterDistance * ( 1 / kPAWFeetToMeters);
        PAWLocationAccuracy distanceForCell = [self distanceForCell:indexPath];
        if (abs(distanceForCell - filterDistanceInFeet) < 0.001 ) {
            cell.accessoryType = UITableViewCellAccessoryCheckmark;
        } else {
            cell.accessoryType = UITableViewCellAccessoryNone;
        }


        return cell;
    }

    else if (indexPath.section == kPAWSettingsTableViewMaptype){
        UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:identifier];
        if ( cell == nil )
        {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier];
        }

        // Configure the cell.
        cell.textLabel.text = [self maptypeLabelForCell:indexPath];


        return cell;

    }


    else {
        return nil;
    }
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    switch ((kPAWSettingsTableViewSections)section) {
        case kPAWSettingsTableViewDistance:
            return @"Search Distance";
            break;

        case kPAWSettingsTableViewMaptype:
            return @"Map Type";
            break;

        case kPAWSettingsTableViewNumberOfSections:
            return @"";
            break;
    }
}

#pragma mark - UITableViewDelegate methods

// Called after the user changes the selection.
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {


    if (indexPath.section == kPAWSettingsTableViewDistance) {
        [aTableView deselectRowAtIndexPath:indexPath animated:YES];



        // if we were already selected, bail and save some work.
        UITableViewCell *selectedCell = [aTableView cellForRowAtIndexPath:indexPath];
        if (selectedCell.accessoryType == UITableViewCellAccessoryCheckmark) {
            return;
        }


        // uncheck all visible cells.
        for (UITableViewCell *cell in [aTableView visibleCells]) {
            if (cell.accessoryType != UITableViewCellAccessoryNone) {
                cell.accessoryType = UITableViewCellAccessoryNone;
            }
        }


        selectedCell.accessoryType = UITableViewCellAccessoryCheckmark;

        PAWLocationAccuracy distanceForCellInFeet = [self distanceForCell:indexPath];
        self.filterDistance = distanceForCellInFeet * kPAWFeetToMeters;
    }



    else if (indexPath.section == kPAWSettingsTableViewMaptype){
        [aTableView deselectRowAtIndexPath:indexPath animated:YES];

        // if we were already selected, bail and save some work.
        UITableViewCell *selectedCell = [aTableView cellForRowAtIndexPath:indexPath];
        if (selectedCell.accessoryType == UITableViewCellAccessoryCheckmark) {
            return;
        }

        // uncheck all visible cells.
        for (UITableViewCell *cell in [aTableView visibleCells]) {
            if (cell.accessoryType != UITableViewCellAccessoryNone) {
                cell.accessoryType = UITableViewCellAccessoryNone;
            }
        }

        selectedCell.accessoryType = UITableViewCellAccessoryCheckmark;

        PAWMaptypeSelect maptypeForCell = [self maptypeForCell:indexPath];

    }

}


@end

回答1:

you have an error with this

// uncheck all visible cells.

    for (UITableViewCell *cell in [aTableView visibleCells]) {
        if (cell.accessoryType != UITableViewCellAccessoryNone) {
            cell.accessoryType = UITableViewCellAccessoryNone;
        }
    }

Here the for loop deselect all the cell. you have to check also the section in if loop

you can set tag for your cell like this

 cell.tag = indexPath.section;

and in for loop if condition is

    for (UITableViewCell *cell in [aTableView visibleCells]) {
        if (cell.accessoryType != UITableViewCellAccessoryNone && cell.tag == indexPath.section) {
            cell.accessoryType = UITableViewCellAccessoryNone;
        }
    }


回答2:

problem in your code is that You only checkmark row for section 0 by replace below method you can solved your problem.

     - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = @"SettingsTableView";
if (indexPath.section == kPAWSettingsTableViewDistance) {
    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:identifier];
    if ( cell == nil )
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier];

    }

    // Configure the cell.
    cell.textLabel.text = [self distanceLabelForCell:indexPath];

    if (self.filterDistance == 0.0) {
        NSLog(@"We have a zero filter distance!");
    }

    PAWLocationAccuracy filterDistanceInFeet = self.filterDistance * ( 1 / kPAWFeetToMeters);
    PAWLocationAccuracy distanceForCell = [self distanceForCell:indexPath];
    if (abs(distanceForCell - filterDistanceInFeet) < 0.001 ) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    } else {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }


    return cell;
}

else if (indexPath.section == kPAWSettingsTableViewMaptype){
    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:identifier];
    if ( cell == nil )
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier];
    }

    // Configure the cell.
    cell.textLabel.text = [self maptypeLabelForCell:indexPath];

    PAWLocationAccuracy filterDistanceInFeet = self.filterDistance * ( 1 / kPAWFeetToMeters);
    PAWLocationAccuracy distanceForCell = [self distanceForCell:indexPath];
    if (abs(distanceForCell - filterDistanceInFeet) < 0.001 ) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    } else {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }
    return cell;

}


else {
    return nil;
}
}


回答3:

For now I have found the following bugs

  1. You don't save which cell is selected for map type.
  2. The way how you checkmark your cell is incorrect.
  3. In switch you don't have to use break after return statement
  4. In case kPAWSettingsTableViewNumberOfSections: you should return kPAWSettingsTableViewNumberOfSections

First two defects causes many boilerplate code. I think you should store the NSIndexPath's for each section that is selected as additional @property. Instead of unchecking all and checking tapped, you can do in tableView:didSelectRowAtIndexPath::

  1. Determine which one should be selected.
  2. Store local old value of your selectedIndexPath.
  3. Change selectedIndexPath proprty to new one.
  4. Reload both rows.

Then you have to add in tableView:cellForRowAtIndexPath: the following code:

if (indexPath.row == self.selectedIndexPath.row && indexPath.section == self.selectedIndexPath.section) {
    // Change to checkmark.
} else { 
   // Change to not checked.
}

I got very similar way to checkmark cell in my own project. I hope the code below also will help you understand my way to fix your problem:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (/*condition*/) {
        [...]
    }
    else if (/*checkmark section*/) {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CheckmarkCellId forIndexPath:indexPath];
        cell.textLabel.text = [self labelForRow:indexPath.row];
        cell.accessoryType = (indexPath.row == [self defaultAgglomerationIndex]) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
        cell.imageView.image = (indexPath.row == [self selectedAgglomerationIndex]) ? [UIImage imageNamed:@"icoMap"] : [UIImage imageNamed:@"region"];
        return cell;
    }
    else if (/*Another condition */) {
        [...]
    }
    return nil;
}

And essencial code in tableView:didSelectRowAtIndexPath: (I reload whole section)

self.selectedIndex = indexPath;
NSIndexSet *set = [NSIndexSet indexSetWithIndex:indexPath.section];
[self.tableView reloadSections:set withRowAnimation:UITableViewRowAnimationNone];


回答4:

Try this, worked just fine for me:

-VARIABLES:

@property NSInteger checkedCellRow;
@property NSInteger checkedCellSection;

-FUNCTIONS:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if (checkedCellSection == indexPath.section)
{
if (checkedCellRow == indexPath.row)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
checkedCellRow = indexPath.row;
checkedCellSection = indexPath.section;
[myTable reloadData];
}