How to Delete Row from Table View from other ViewController - ios

So I have a DetailViewController which displays the details of row/cell from table view. Now I would like to add an option of DELETE on this controller. I added a Bar Button Item(trash) on it. How will I be able to delete the current row/data and remove it also from the TableViewController?
TableViewController
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
WishlistItem *wish = [self.wishlistItem objectAtIndex:indexPath.row];
DetailViewController *dvc = [self.storyboard instantiateViewControllerWithIdentifier:#"dvcID"];
dvc.wishItemStr = wish.wishlistItem;
dvc.dateItemStr = wish.targetDate;
dvc.descItemStr = wish.descWishItem;
[self.navigationController pushViewController:dvc animated:YES];
}

When you create the detailed view controller you must be initializing it with some data relevant to the row. So you can extent that initializer method (or add a new method if you like) which takes a block.
When the table view controller creates and loads the detailed view controller it initializes it with some block code that will delete the relevant row.
Example: (note I haven't compiled this).
Add this to the DetailViewController:
#property (copy, nonatomic) void (^deleteRowBlock)(void);
- (void) onDeletion:(void (^)(void)) deletionBlock;
The implementation of onDeletion is
- (void) onDeletion:(void (^)(void)) deletionBlock
{
self.deleteRowBlock = deletionBlock;
}
When the button is pressed in the DetailViewController call the block like this:
self.deleteRowBlock();
Then in didSelectRowAtIndexPath: add this:
[dvc onDeletion:^{
code to delete the row and update your data model
}];
Then when the button is pressed the "code to delete the row and update your data model" will get executed.
Or alternatively if you don't like blocks (but you should learn to like them) define a protocol with a method such as onDelete:(NSIndexPath*) row. The table view is a delegate of the detailed view and implements the protocol method, which you invoke when the button is pressed.
The detailed view would need to know its row number. Alternatively remove the NSIndexPath as the parameter to onDelete and have the tableView cache the row number of the currently presented detail view controller and when onDelete is called it deletes the row for the cached row number.
But it is preferable to use blocks

well, this is another solution,
1) pass self.wishListItem to DetailViewController, here is example
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
DetailViewController *dvc = [self.storyboard instantiateViewControllerWithIdentifier:#"dvcID"];
...
[dvc setWishListItem:self.wishListItem];
...
[self.navigationController pushViewController:dvc animated:YES];
}
2) in TableViewController implement viewWillAppear method like this
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.tableView reloadData];
}
3) in DetailViewController you have a delete button, right ? here is your button's action method
- (void)onDelete {
// your deleting stuff ...
[self.wishListItem removeObjectAtIndex:self.currentItemIndex]; // this line updates shared data
}
So TableViewController reloads it's data and keeps cells up to date once you get back (by touching back button, for example)
if you still have questions feel free to comment.

Related

How do you pass the object selected in tableView:didSelectRowAtIndexPath to new UIViewControllers pushed onto the stack?

I'm displaying a list of items in a tableViewController, then when a row is selected pushing a viewController with a .xib file onto the stack, giving a more detailed explanation of the item in a textView with a few UIButtons to select for further action. All of this works so far.
The problem I'm running into is pushing yet another viewController onto the stack with editable textViews containing information from the original object selected in the tableView. I can get the viewController and .xib to load, but not the data.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
JASDetailViewController *dvc = [[JASDetailViewController alloc]init];
NSArray *ideas = [[JASIdeaStore sharedStore]allIdeas];
JASIdea *selectedIdea = ideas[indexPath.row];
dvc.idea = selectedIdea;
[self.navigationController pushViewController:dvc animated:YES];
}
This populates the DetailViewController perfectly. Then, when moving to the next viewController:
- (IBAction)editButton:(id)sender
{
JASEditViewController *evc = [[JASEditViewController alloc]init];
//NSIndexPath *path = (NSIndexPath *)sender;
//NSIndexPath *path = [[NSIndexPath alloc]init];
//path = [NSIndexPath indexPathForItem:0 inSection:0];
NSArray *ideas = [[JASIdeaStore sharedStore]allIdeas];
JASIdea *selectedIdea = // Looking for a solution here. . .
evc.idea = selectedIdea;
[self.navigationController pushViewController:evc animated:YES];
}
I can get the EditViewController to load, but not the data. The gaps and commented out sections represent some of my attempts to get this to work. By specifying a particular indexPath, I can make an idea show up, but have been unable to get my original selection from the tableView to pass through.
Don't you want this?
JASIdea *selectedIdea = self.idea;
If I understand your code correctly, you're going from an edit button on JASDetailViewController to JASEditViewController, but the JASIdea instance is staying the same. You passed selectedIdea from your table view's didSelectRowAtIndexPath into JASDetailViewController.idea. That means that it's there in self.idea when you're inside JASDetailViewController.editButton
Since you're getting your data from a shared instance, there's no need to pass selectedIdea to JASDetailViewController. Instead, you should create a property, lets call it selectedIndex, and pass that. That way, you'll have that value to pass on to JASEditViewController as well.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
JASDetailViewController *dvc = [[JASDetailViewController alloc]init];
dvc.selectedIndex = indexPath.row;
[self.navigationController pushViewController:dvc animated:YES];
}
In viewDidLoad of JASDetailViewController, you can get the selected idea with,
JASIdea *selectedIdea = ideas[self.selectedIndex];

