UITableView per-cell selection mode while editing - iphone

In my UITableView, when it enters editing mode, I'd like only a select few cells to be selectable. I know the UITableView class has the property allowsSelectionDuringEditing, but this applies to the whole UITableView. I don't see any relevant delegate methods to set this on a per-cell basis.
The best solution I can come up with is to set allowsSelectionDuringEditing to YES. Then, in didSelectRowAtIndexPath, filter out any unwanted selections if the table view is editing. Also, in cellForRowAtIndexPath, change those cells selectionStyle to None.
The problem with this is that going into editing mode does not reload the UITableViewCells, so their selectionStyle doesn't change until they scroll offscreen. So, in setEditing, I also have to iterate over the visible cells and set their selectionStyle.
This works, but I just wonder if there is a better/more elegant solution to this problem. The basic outline of my code is attached. Any suggestions greatly appreciated! Thank you.
- (void) tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
if (self.editing && ![self _isUtilityRow:indexPath]) return;
// Otherwise, do the normal thing...
}
- (UITableViewCell*) tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
// UITableViewCell* cell = ...
if (self.editing && ![self _isUtilityRow:indexPath])
{
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
else
{
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
}
return cell;
}
- (void) setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
if (editing)
{
for (UITableViewCell* cell in [self.tableView visibleCells])
{
if (![self _isUtilityRow:[self.tableView indexPathForCell:cell]])
{
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
}
}
else
{
for (UITableViewCell* cell in [self.tableView visibleCells])
{
if (![self _isUtilityRow:[self.tableView indexPathForCell:cell]])
{
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
}
}
}
}

I'm not sure how your app works, but perhaps you could try make use of the following somewhere in your DataSource definition:
// Individual rows can opt out of having the -editing property set for them. If not implemented, all rows are assumed to be editable.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath;
when going into editing mode, use the function to filter the first selection level, then on to your second selection level

Related

How do I cover the "no results" text in UISearchDisplayController's searchResultTableView?

I don't want to show the "no results" text while my server is processing a search query.
I figured out the exact coordinates of the table cell that contains the label and attempted to cover it.
self.noResultsCoverView = [[[UIView alloc] initWithFrame:CGRectMake(
0.0,
44.0,
320.0,
43.0
)] autorelease];
self.noResultsCoverView.backgroundColor = [UIColor whiteColor];
[self.searchDisplayController.searchResultsTableView addSubview:self.noResultsCoverView];
To my chagrin, my cover was above the table view, but below the label. I need the cover to be above the label. searchResultsTableView::bringSubviewToFront didn't work, which makes me believe that the label isn't a child of the searchResultsTableView at all.
BTW, this Stack Overflow answer doesn't quite work for me. It works on the very first search, but flashes a weird black cover on subsequent searches.
this should do the work properly. The code to return at least one cell:
BOOL ivarNoResults; // put this somewhere in #interface or at top of #implementation
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
if (filteredList.count == 0) {
ivarNoResults = YES;
return 1;
} else {
ivarNoResults = NO;
return [filteredList count];
}
}
// {…}
// return the unfiltered array count
}
and for "showing" the clean cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.searchDisplayController.searchResultsTableView && ivarNoResults) {
static NSString *cleanCellIdent = #"cleanCell";
UITableViewCell *ccell = [tableView dequeueReusableCellWithIdentifier:cleanCellIdent];
if (ccell == nil) {
ccell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cleanCellIdent] autorelease];
ccell.userInteractionEnabled = NO;
}
return ccell;
}
// {…}
}
The easiest way to work around this is to return 1 in numberOfRowsInSection while the query is in progress and leave the dummy cell empty or set its hidden property to YES so it is not visible.
Try this it worked for me
In the UISearchDisplayController delegate do this:=
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.001);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
for (UIView* v in self.searchDisplayController.searchResultsTableView.subviews) {
if ([v isKindOfClass: [UILabel class]] &&
[[(UILabel*)v text] isEqualToString:#"No Results"]) {
[(UILabel*)v setText:#""];
break;
}
}
});
return YES;
}
You need to realize that when you have a UISearchDisplayController, and the search bar is active, the UITableView argument passed into your UITableView data source and delegate methods is in fact NOT your tableView object, but a tableView managed by the UISearchDisplayController, intended to display "live" search results (perhaps results filtered from your main data source, for example).
You can easily detect this in code, and then return the appropriate result from the delegate/data source method, depending on which tableView object is asking.
For example:
- (NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section
{
if (tv == self.searchDisplayController.searchResultsTableView) {
// return the number of rows in section for the visible search results.
// return a non-zero value to suppress "No results"
} else {
// return the number of rows in section for your main data source
}
}
The point is that your data source and delegate methods are serving two tables, and you can (and should) check for which table is asking for data or delegation.
By the way, the "No results" is (I believe) provided by a background image which the UISearchDisplayController displays when the delegate says there are no rows... You are not seeing a 2-row table, the first blank and the second with text "No results". At least, that's what I think is happening there.

how to make a uitableviewcell unselectable

I am putting together a search string based off 4 tableviewcells, each cell opens a subview and loads a bunch of data the user selects to set the cell of the previous view.
There is an order in which these cells needs to be set so that each preceding list of data in the subview is related to the data set in the parent view.
i.e. in the first cell you select a type of car, in the next cell you look at the models related to the type of car chosen.
That aside The basis of my question is how do I make a cell unselectable until the previous cell/s have been set.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//...
if (indexPath.section == 0)
{
if (indexPath.row == 0) // <<--- what could I put in here....
{
//...
}
}
}
Disallow the cell to track any interaction:
[cell setUserInteractionEnabled:NO];
or allow interaction, hiding the selection colour, and when clicked, do nothing.
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
- (void) tableViewDidSelectRow.... {
if(indexPath.row == indexOfCellWithNoUserInteraction) {
//do nothing
}
}
Use tableView:willSelectRowAtIndexPath:. Return nil for the rows you don't want selected.
Swift Syntax
cell.selectionStyle = UITableViewCellSelectionStyle.None

