How to properly add and delete objects to tableview with custom tableviewcells? - arrays

I'm currently developing an app that behaves like the Messages.app. The MasterViewController is the main view where it loads a table of the contact name, time and a snippet of the most recent message. When you tap a specific cell, it slides to the DetailViewController where it loads the messages I sent to the contact with the latest, complete message. Hitting the back button goes back to the MasterViewController. Tapping the rightBarButtonItem opens up a ComposeViewController (modal) where the user can compose a message to a specific contact. The difference of this app to the default Messages.app is that it has a delay timer before the message is sent. The ComposeViewController has a textfield to input the message, button to select a contact, button to pick a time delay, button to send, button to cancel the timer, and a button to dismiss the ModalViewController.
I removed the ability to send an actual SMS message entirely. I just presented the user with an alert view telling him/her that the message was sent and if he/she wants to compose a new one. Hitting Cancel will dismiss the ModalViewController and go back to the MasterViewController.
Problem is, I can't make the rows appear on the table and also have the ability to add and remove cells in the table.
Here's some code inside my MasterViewController's viewDidLoad:
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Delete button to delete messages
UIBarButtonItem *deleteBarButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemTrash
target:self
action:#selector(deleteText)];
self.navigationItem.leftBarButtonItem = deleteBarButtonItem;
// Compose button to go to compose messages
UIBarButtonItem *composeBarButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCompose
target:self
action:#selector(composeText)];
self.navigationItem.rightBarButtonItem = composeBarButtonItem;
[deleteBarButtonItem release];
[composeBarButtonItem release];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *message = [defaults objectForKey:kMessageText];
NSString *contactname = [defaults objectForKey:kContactNameText];
NSString *timestamp = [defaults objectForKey:kTimeStampText];
[messageDetails initWithObjectsAndKeys:contactname, kContactNameKey, message, kContactMsgKey, timestamp, kContactTimeKey, nil];
NSMutableArray *messageInfo = [[NSMutableArray alloc] initWithObjects:messageDetails, nil];
self.messagesList = messageInfo;
[messageInfo release];
[super viewDidLoad];
Here's the code in cellForRowAtIndexPath:
CustomCellViewController *customCell = (CustomCellViewController *)[tableView dequeueReusableCellWithIdentifier:#"CustomCellViewController"];
if (customCell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCellViewController"
owner:self
options:nil];
for (id oneObject in nib) if ([oneObject isKindOfClass:[CustomCellViewController class]])
customCell = (CustomCellViewController *)oneObject;
}
NSUInteger row = [indexPath row];
NSDictionary *messages = [self.messagesList objectAtIndex:row];
customCell.nameLabel.text = [messages objectForKey:kContactNameKey];
customCell.nameLabel.textColor = [UIColor whiteColor];
customCell.messageLabel.text = [messages objectForKey:kContactMsgKey];
customCell.messageLabel.textColor = [UIColor lightGrayColor];
customCell.timeLabel.text = [messages objectForKey:kContactTimeKey];
customCell.timeLabel.textColor = [UIColor blueColor];
customCell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return customCell;
Here's the code for deleting cells:
- (void)tableView:(UITableView *)tableView commitEditingStyle(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Delete the row from the data source.
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]withRowAnimation:UITableViewRowAnimationFade];
[messagesList removeObjectAtIndex:indexPath.row];
[self.tableView reloadData];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}

Your final code snippet where you are creating a new instance of the master view controller is the problem.
This is not the view controller you are looking for. Since you've presented the detail view controller modally, you can access the master controller via the parentViewController property of the detail controller:
MasterViewController *master = (MasterViewController*)self.parentViewController;
Other design patterns that are typically used in this situation:
Create the new object in your master controller and insert the row in your table before passing the new object to the detail controller for updating
Create a delegate protocol for the detail controller, which your master controller conforms to. Set the detail controllers delegate to the master controller when pushing it
The latter is almost what you are doing for all practical purposes, except your detail controller knows more than it needs to about the master controller (i.e you are importing the whole master .h file rather than just knowing it conforms to a protocol).
Regarding your data structure, I don't understand how you expect to have multiple rows in here - you store one message in user defaults and then create an array with that message. I know you don't intend to use defaults to store this in the end, but I would expect to see an array of dictionaries stored in defaults under a single key, and then each dictionary represents a row in your table, with the various details stored as strings in the dictionary against your message, contact name keys etc.
You have to make a mutable version of the array returned from defaults as it always returns an immutable array.
In your cellForRow... method you'd then get the appropriate dictionary from the array and populate the cell from it.
When adding a new row you'd create a new dictionary to pass to your detail controller.
When deleting a row you'd remove the relevant dictionary from the array.

