In the Apple reminders App, and in the details screen of a remind,
when you switch on the control "Remind me at a location", a row "Location" is added (in fact a table view cell).
I would like to do the same in one of my application, when a switch control is actived 2 cells are added... how can i do this?
Thank you for your help
Check out this Project
https://github.com/singhson/Expandable-Collapsable-TableView
It may helps you.
in the method handling the change of your UISwitch you could either just reload the complete tableview using -(void)reloadData or (much nicer) use - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
You can use UISwitch as accessory view in one of your cells. However, you can face problems with reusing cells. If you need only one row with switch, you can simply add the UISwitch as a strong property in your TableViewController. You have to create it when initializing the controller:
self.locationSwitch = [[UISwitch alloc] init];
[self.locationSwitch addTarget:self action:#selector(handleSwitchValueChanged:) forControlEvents:UIControlEventValueChanged];
And then you can use it as an accessory view for your UITableViewCell:
cell.accessoryView = self.locationSwitch;
And you have to add (or remove) rows when switch value changes":
-(void)handleSwitchValueChanged:(id)sender
{
NSIndexPath* indexPath = // calculate your index path;
if(self.locationSwitch.on) {
[self.tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
else {
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
And also remember to update your data source, so the value returned by tableView:numberOfRowsInSection is consistent with value of self.locationSwitch.
Related
I have a subclass of UITableViewController.
By default, it creates a UITableView when it is initialized. To that tableview I have set a header that I created in Interface Builder in the screen that is controlled by the controller. The header has two buttons:
one to enter editing mode for the tableview (called "Edit")
one to add a random item to the tableview (called "New").
I linked an IBOutlet property called headerView to the header from Interface Builder and I set it to be the header of the UITableView created at initialization in the viewDidLoad method.
The problem is that when I press the "New" button (which adds a new row with a new item to the tableview) the header of the tableview falls down to the bottom of the tableview.
Any idea why? How can I make it stick to the top?
This is the viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"UITableViewCell"];
[self.tableView setTableHeaderView:self.headerView];
}
This is the method that gets executed when the "New" button is pressed:
- (IBAction)addNewItem:(id)sender {
Item *newItem = [[ItemStore sharedStore] createItem];
NSInteger lastRow = [[[ItemStore sharedStore] allItems] indexOfObject:newItem];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:lastRow inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationTop];
}
Thanks.
Possibly you've set up the layout for table header view in a wrong way. Was there any layout constraint on it?
By the way, this project on GitHub is a very simple example to demonstrate how simply it is to achieve what you want in the storyboard. It will produce the result like below:
Please use this project to compare with your current configuration.
I hope this helps in one way or another.
I have spent hours searching for the solution with out any luck. I am trying to delete a row (also deselect same row) programmatically. After row deletion call below, UITableViewDelgate methods get called expectedly and data source is updated but UITableView is not refreshed. deselectRowAtIndexPath call also does not work. I tried all kinds of scenarios as shown by commented lines.
Here is my code:
checkoutPerson is called as a result of observer listening for NSNotificationCenter messages.
- (void) checkoutPerson: (NSNumber*) personId {
Person *person = [_people objectForKey:personId];
if( person )
{
// Remove person from data source
int rowIndex = person.rowIndex;
S2Log(#"Deleting row number=%d", rowIndex);
[_allKeys removeObjectAtIndex:rowIndex];
[_people removeObjectForKey: personId];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:0];
//[[self tableView] beginUpdates];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
S2Log(#"Deleting indexPath row=%d", [indexPath row]);
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
//[[self tableView] endUpdates];
S2Log(#"Reloading data");
//[[self tableView] reloadData];
//[self performSelector:#selector(refreshView) withObject:nil afterDelay:1.5];
//[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
}
}
I will appreciate for help.
Thanks
-Virendra
I believe deleted cell is not being recycled. If I delete row in the middle, last row is always erased (since there is one less item) but the deleted row remains.
Use the above code between two function for table view
[tableView beginUpdates];
// the deletion code from data source and UITableView
[tableView endUpdates];
By calling this functions you are telling UITableView that you are about to make updates for deleting your cell.
Edit
The other problem I see with your code is you first delete the data from the data source.
Now you are asking for the UITableViewCell (which actually reloads the UITableView)
and then you are deleting the row from UITableView
I guess you should fetch the UITableViewCell before deleting values from your data source.
I found the problem. It has nothing to do with the code I posted above. It is syncing problem between visual display and the contents of data source. I have an embedded UITableView as part of a composite view. In composite view's controller, I was wiring up UITableView's delegate and data source to an instance of UITableViewController. Instead of this, I should have set UITableViewController's tableView property to the embedded UITableView. It seems that UITableView has to be contained within UITableViewController in order to correctly sync up table view visual display to the contents of data source. This also fixes row deselection and scrolling. I also needed to delay reloadData call in which case deleteRowsAtIndexPaths:withRowAnimation is not required. All you need is to modify the contents of your data source and call reloadData with a delay of 1.5 Seconds.
Thanks to all for great help.
I am trying desperately to make this IBAction just effectively press a cell at a selected row. I have managed to get it to select a row, but I can't work out how to effectively click on this cell! I am only making my first app but I have managed to figure most things out by myself, but just can't seem to find out how to do this, i'm hoping it is a simple solution (or there is a much better way to do it than I have).
Here is the code for my IBAction anyway:
- (IBAction)myButton:(id)sender {
// Specify which cell I wan't to select
NSIndexPath *myIP = [NSIndexPath indexPathForRow:0 inSection:0];
// Select it
[self.tableView selectRowAtIndexPath:myIP animated:NO scrollPosition:UITableViewScrollPositionTop];
// Click this cell??
}
Thanks in advance for any help
Just tell the delegate that you've selected it
[self tableView:self.tableView didSelectRowAtIndexPath:myIP];
Assuming that self is your VC that controls the table.
The below stackoverflow answer looks like exactly what you need...
Automatically cell selected UITableView
Not a clean way to achieve but as per my understanding, You can add custom UIButton (transparent) on each cell such a way it covers almost complete cell in cellForRowAtIndexPath:, disable row selection. On these button you can use addTarget:action:
If your view controller that has the UITableView in it, is not subclassing UITableViewController you need to create an IBOutlet of the UITableView call it myTableView or whatever you'd like, then in your IBAction you can reference it like this:
- (IBAction)myButton:(id)sender {
// Specify which cell I wan't to select
NSIndexPath *myIP = [NSIndexPath indexPathForRow:0 inSection:0];
// Select it
[self.myTableView selectRowAtIndexPath:myIP animated:NO scrollPosition:UITableViewScrollPositionTop];
// (Per Dmitry's answer)
[self.myTableView didSelectRowAtIndexPath:myIP];
}
I have a UITableView with two sections. Based on user interactions (selections and deselections), my datasource and UITableView are updated to move data between sections. Initially their is only data in section 0. When I tap a cell, willSelectCellAtIndexPath and didSelectCellAtIndexPath get called. As expected, when I tap the same cell again, didDeselectCellAtIndexPath is called.
Even after I begin to move data down to section 1 and select and deselect, the UITableView's delegate methods are called appropriately.
Once all data has been moved to Section 1, the UITableView begins to exhibit strange behavior. I can initially select a call and didSelectCellAtIndexPath is called. However, when I tap it again, didDeselectCellAtIndexPath is never called. Instead, any taps on the selected cell (I have confirmed it is indeed selected through [tableView indexPathsForSelectedCells] or any other cells in Section 1 only result in willSelectIndexPath and didSelectIndexPath getting called.
I have quite a bit of code in these delegate methods which is unrelated (I believe).... I do not explicitly change the selected state of a cell anywhere. I have posted willSelect method and can post more if necessary.
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if (remainingItemIsSelected && indexPath.section == 0) {
//other cells in the remaining items section are selected and a cell from that section is being selected
NSMutableIndexSet *arrayIndexesToBeDeleted = [[NSMutableIndexSet alloc] init];
for (NSIndexPath *previouslySelectedIndexPath in [tableView indexPathsForSelectedRows]) {
if (((ReceiptItem *)[self.remainingReceiptItems objectAtIndex:previouslySelectedIndexPath.row]).allocated == YES) {
//update data sources
NSLog(#"%#%i%#,%i",#"Section #:",previouslySelectedIndexPath.section,#" Row #:",previouslySelectedIndexPath.row);
[self.assignedReceiptItems addObject:[self.remainingReceiptItems objectAtIndex:previouslySelectedIndexPath.row]];
[arrayIndexesToBeDeleted addIndex:previouslySelectedIndexPath.row];
//update index path arrays
[self.receiptItemsToDeleteIndexPaths addObject:previouslySelectedIndexPath];
[self.receiptItemsToAddIndexPaths addObject:[NSIndexPath indexPathForRow:self.assignedReceiptItems.count-1 inSection:1]];
//update the pressed indexpath to equal to resulting indexpath to pass on to the didSelect method
if (previouslySelectedIndexPath.row < indexPath.row) {
indexPath = [NSIndexPath indexPathForRow:indexPath.row-1 inSection:0];
}
}
}
//Delete assigned items from the remaining receipt items
[self.remainingReceiptItems removeObjectsAtIndexes:arrayIndexesToBeDeleted];
//update table (move allocated item down)
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:self.receiptItemsToDeleteIndexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView insertRowsAtIndexPaths:self.receiptItemsToAddIndexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
if (self.remainingReceiptItems.count == 0) {
[tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic];
}
//other cells in the remaining items section are selected and a cell from assigned items is being selected
return nil;
}
return indexPath;
}
From the Documentation for UITableViewDelegate:
tableView:willDeselectRowAtIndexPath:
This method is only called if there is an existing selection when the user tries to select a different row. The delegate is sent this method for the previously selected row.
If you think this through you will find that what you encounter is expected behavior. Tapping a row that is selected does not call will/didDeselctRowAtIndexPath on this row.
Instead, you could handle this in didSelectRowAtIndexPath for the selected row, i.e. deselect it there.
Possible Alternative
That being said, I think you are abusing the UITableView class. It is really not designed to do this moving stuff. You have no doubt noticed yourself that you have to write a lot of code to make this work -- the very reason you are encountering intractable errors.
It seems to me that a much cleaner (and ultimately more flexible) solution would be to have two separate table (or other) views that notify each other via delegates about datasource changes. Maybe a bit more work setting it up, but surely much less trouble down the road.
FYI - Noob iOS developer here.
My current setup is a UIViewController with a UIView within, then a UITableView within the UIVIew. So it goes like this...
UIViewController --> UIView --> UITableView
The reason for this is because I have other elements wrapped with the tableview. The UIViewController loads dynamic content into the table view. I have a segmented Control in which I want to use to switch the content within the table view.
I've read something on [table reload] and [table beginUpdate] but don't understand how to use it. Any help would be great.
You need to implement a method for UIControlEventValueChanged event ofUISegmentedControl for this.
[yourSegmentedControl addTarget:self action:#selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];
And implement the segmentChanged method like:
- (void)segmentChanged:(id)sender
{
UISegmentedControl *mySegment = (UISegmentedControl *)sender;
switch ([mySegment selectedSegmentIndex])
{
case 1:
//load first contents
break;
case 2:
//load second contents
break;
default:
break;
}
[self.yourTableView reloadData];
}
Ok, so [table reloadData] will reload the data (so if you change the data and want to update the table with the necessary data call this), but straight after you call that make sure to call [table setNeedsDisplay] to refresh the UI.[table beginUpdates]
begins a series of method calls that insert, delete, or select rows and sections of the receiver. You end the processes with [table endUpdates];
Make sure you set your table view's dataSource and delegate to self, this can be done through the xib and programmatically like this:
[table setDelegate: self];
or
[table setDataSource: self];
As said:
Call this delegate method for UISegmentedControl
- (void)segmentedControl:(UISegmentedControl*)segmentedControl didSelectIndex:(NSUInteger)selectedIndex
{
if(selectedIndex == 0)
{
// Update the data
}
else if(selectedIndex == 1)
{
// Update the data
}
[table reloadData];
[table setNeedsDisplay];
}
For example,
Your UIView named *myView and your UITableView named *myTableView,
the time you want to reload tableview, in your UIViewController , you should reload tableview like this:
[self.myView.myTableView reload];
and make sure tableview's delegate and dataSourceDelegate is set correctly.
Need to set the delegate & data source for tableview in ViewController.h file like
UITableViewDataSource, UITableViewDelegate.
Implement delegate & datasource methods in ViewController.m file
[tableview SetDelagate:self];
[tableview SetDatasource:self];
Implement the delegate methods.
And reload the table using
[tableView reloadData];
method.