accessory tick on uitableviewcell not showing in some instances

I have two tableviews, one has several tableview cells each cell opens the same subview but initalized with new data..
There are around about 100 - 200 entries into the table and I have a accessory view that is a tick that when a cell is selected it ticks the cell then loads the main view again.
If I select that same cell to get the same dataset back it loads the previously selected cell in the middle of the screen (so it knows its index path) however the tick "depending on how deep in the list" will or will not be visible..
It tends to work in about the top 30/40% of the table but anything lower the tick will not be visible... that is unless I go back and forth getting deeper and deeper each time then sometimes I can get the tick to appear in the deeper part of the tableview.. Would anyone know why this is happening?
Has anyone had something of this nature happen to them before?
At further investigation I think something is going wrong inside this method..
First of all, in the subview once the user selects a cell this method is called
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
[self.navigationController popViewControllerAnimated:YES]; //pops current view from the navigatoin stack
//accesses selected cells content
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
// now you can use cell.textLabel.text
//This if statment is based off which cell was selected in the parent view so that it knows which cell to pass the data back to
if (parentViewSelectedIndexPath.section == 0) {
if (parentViewSelectedIndexPath.row == 0) {
manufactureCellTextLabel = cell.textLabel.text; //passing label text over to NSString for use with delegate (check "viewwilldissapear")
[[self delegate] setManufactureSearchFields:manufactureCellTextLabel withIndexPath:indexPath]; //This is where I pass the value back to the mainview
}
// a few more If statements for the other methods I can pass data too.
//--- this if block allows only one cell selection at a time
if (oldCheckedData == nil) { // No selection made yet
oldCheckedData = indexPath;
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
}
else {
UITableViewCell *formerSelectedcell = [tableView cellForRowAtIndexPath:oldCheckedData]; // finding the already selected cell
[formerSelectedcell setAccessoryType:UITableViewCellAccessoryNone];
[cell setAccessoryType:UITableViewCellAccessoryCheckmark]; // 'select' the new cell
oldCheckedData = indexPath;
}
}
This passes Index path over to the main view method...
- (void) setManufactureSearchFields:(NSString *)cellLabeltext withIndexPath:(NSIndexPath *)myIndexPath
{
manufactureSearchObjectString = cellLabeltext;
manufactureResultIndexPath = myIndexPath;
[self.tableView reloadData]; //reloads the tabels so you can see the value.
}
//Which then sets the manufactureResultIndexPath that is used in the next method to pass it back to the subview
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
//--- Idendify selected indexPath (section/row)
if (indexPath.section == 0) {
//--- Get the subview ready for use
VehicleResultViewController *vehicleResultViewController = [[VehicleResultViewController alloc] initWithNibName:#"VehicleResultViewController" bundle:nil];
// ...
//--- Sets the back button for the new view that loads
self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Back" style: UIBarButtonItemStyleBordered target:nil action:nil] autorelease];
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:vehicleResultViewController animated:YES];
[vehicleResultViewController setDelegate:self];
if (indexPath.row == 0)
{
vehicleResultViewController.title = #"Manufacture";
[vehicleResultViewController setRequestString:#"ID.xml"]; //sets the request string in searchResultsViewController
vehicleResultViewController.dataSetToParse = #"ID"; // This is used to controll what data is shown on subview... logic
[vehicleResultViewController setAccessoryIndexPath:manufactureResultIndexPath]; //sends indexpath back to subview for accessory tick
vehicleResultViewController.parentViewSelectedIndexPath = indexPath;
}
//etc etc
}
And finaly I pass it to the method in my subview that passes the indexpath to oldCheckedData
- (void)setAccessoryIndexPath:(NSIndexPath *)myLastIndexPath
{
oldCheckedData = myLastIndexPath;
[self.tableView reloadData]; //<<---- this is where I reload the table to show the tick...
}
Try moving the cell.accessoryType = lines to the willDisplayCell: delegate function like so:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
// You can move this one here too:
cell.selectionStyle = UITableViewCellSelectionStyleNone; // no blue selection
if (indexPath == oldCheckedData) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
I read that the willDisplayCell: method should be used for any basic visual related modifications to a cell like selectionStyle/accessoryType, and the cellForRowAtIndexPath: method for cell data related operations like setting text, images, etc...
I have recently come across this issue, if turned out the in my case the cell has a accessoryview set. This snippet withh ensure the view is removed.
public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
..logic here to to determine if cell should be selected...
if ( cell.accessoryView != nil) {
cell.accessoryView?.removeFromSuperview()
cell.accessoryView = nil
}
cell.accessoryType = .checkmark

iPhone: Problem with setting custom cell's accesory

I can not set the custom cells accessory view while the page loads for the first time, any ideas?
In DetailView customcell's class I have this:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
if(self.selected)
self.accessoryType=UITableViewCellAccessoryCheckmark;
else
self.accessoryType=UITableViewCellAccessoryNone;
}
And In tableview controller:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
DetailViewCell *cell = [DetailViewCell cellForTableView:tableView];
if(somelogichere){
[cell setSelected:YES];
}
return cell;
}
I debug and hit that line so logic is true, but in debug I also see that after it sets the accesory then again it calls twice the selected method, which overrrides the accessory to none in the else inthe second call. setSelected causes the selected method to be called twice and overrides the setting on the second call cause somehow on second call self.selected returns false;
UPDATE: I solved the problem by creating a boolean cellSelected property in the custom cell class and changing and checking its status rather than setting and changing the selected property of the cell, this is also better cause I can support multi selection tableview's better in future.
Does it work the second time after the view has loaded?
It most probably has something to do with following line:
if(somelogichere){
[cell setSelected:YES];
}
What is somelogichere ?
Depending on somelogichere your selected property might not be true the first time you run your code:
if(self.selected)
self.accessoryType=UITableViewCellAccessoryCheckmark;
else
self.accessoryType=UITableViewCellAccessoryNone;
Make sure you set your cell's selected property in the beginning. Or change the code inside your setSelected.
HTH
You try this code its help u
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
UIImage *selectionBackground = [UIImage imageNamed:#"list_BG.jpg"];
UIImageView *iview=[[UIImageView alloc] initWithImage:selectionBackground];
self.selectedBackgroundView=iview;
}
OR
In cellForRowAtIndexPath method of tableView:
cell.accessoryType = UITableViewCellAccessoryCheckmark;

