Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

tsuru

macrumors member
Original poster
Dec 14, 2008
42
1
I've looked over many online code samples but I couldn't find examples relevant to this particular situation and so I thought I would try asking.

My application is using the utility application template (a main view and a flipside view) with Automatic Reference Counting (ARC) enabled.

On the flipside view, I have an empty UITableView with a navigation bar with a "Done" button, that returns you to the main view, and an "Add" button. Clicking the "Add" button creates an empty row and activates the UIImagePicker. After selecting a photo from the UIImagePicker, that photo is passed into the new row's cell imageView.

My problem is that, after adding a new row to the table, when I switch to the main view then back to the flipside view, the table is empty again.

Since the NSMutableArray resides in FlipviewController, I suspected that the table was getting emptied when FlipviewController (and consequently the NSMutableArray) was unloaded in order to load MainViewController. As such, I tried moving the NSMutableArray to the AppDelegate which prevented the rows from disappearing when switching view controllers, but for some reason the images that I added in each row still disappeared when switching view controllers.

Anyone know what's going on here? Any help would be greatly appreciated!

AppDelegate.h
Code:
#import <UIKit/UIKit.h>

@class MainViewController;

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@property (strong, nonatomic) MainViewController *mainViewController;

@end

AppDelegate.m
Code:
#import "AppDelegate.h"

#import "MainViewController.h"

@implementation AppDelegate

@synthesize window = _window;
@synthesize mainViewController = _mainViewController;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{    
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.mainViewController = [[MainViewController alloc] initWithNibName:@"MainViewController" bundle:nil];
    self.window.rootViewController = self.mainViewController;
    [self.window makeKeyAndVisible];
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application
{
    /*
     Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
     Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
     */
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    /*
     Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
     If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
     */
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    /*
     Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
     */
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    /*
     Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
     */
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    /*
     Called when the application is about to terminate.
     Save data if appropriate.
     See also applicationDidEnterBackground:.
     */
}

@end

MainViewController.h
Code:
#import "FlipsideViewController.h"

@interface MainViewController : UIViewController <FlipsideViewControllerDelegate>

- (IBAction)showInfo:(id)sender;

@end

MainViewController.m
Code:
#import "MainViewController.h"

@implementation MainViewController

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)viewDidUnload
{
    [super viewDidUnload];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

#pragma mark - Flipside View

- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller
{
    [self dismissModalViewControllerAnimated:YES];
}

- (IBAction)showInfo:(id)sender
{    
    FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:@"FlipsideViewController" bundle:nil];
    controller.delegate = self;
    controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
    [self presentModalViewController:controller animated:YES];
}

@end

FlipsideViewController.h
Code:
#import <UIKit/UIKit.h>

@class FlipsideViewController;

@protocol FlipsideViewControllerDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
@end

@interface FlipsideViewController : UIViewController <UIActionSheetDelegate, UIAlertViewDelegate, UIImagePickerControllerDelegate,UINavigationControllerDelegate>
{
NSMutableArray *dataArray;
IBOutlet UITableView *tableView;
IBOutlet UIBarButtonItem *addPhotoButton;
UITableViewCell *addedCell;
UIImage *image;
UIImagePickerController *imagePicker;
}

@property (nonatomic, strong) NSMutableArray *dataArray;
@property (nonatomic, strong) IBOutlet UITableView *tableView;
@property (nonatomic, strong) IBOutlet UIBarButtonItem *addPhotoButton;
@property (nonatomic, strong) UITableViewCell *addedCell;
@property (nonatomic, strong) UIImage *image;
@property (nonatomic, strong) UIImagePickerController *imagePicker;

@property (weak, nonatomic) IBOutlet id <FlipsideViewControllerDelegate> delegate;

- (IBAction)addPhoto:(id)sender;
- (IBAction)done:(id)sender;
@end

FlipsideViewController.m
Code:
#import "FlipsideViewController.h"
#import "MainViewController.h"

@implementation FlipsideViewController

@synthesize delegate = _delegate;
@synthesize dataArray, tableView, addPhotoButton, addedCell, image, imagePicker;

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
[super viewDidLoad];

self.dataArray = [[NSMutableArray alloc] init];
self.imagePicker = [[UIImagePickerController alloc] init];
self.imagePicker.allowsEditing = NO;    
self.imagePicker.delegate = self;   
self.imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
}

#pragma mark - UITableView delegate methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.dataArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *kCellID = @"cellID";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellID];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellID];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.showsReorderControl = YES;
}

return cell;
}

- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}

- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}

- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

#pragma mark - Actions

- (IBAction)addPhoto:(id)sender
{
[self presentModalViewController:self.imagePicker animated:YES];
}


- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)img editingInfo:(NSDictionary *)editInfo {
// AppDelegate* appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
// appDelegate.pickerImage = img;

image = img;
[self dismissModalViewControllerAnimated:YES];

[self.tableView beginUpdates]; 
[self.dataArray addObject:@""];
NSArray *paths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:(self.dataArray.count - 1) inSection:0]];
[self.tableView insertRowsAtIndexPaths:paths withRowAnimation:NO];
[self.tableView endUpdates];

addedCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:(self.dataArray.count - 1) inSection:0]];
addedCell.imageView.image = image;
[self.tableView reloadData];
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = [indexPath row];
[self.dataArray removeObjectAtIndex:row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}

- (IBAction)done:(id)sender
{
[self.delegate flipsideViewControllerDidFinish:self];
}
@end
 
You should be practising MVC design. The entire Cocoa Touch API is designed around this. This means you separate your Model from your Controller layers. In practice this means you should not ever be storing the rows added in a controller.
 
You should be practising MVC design. The entire Cocoa Touch API is designed around this. This means you separate your Model from your Controller layers. In practice this means you should not ever be storing the rows added in a controller.

Thank you for the reply robbieduncan! Thanks to your advice and advice from a few other resources, I now understand that my problem was mainly logic-related, in how I was incorrectly grouping my data with my view controller. Such a rudimentary concept, but one that I was failing to see... Thanks again!
 
It also took me a long time to start to adopt the MVC and still kind of struggle with it today. My current project I have a UITableView where I gather information and then push a UIViewController on screen with nice graphics. Passing the information from the TableView to the viewController I used a Property list. Save it as I exit one screen and reload the TestField, Labels and so on as the new view comes up.

It works great and everything loads fine.
 
Update

For those wondering what I ended up doing, I now save the image I pick from UIImagePickerController into the app's "Documents" folder, and save the image name in a plist file. To load the image, I read the image name from the plist file, and call for the image in the "Documents" folder that has that same name.

I hope this helps anyone else trying to figure out how to save and load images in their apps!
 
Jknox- I write to a plist for my stuff and retrieve it in the next viewController as well. Can you explain the 'singleton' or point me to any more info on this. Right now I use about 6 property lists in my app. Client address info, jobs and more.

I am always wanting to learn more and different approaches.

Thanks.
 
Jknox- I write to a plist for my stuff and retrieve it in the next viewController as well. Can you explain the 'singleton' or point me to any more info on this. Right now I use about 6 property lists in my app. Client address info, jobs and more.

I am always wanting to learn more and different approaches.

Thanks.

Don't take this the wrong way but have you gone on the Apple developer site and searched the term? Examples are given...
 
No, I have not. I have not heard of that term before, 'Singleton'. I will Google it when I get back home tonight.
 
No, I have not. I have not heard of that term before, 'Singleton'. I will Google it when I get back home tonight.

A Singleton is an object that there is only every a single instance of. Think about NSUserDefaults and how you get the instance of that. It is a Singleton
 
I understand it but I don't quite grasp it yet but I am reading up on it tonight.

The original poster was struggling with something I was struggling with a few months ago. Collect data on viewController number 1 and use it on viewController number 2. I found writing that information to a property list and then reading it back in seemed to be the right way.

The other way, which I was not sure since I am learning too if it would work, is if I could get a handle on that data through inheritance. I would just subclass the first view so VIEW 1 : NSObject and then VIEW 2 : VIEW 1. I was not sure if I just gain access to the instance variables to use, or the data that was stored in the instance variables? But my first view was a table view and my second view was just a regular viewController.

When Jnoxx said I would just make a Singleton to pass that data forward it peeked my interest as to how to do that and what was this magical Singleton I have never heard of :)

Lots to learn still - Thanks
 
The other way, which I was not sure since I am learning too if it would work, is if I could get a handle on that data through inheritance. I would just subclass the first view so VIEW 1 : NSObject and then VIEW 2 : VIEW 1. I was not sure if I just gain access to the instance variables to use, or the data that was stored in the instance variables? But my first view was a table view and my second view was just a regular viewController.

The purpose of inheritance is to modify or extend the functionality of a class, not to share data. You still end up with two objects that you need to pass data between, so you've solved nothing.
 
That is what I thought and was learning in my Java class regarding inheritance, extending the functionality, 'ISA'. But was unsure if that extension also meant forwarding data some how.

It seems unnecessary that as I push a new viewController on screen I write all the data to a plist. Then when I press the back button to return to the last viewController I save all the data to the plist again if changes were made.

It seems like there could be 1 master NSMutableDict or object that every object has access to. That being said I have only explored Property Lists as a storage device so far. Core Data my provide that functionality.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.