How can I add a UITableView into my View-Based Application where the user will tap on more than one cell, and it will become selected, exactly like the Clock app's "New Alarm" setting named "Repeat" (Clock>Alarms> + >Repeat), and how can I get all of the sele开发者_如何学Ccted cells in an array?
For multiple selection, add the line below in viewDidLoad()
tableView.allowsMultipleSelection = true
Configure each cell
after dequeuing (or initializing) it in tableView(_:cellForRowAt:)
let selectedIndexPaths = tableView.indexPathsForSelectedRows
let rowIsSelected = selectedIndexPaths != nil && selectedIndexPaths!.contains(indexPath)
cell.accessoryType = rowIsSelected ? .checkmark : .none
// cell.accessoryView.hidden = !rowIsSelected // if using a custom image
Update each cell when it's selected/deselected
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)!
cell.accessoryType = .checkmark
// cell.accessoryView.hidden = false // if using a custom image
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)!
cell.accessoryType = .none
// cell.accessoryView.hidden = true // if using a custom image
}
When you're done, get an array of all the selected rows
let selectedRows = tableView.indexPathsForSelectedRows
and get the selected data, where dataArray
maps to the rows of a table view with only 1 section
let selectedData = selectedRows?.map { dataArray[$0.row].ID }
In your implementation of -tableView:didSelectRowAtIndexPath:
you would set the table view cell's accessoryType
property depending on its current value (so it would toggle on and off with multiple taps). For example:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)path {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:path];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
} else {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
You could either maintain an array of selected states in addition to the cells' own accessory type state, or iterate over the cells in the table view querying for each one's state in order to read out the selected rows.
@BrendanBreg implementation didn't worked for me. @RaphaelOliveira provided good solution, but when you scrolls your table down - wrong rows become selected (because UITableView
caches it's cells). So, I've slightly modified Raphael's solution:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryNone;
}
/*Here is modified part*/
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
/*
...
Your implementation stays here
we're just adding few lines to make sure
that only correct rows will be selected
*/
if([[tableView indexPathsForSelectedRows] containsObject:indexPath]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
self.tableView.allowsMultipleSelection = YES;
Just a quick tip in addition to the great answer above: to mimic Apple's style from the clock app (making the row select color fade back out after checking / unchecking the row), add this to the didSelectRowAtIndexPath
, after the conditionals:
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
From Apple's TableMultiSelect guide.
Here it is what you needed http://developer.apple.com/library/ios/#samplecode/TableMultiSelect/Introduction/Intro.html#//apple_ref/doc/uid/DTS40011189-Intro-DontLinkElementID_2
I have seen this problem with so many developers. Due to table view's nature of re-using cell it removes or haphazard the checks. I have created a working solution for that. Clone/download code from DevelopmentSupportWorkspace and Execute UITableViewTest
project from there.
Here is code summery for that:
@interface CheckBoxTestTableViewController ()
@property (nonatomic, strong) NSArray *dataArray;
@property (nonatomic, strong) NSMutableDictionary *selectedIndexDictionary;
@end
@implementation CheckBoxTestTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
//
_dataArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ImageList" ofType:@"plist"]];
_selectedIndexDictionary = [NSMutableDictionary dictionary];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _dataArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"checkMarkCell" forIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryNone;
// Configure the cell...
cell.textLabel.text = _dataArray[indexPath.row][@"text"];
if (_selectedIndexDictionary[indexPath] != nil) cell.accessoryType = UITableViewCellAccessoryCheckmark;
return cell;
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
if (_selectedIndexDictionary[indexPath] == nil) {
[_selectedIndexDictionary setObject:_dataArray[indexPath.row] forKey:indexPath];
[[tableView cellForRowAtIndexPath:indexPath] setAccessoryType:UITableViewCellAccessoryCheckmark];
}else{
[_selectedIndexDictionary removeObjectForKey:indexPath];
[[tableView cellForRowAtIndexPath:indexPath] setAccessoryType:UITableViewCellAccessoryNone];
}
// [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
@end
The clock alarms repeat table view is not multiple selection. Think of it as a lit of checkboxes.
When a cell is selected, the font color and accessory type are changed and the selection fades out. When a checked cell is selected, the font color and accessory type are changed back and the selection fades out.
In your didSelectRowAtIndexPath delegate method, you would set the text color and accessory type for the selected cell, then deselect the cell. You would also record the new state in your data model. That could be as simple as a bit mask representing the selected state, but depends on what data you are displaying.
In your cellForRowAtIndexPath: dataSource method, set the text color and accessory type based on your data model.
Actual multiple selection would be similar. You have to keep track of which cells are selected, and set the selected cell of each state as it is created or shown. When the table view reports that a cell is selected, toggle the selection state in your data model and set the selected state of the cell accordingly.
You can't key off indexPath because the cells that refers to changes as you scoll. Put an NSLog in cellForRowAtIndexPath to see that. You can do the check/uncheck in willSelectRowAtIndexPath or didSelectRowAtIndexPath. That covers only the initial check or uncheck though, and will also have things appear as checked once you've scrolled because the underlying cell for a given indexPath changes.
So the solution I found is to have an array of selected things with something that is specific to that given cell, and do the initial check.
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
if (![selectedIndexes containsObject:cell.textLabel.text]){
[selectedIndexes addObject:cell.textLabel.text];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
[selectedIndexes removeObject:cell.textLabel.text];
cell.accessoryType = UITableViewCellAccessoryNone;
}
return indexPath;
}
You also have to have logic in cellForRowAtIndexPath to make sure the right stuff is checked or not as the view scrolls:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
cell.accessoryType = UITableViewCellAccessoryNone;
if ([selectedIndexes containsObject:cell.textLabel.text]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[cell setSelected:YES animated:YES];
}
return cell;
}
1 - Allow multiple selection and toggle of selected state:
tableView.allowsMultipleSelection = true
2 - Collect or get an array of all selected indices when you are done:
let selected = tableView.indexPathsForSelectedRows
Note: This is independent of which cells are currently showing on screen.
3 - Change the appearance of the cell depending on selected state: Override this method in your UITableViewCell subclass. If you don't have a subclass, make one.
// In UITableViewCell subclass
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
accessoryType = selected ? .Checkmark : .None
}
I know that this is an old posting, but for future use, the following code will solve the problem of ensuring that a checkmark will only appear on selected rows during scrolling:
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.accessoryType = (cell.isSelected) ? .checkmark : .none
}
精彩评论