Is it possible to configure a UITableView to allow multiple-selection?

For the iPhone, is it possible to configure a UITableView such that it will allow multiple-selection?
I've tried overriding -setSelected:animated: for each UITableViewCell, but trying to fudge the required behavior is tricky as it's difficult to separate the real unselections from the ones where the UITableView thinks I've unselected due to selection of another cell!
Hope someone can help!
Thanks,
Nick.
Following property should work fine if you are developing app for iOS5.0+
self.tableView.allowsMultipleSelection = YES;
The best way to do this would be to a checkmark per selected row.
You can do that by setting the accessoryType on the selected UITableViewCell instances to UITableViewCelAccessoryCheckmark.
To deselect the row, set it back to UITableViewCellAccessoryNone.
To enumerate which cells/rows were selected (say, upon clicking a button), simply iterate over the cells of the table looking for UITableViewCellAccessoryCheckmark. Or, manage some NSSet or the like in your table view delegate in the "did select" delegate methods.
Use the following code to set up the cell accesory types:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *thisCell = [tableView cellForRowAtIndexPath:indexPath];
if (thisCell.accessoryType == UITableViewCellAccessoryNone) {
thisCell.accessoryType = UITableViewCellAccessoryCheckmark;
}else{
thisCell.accessoryType = UITableViewCellAccessoryNone;
}
}
- (UITableViewCellAccessoryType)tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath {
//add your own code to set the cell accesory type.
return UITableViewCellAccessoryNone;
}
Jeff Lamarche has a tutorial on how to do this here:
http://iphonedevelopment.blogspot.com/2008/10/table-view-multi-row-edit-mode.html
I've not tried the code but it's been on the back of my mind for a while, knowing the day will come when I need it.
I backported allowsMultipleSelectionDuringEditing and allowsMultipleSelection from iOS5 to older iOS. You can fork it at https://github.com/ud7/UDTableView-allowsMultipleSelection
It's drop in replacement and only thing you need to do is change UITableView to UDTableView (in code or interface builder)
From the HIG:
Table views provide feedback when users select list items. Specifically, when an item can be selected, the
row containing the item highlights briefly when a user selects it to show that the selection has been received.
Then, an immediate action occurs: Either a new view is revealed or the row displays a checkmark to indicate
that the item has been selected. The row never remains highlighted, because table views do not display a
persistent selected state.
You'll need to roll your own multiple selection style, either with something like Mail, or using the checkmark accessory on your cells.
Guys for multiple selection you just need
self.tableView.allowsMultipleSelection = YES;
on viewDidLoad and
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *tableViewCell = [tableView cellForRowAtIndexPath:indexPath];
tableViewCell.accessoryView.hidden = NO;
// if you don't use custom image tableViewCell.accessoryType = UITableViewCellAccessoryCheckmark;
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *tableViewCell = [tableView cellForRowAtIndexPath:indexPath];
tableViewCell.accessoryView.hidden = YES;
// if you don't use custom image tableViewCell.accessoryType = UITableViewCellAccessoryNone;
}
I was searching for the same issue and the answer of Bhavin Chitroda sovled it for me but with some addition to keep the check mark as it was while scrolling.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if ( [array indexOfObject:indexPath] == NSNotFound ) {
[array addObject:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
[array removeObject:indexPath];
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
The addition:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
// Your code here
.
.
.
if ( [array indexOfObject:indexPath] == NSNotFound ) {
cell.accessoryType = UITableViewCellAccessoryNone;
} else {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
return cell;
}
If you're trying to do something like Mail's multiple-select (to delete mail, for example), then you're probably going to have to manage all the selection yourself. Multiple row selection isn't something that's standard on the iPhone. Mail solves this by using checkmarks to indicate which rows have been selected.
blue highlighted row as an indicator of whether a row is selected is actually discouraged according to the HIG page 121. Checkmarks will do the trick.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
int selectedRow = indexPath.row;
cout << "selected Row: " << selectedRow << endl;
UITableViewCell *indexPathForCell = [tableView cellForRowAtIndexPath:indexPath];
if (indexPathForCell.accessoryType == UITableViewCellAccessoryNone) {
indexPathForCell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
indexPathForCell.accessoryType = UITableViewCellAccessoryNone;
}
}
then add your arraying or how ever you wish to store the data of which were selected.
Note: This does not work in iOS 4+. This is a private, undocumented constant. Do not use it.
If you are not planning to submit your app to the App Store, you can invoke multi-row edit mode by implementing the following method in your UITableViewController delegate:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return 3; // Undocumented constant
}
Tested with iOS4.3 - 6.0
-(void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
if ([controller.searchResultsTableView respondsToSelector:#selector(allowsMultipleSelectionDuringEditing)]) {
controller.searchResultsTableView.allowsMultipleSelectionDuringEditing = YES;
}
else {
controller.searchResultsTableView.allowsSelectionDuringEditing = YES;
}
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellAccessoryCheckmark;
}