Reload UIViewController in "viewDidLoad"

I change the value of 2 UILabels in my "viewDidLoad" method, but I need the view to refresh after that in order to display the values. As it currently stands, the UILabels display the value of the previously selected cell. I need to do the refresh right after I change the labels' values. The "setNeedsDisplay" method is not doing the job.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_nameLabel.text = _selectedLocation.name;
_addressLabel.text = _selectedLocation.address;
[self.view setNeedsDisplay];
}
Based on your comments, I think you are trying to do something like:
- (void)updateLabelTexts {
_nameLabel.text = _selectedLocation.name;
_addressLabel.text = _selectedLocation.address;
}
and wherever you are changing the _selectedLocation values:
//Just an example
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
_selectedLocation = _yourLocationsArray[indexPath.row];
//now you call your update method
[self updateLabelTexts];
}
The point is that you have to call [self updateLabelTexts]; just after you update the values.
A very stupid bug. Turns out when I made the segue to transition into the next view, I actually dragged it from a physical cell on to the destination controller. However, I should've simply connected the sending uiview controller to the destination viewcontroller with the segue, and then manually handled the transition. That fixed it, so there's no need to "refresh or reload" the UIView as I was trying to do.

Calling methods when UITableViewCell is pressed

I would like to call a method when a UITableViewCell is selected/tapped. I could do it easily with a static table view, but it requires a UITableViewController which is not good for me in this case, therefore I'm using a normal vc.
I have 10 specified methods like this:
- (void) methodOne {
NSLog(#"Do something");
}
- (void) methodTwo {
NSLog(#"Do something");
}
....
And I would like to call the methodOne when the first cell was tapped, call the methodTwo when the second cell was tapped and so on..
As a first step I set the numberOfRowsInSection to return 10 cells, but have no idea how could I connect the selected cells with the methods. Is there any quick way to do it? It would be a dirty solution to create 10 custom cells and set the every method manually for the custom cells, and there is no free place for it.
You can create an array of NSStrings with method names in the order they should be called from their corresponding UITableViewCells.
NSArray *selStringsArr = #[#"firstMethod", #"secondMethod", #"thirdMethod];
Then create a selector in tableView:didSelectRowAtIndexPath: from the strings array and call it using performSelector:.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *selString = selStringsArr[indexPath.row];
SEL selector = NSSelectorFromString(selString);
if ([self respondsToSelector:#selector(selector)]) {
[self performSelector:#selector(selector)];
}
}
Of course, there are some limitations to using performSelector: which you can read here.
You can use this method for whenever any cell is tapped on the table view
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger selectedRow = indexPath.row; //this is the number row that was selected
switch (selectedRow)
{
case 0:
[self methodOne];
break;
default:
break;
}
}
Use selectedRow to identify which row number was selected. If the first row was selected, selectedRow will be 0.
Don't forget to set the table view's delegate to your view controller. The view controller also has to conform to the UITableViewDelegate protocol.
#interface YourViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
As long as the table view has a data source and a delegate, it doesn't matter what kind of view controller it is on. All a UITableViewController really is is a UIViewController that already has a table view on it and is that table view's delegate and data source.

How to pass the in-place edited content back to the previous tableview controller (update the model)

I have two table views, one called mainTableViewController (mtvc), the other called detailTableViewController (dtvc). It's very typical click the accessory button on the main tableview cell bring you to the detail tableview kinda thing.
In the prepareForSegue method, the data passed from the main tableview to detail tableview is a NSMutableArray called item.
And this is how I got it displayed: cell.detailTextLabel.text = self.item[indexPath.row];
The cool thing is I managed to do in-place editing on the detail table view cell (overwrote the NSTableViewCell, added a UITextField as subview to each cell).
everything works, the last thing I spent whole day cannot figure out is how do I update the NSMutableArray item after in-place editing taken place, the ultimate goal is in-place editing, and the main tableview data shall reflect the change.
I tried to use delegation and protocol but it does not work (the in-place edited content didn't got passed back, part of the reason is I don't know how to capture the edited content, it's not like it's a text field with a name, I can't just do updatedContent = self.myTextField.text to grab the change)
I'm running out of ideas, any help would be highly appreciated, thanks.
Here's the prepareForSegue in the main tableview controller
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"toInventoryDetail"]) {
NSMutableArray *selectedItem = nil;
if (self.searchDisplayController.active) {
selectedItem = _searchResults[[sender row]];
} else {
selectedItem = _appDelegate.items[[sender row]];
}
UPFInventoryDetailTableViewController *idtvc = segue.destinationViewController;
idtvc.item = selectedItem;
}
}
and here's the cellForRowAtIndex at the detail tableview controller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UPFEditableUITableViewCell *cell = [[UPFEditableUITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:CellIdentifier];
cell.textLabel.text = _appDelegate.title[indexPath.row];
cell.detailTextLabel.text = self.item[indexPath.row];
[cell showEditingField:YES];
return cell;
}
I wrote the delegation but delete them after cause they didn't work.
I had an idea, still using delegation and protocol obviously: when the 'done' button in the detail tableview hit, I go grab all the row contents and build a new array, using delegation to pass this new array back to the main tableview controller, add this new array into the model meanwhile delete the old one. The tricky thing is still HOW CAN I GRAB ALL THE CONTENTS in the detail tableview?
update:
Haha! I think solved half of the puzzle !
here's the solution for the detail tableview controller
- (IBAction)doneUpdate:(UIBarButtonItem *)sender {
[self.delegate addItem:[self newItem]];
}
- (NSMutableArray *)saveItem
{
NSMutableArray *newItem = [[NSMutableArray alloc] init];
NSArray *indexPathes = [self.tableView indexPathsForVisibleRows];
for (NSIndexPath *indexPath in indexPathes) {
UPFEditableUITableViewCell *cell = (UPFEditableUITableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
[newItem addObject:cell.editField.text];
}
return newItem;
}
and here's the main tableview controller
- (void)addItem:(NSArray *)item
{
//take the updated item then insert the items array as new item
[_appDelegate.items addObject:item];
//remove the selected item (the one being updated) from the items array
[_appDelegate.items removeObject:_appDelegate.selectedItem];
[self.tableView reloadData];
[self.navigationController popViewControllerAnimated:YES];
}
When you creating a cell - give tags to your UITextFields
You can collect data entered by its delegate methods - you can either make NSDictionary/ key value pairs or you can add it to NSArray.
- (void)textFieldDidEndEditing:(UITextField *)textField {
if(textField.tag == 11) {
// you can add it to your desired array/dictionary
}
}
OR
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if(textField.tag == 11) {
// you can add it to your desired array/dictionary
}
}
You can use Delegation/Protocol or store this values in NSUserDefault and get it back on mainViewController.
Do you have a separate data model class(classes) for your selectedItem? That would be the appropriate way to persist data between the two TableViewControllers. It can be Core Data or simply a NSMutableArray that lives in memory. The DetailViewController updates the item and saves the changes, then the mainTableViewController reloads the TableView (or even just the data backing the previously edited cell.
Perhaps even consider the Model-View-Controller-Store pattern promoted by BigNerdRanch.

UITableView - Accessing in Code from ViewWillAppear

I would like to manipulate table cells in my UITableView as it comes back from its detail view, to remind the user what row they last selected. The UITableView was created in IB and there currently isn't a #property or custom class for the UITableView or any other handle. Clearly, it exists when, the user is coming back from the detail view and viewWillAppear is called but I don't see any handle in the debugger for my UITableView.
Any way to simply get a handle to the IB tableView object?
// - The Containing ViewController
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSIndexPath *selected = [self.tableView indexPathForSelectedRow];
if(selected){
//Do something stylish with the last-selected row
NSLog(#"Last row selected was: %#",selected);
}
}
I solved this by creating a class member of NSIndexPath * and set it inside didSelectRowAtIndexPath. Then, when viewWillAppear executes, when the user navigates back fram the table view detail, I have the last-selected row so I can easily highlight it or treat it any way I want:
//The Containing View Controller
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(self.lastSelectedRow){
[self.tableView selectRowAtIndexPath:self.lastSelectedRow animated:YES scrollPosition:UITableViewScrollPositionNone];
}
}

Resources