Related

How to switch from one view controller to another in a chain

I have a initial view controller, from which i need to push to first view controller. on back click of first view controller instead of going back to initial view controller i need to go to a third view controller. from back click on third view controller it should go back to initial view controller. Could anyone suggest me how to do it in ios6 and also in ios7.
Call setViewControllers:animated: after the push, to insert the middle view controller into the stack.
there are many other way to do but i prefer this one:
for that you have to add programmatically navigation bar button as "back" at first view controller(in your case) but in this case you required back_arrow image:
UIImage *faceImage = [UIImage imageNamed:#"back_arrow.png"];
UIButton *face = [UIButton buttonWithType:UIButtonTypeCustom];
face.bounds = CGRectMake( 10, 0, faceImage.size.width, faceImage.size.height );
[face addTarget:self action:#selector(handleBack) forControlEvents:UIControlEventTouchUpInside];
[face setImage:faceImage forState:UIControlStateNormal];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithCustomView:face];
self.navigationItem.leftBarButtonItem = backButton;
[self.navigationItem setHidesBackButton:YES animated:YES];
[self.navigationItem setLeftBarButtonItem:nil animated:NO];
[self.navigationItem setBackBarButtonItem:nil];
-(void)handleBack
{
//got third view controller here
}
may it will help you.

iOS - searching a table view which is populated by an array of dictionaries

I have a plist file that is an array of dictionaries. Each dictionary has 5 similar keys and the tableview displays the title key. Eventually i will have 136 + titles so i want to implement a search function so to find desired information quickly.
I am using the nspredicate to search the title and it works. The searched result reloads a new table display the searched results. The problem I am having is when a user selects a searched title, the detail view doesn't display the correct information in that dictionary.
I know logically why it doesn't work, but cannot figure out how to fix it.
Thanks
edit
Code for didSelect:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSIndexPath *indexPath1 = nil;
if ([self.searchDisplayController isActive]) {
id searchResult = [searchResultsTitle objectAtIndex:1];
indexPath1 = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
self.detailViewController.detailItem = [searchResultsTitle valueForKeyPath:#"description"];
}
else {
self.detailViewController.detailItem = decsText;
self.detailViewController.title = selectedTitle;
}
}
So When the search is active, the titles are displayed, but I want the search selected title to open up the appropriate detail referred to as 'description'

How can I present a ModalViewController from a UIActivity item picked from UIActivity View Controller?

I am working on an app that presents some data in a detailViewController. I have a rightBarButton in the navbar that presents a UIActivityViewController that is filled with my own UIActivity sublclassed items. Most of them work fine as they are just changing a small aspect to the data from the detail view, but I need one of them to open a modalViewController when selected. I keep getting the following warning from the console.....
Warning: Attempt to present <UINavigationController: 0x1fd00590>
on <UITabBarController: 0x1fde1070> which is already presenting <MPActivityViewController: 0x1fd2f970>
I suppose it is worth noting that the app doesn't crash but the modal view doesn't appear either. I am assuming that the UIActivityViewController is a modal view itself and you can only display one of those at a time so the task is to figure out how to perform my segue after the ActivityView has disappeared, but thats where I am stumped. I welcome any help, thoughts or feedback. I've tried google but haven't had much luck, I assume because The UIActivityViewController is so new.
Here is my setup so far,
my UIActivity objects have a delegate set to the detailViewController for a custom protocol that lets the detailViewController perform the data changes and then update its view.
for the activities in question that should present the modalView controller I have tried a few approaches that all get the same warning.
None of These Works!!!
1) simply tried performing segue from my delegate method
- (void) activityDidRequestTransactionEdit
{
NSLog(#"activityDidRequestTransactionEdit");
[self performSegueWithIdentifier:#"editTransaction" sender:self];
}
2)tried setting a completion block on the UIActivityViewController and having my delegate method set a bool flag that the modal view should be shown(self.editor)
[activityViewController setCompletionHandler:^(NSString *activityType, BOOL completed) {
NSLog(#"completed dialog - activity: %# - finished flag: %d", activityType, completed);
if (completed && self.editor) {
[self performSegueWithIdentifier:#"editTransaction" sender:self];
}
}];
3) subclassing the UIActivityViewController itself, giving it the detailView as a delegate, and overriding it's dismissViewControllerAnimated: method with my own completion block
- (void) dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
{
[super dismissViewControllerAnimated:flag completion:^{
[self.MPActivityDelegate activityDidRequestTransactionEdit];
}];
}
The working Solution
In the UIActivity subclass you need to override this method like so
- (UIViewController *) activityViewController {
MPEditMyDataViewController *controller = [[MPEditMyDataViewController alloc] init];
controller.activity = self; // more on this property below
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
return navController;
}
in your MPEditMyDataViewController.h (the view controller the chosen action should produce)
You need a property back to the activity subclass like so
#property (strong, nonatomic) MPEditMyDataActivity *activity;
in your MPEditMyDataViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self
action:#selector(cancel)];
self.navigationItem.leftBarButtonItem = cancelButton;
}
// here's how you dismiss the view controller when you are done with it
// after saving the changes to your data or whatever the view controller is supposed to do.
-(void) cancel
{
NSLog(#"Cancel Button Pushed");
[self.activity activityDidFinish:YES];
}
#end
Did some more documentation digging and found this method for UIActivity subclassing
- (UIViewController *) activityViewController
it gets my view controller to pop up like I wanted by returning it from here instead of trying to segue it from my detailViewController. Now to figure out how to dismiss it when I'm done with it!!!!

Multiple TableViews in a Single View Xcode

I have one view that has two tableviews, I want one of them to be a twitter feed and the other an RSS feed. The problem is they show up exactly the same, same content. I have tried using another controller, but I cannot do that because i cannot figgure it out.
Not sure if you are still looking for the answer to your question, but in any case it may be helpful for others too. I have tried the following with success:
You can use a separate tableviewcontroller for each tableview. In your viewDidLoad you can alloc init your tableviewcontrollers and then set the tableview's datasource and delegate to be your respective tableviewcontrollers. Finally, set the view of your controllers to be the tableview of your tableview controller. The following code shows what I mean:
- (void)viewDidLoad
{
[super viewDidLoad];
// ...
if(!self.tweeterTableViewController)
{
self.tweeterTableViewController = [[TweeterTableViewController alloc] init];
}
if(!self.rssTableViewController)
{
self.rssTableViewController = [[RssTableViewController alloc] init];
}
[self.tweeterTableView setDataSource:self.tweeterTableViewController];
[self.tweeterTableView setDelegate:self.tweeterTableViewController];
self.tweeterTableViewController.view = self.tweeterTableViewController.tableView;
[self.rssTableView setDataSource:self.rssTableViewController];
[self.rssTableView setDelegate:self.rssTableViewController];
self.rssTableViewController.view = self.rssTableViewController.tableView;
// ...
}

Saving Custom UITableView detail views with SQlite?

I need some help saving custom detail view controllers. In my table view I have an add and edit button where the user can add and delete custom cells. Each cell goes to the same custom view, my only problem is that is there any way that I can save each cell using SQlite or another Database? I just don't want to have to create a ton of viewcontrollers and save each and every cell using NSUserDefaults.
My code :
table . h =
IBOutlet UITableView *warrantyTableView;
NSMutableArray *data;
IBOutlet UITextField *tableCellText;
IBOutlet UITableView *mainTableView;
IBOutlet UINavigationItem *navItem;
}
- (IBAction)addRowToTableView;
- (IBAction)editTable;
- (NSString *)dataFilePath;
- (IBAction)endText;
Table.m =
- (NSString *)dataFilePath {
NSString *dataFilePath;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
dataFilePath = [[documentDirectory stringByAppendingPathComponent:#"applicationData.plist"] retain];
return dataFilePath;
}
- (void)saveData {
[NSKeyedArchiver archiveRootObject:[data copy] toFile:[self dataFilePath]];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
PushedViewController *pushedViewController = [[PushedViewController alloc] initWithNibName:#"PushedView" bundle:nil];
// ...
// Pass the selected object to the new view controller.
PushedViewController *ifView = [[PushedViewController alloc] initWithNibName:#"PushedView" bundle:nil];
[self.navigationController pushViewController:ifView animated:YES];
[ifView release];
}
Why do you want to save each cell in database ? Save all the data in the cells in SQLite. While accessing, get the data from SQLite database to an array of dictionary. The dictionary will contain all data of one cell, the array will contain all the dictionaries having the data of each cell. Then reload the tableview with the data from that array. WWhile editing/adding/deleting objects to the tableview, do the whole operation first from the array of dictionary and then, when a single operation is finished, save it to the sqlite and again reload the tableview.

Resources