Retain UITableView Content When Switching ViewControllers

Discussion in 'iOS Programming' started by tsuru, Nov 23, 2011.

  1. tsuru macrumors member

    Joined:
    Dec 14, 2008
    #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
    
     
  2. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #2
    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.
     
  3. tsuru thread starter macrumors member

    Joined:
    Dec 14, 2008
    #3
    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!
     
  4. larswik macrumors 68000

    Joined:
    Sep 8, 2006
    #4
    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.
     
  5. tsuru thread starter macrumors member

    Joined:
    Dec 14, 2008
    #5
    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!
     
  6. jnoxx macrumors 65816

    jnoxx

    Joined:
    Dec 29, 2010
    Location:
    Aartselaar // Antwerp // Belgium
    #6
    You could make a Singleton and hold everything you need in there, and when you don't, just remove it. That's how I would've done it, but everyone does it their way :)
     
  7. larswik macrumors 68000

    Joined:
    Sep 8, 2006
    #7
    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.
     
  8. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #8
    Don't take this the wrong way but have you gone on the Apple developer site and searched the term? Examples are given...
     
  9. larswik macrumors 68000

    Joined:
    Sep 8, 2006
    #9
    No, I have not. I have not heard of that term before, 'Singleton'. I will Google it when I get back home tonight.
     
  10. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #10
    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
     
  11. larswik macrumors 68000

    Joined:
    Sep 8, 2006
    #11
    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
     
  12. admanimal macrumors 68040

    Joined:
    Apr 22, 2005
    #12
    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.
     
  13. larswik macrumors 68000

    Joined:
    Sep 8, 2006
    #13
    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.
     

Share This Page