2

Following this tutorial from Scott Sherwood, (http://www.scott-sherwood.com/ios-5-creating-a-custom-side-tabbar-using-storyboards-and-custom-segues/), I have created a custom tab bar that uses a subview to load in other views. Everything works beautifully and I'm very happy with the results. However, when I add a table view into one of the "tab pages" and connect it to a data source, the app is crashing.

I am using ARC and have zombies turned on, so my console tells me that a message has been sent to a deallocated instance of my table view. I cannot figure out how to retain the table view so it will display my data. This is only my second app, and I am not too familiar with storyboards and advanced view controllers yet. What am I missing?

Here is my custom tab view controller:

CustomTabBarViewController.h

@interface CustomTabBarViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>{

}

@property(weak,nonatomic)UIViewController *currentViewController;
@property(weak,nonatomic)IBOutlet UIView *placeholder;
@property (weak, nonatomic) IBOutlet UIImageView *tabIndicator;
@property (weak, nonatomic) IBOutlet UIImageView *headerBacker1;
@property (weak, nonatomic) IBOutlet UIImageView *headerBacker2;



@end

CustomTabBarViewController.m

#import "CustomTabBarViewController.h"

@interface CustomTabBarViewController ()

@property (weak, nonatomic) IBOutlet UIView *buttons;

@end

@implementation CustomTabBarViewController

@synthesize currentViewController;
@synthesize placeholder;
@synthesize buttons;
@synthesize tabIndicator;
@synthesize headerBacker1;
@synthesize headerBacker2;


- (void)viewDidLoad
{
    [super viewDidLoad];
    [self performSegueWithIdentifier:@"ProductSegue" sender:[self.buttons.subviews objectAtIndex:0]];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)viewDidUnload
{
    [self setTabIndicator:nil];
    [self setHeaderBacker1:nil];
    [self setHeaderBacker2:nil];
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    if([segue.identifier isEqualToString:@"ProductSegue"]
       || [segue.identifier isEqualToString:@"ContactSegue"]
       || [segue.identifier isEqualToString:@"CategoryPage"]){


        if([segue.identifier isEqualToString:@"ContactSegue"]){

            [UIView animateWithDuration:0.25f delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{
                [tabIndicator setCenter:CGPointMake(614, 108)];
                [headerBacker1 setCenter:CGPointMake(1024, 48)];
                [headerBacker2 setCenter:CGPointMake(1024, 789)];
            } completion:nil];

        }else{
            [UIView animateWithDuration:0.25f delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{
                [tabIndicator setCenter:CGPointMake(499, 108)];
                [headerBacker1 setCenter:CGPointMake(341, 48)];
                [headerBacker2 setCenter:CGPointMake(341, 789)];
                } completion:nil];

        }
    }
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}

@end

And the page that gets loaded into the tab, which contains the table view: (Note*: This page is linked as the tableview data source and table view delegate in the storyboard)

ProductListViewController.h

#import <UIKit/UIKit.h>

@interface ProductListViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>


@property (strong, nonatomic) NSMutableArray *maTheData;


@end

ProductListViewController.m

#import "ProductListViewController.h"

@interface ProductListViewController ()

@end

@implementation ProductListViewController


@synthesize maTheData;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        //
    }
    return self;
}


- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"viewDidLoad");
    if (!maTheData) {
        maTheData = [[NSMutableArray alloc] initWithObjects:@"Comets", @"Asteroids", @"Moons", nil];
    }
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


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


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [maTheData count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"categoryTableCell"];
    UILabel *lblName = (UILabel *)[cell viewWithTag:100];
    [lblName setText:[maTheData objectAtIndex:[indexPath row]]];

    return cell;
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Navigation logic may go here. Create and push another view controller.
    /*
     <#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil];
     // ...
     // Pass the selected object to the new view controller.
     [self.navigationController pushViewController:detailViewController animated:YES];
     */
}


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    //return UIInterfaceOrientationIsLandscape(interfaceOrientation);
    return interfaceOrientation == UIInterfaceOrientationLandscapeRight;

}

@end

Thanks for your thoughts.

Malloc
  • 15,434
  • 34
  • 105
  • 192
Rozzadon
  • 123
  • 2
  • 10
  • Maybe I am missing something, but where is your UITableView property? – JonahGabriel Apr 22 '13 at 19:40
  • Do you get an error message with your crash? – rdelmar Apr 22 '13 at 19:54
  • @ate50eggs: I did not specifically add one. I added the table view in my storyboard. After your comment however, i did add a property in ProductListViewController.h and synthesize it in the implementation file, but the result is the same. – Rozzadon Apr 22 '13 at 20:56
  • @rdelmar: since I enabled zombies, the only error message I receive is: -[ProductListViewController numberOfSectionsInTableView:]: message sent to deallocated instance 0x18d400 – Rozzadon Apr 22 '13 at 20:56
  • That error is saying that the deallocated object is ProductListViewController, not the table view. What kind of segue are you doing to go to that controller? – rdelmar Apr 23 '13 at 02:40

3 Answers3

0

I am guessing the problem you are having is due to the fact that you have weak properties instead of strong ones, especially for your currentViewController. Try changing them all to strong and see if you still have issues.

Taken from: Weak and strong property setter attributes in Objective-C

weak (iOS4 = unsafe_unretained )

  • it says "keep this as long as someone else points to it strongly" the same thing as assign, no retain or release
  • A "weak" reference is a reference that you do not retain.
  • We generally use weak for IBOutlets (UIViewController's Childs).This works because the child object only needs to exist as long as the parent object does.
  • a weak reference is a reference that does not protect the referenced object from collection by a garbage collector.
  • Weak is essentially assign, a unretained property. Except the when the object is deallocated the weak pointer is automatically set to nil
Community
  • 1
  • 1
JonahGabriel
  • 3,066
  • 2
  • 18
  • 28
0
@property (nonatomic, strong) IBOutlet UITableView *tableView;

Connect this property with the tableview in the storyboard. This should stop the tableview from dealicating because the view controller has a strong pointer. From a debugging point start placing break points in the areas you think are causing the error to locate the line that the code breaks. Then you can investigate the error deeper. I hope this helps.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
ejkujan
  • 271
  • 1
  • 3
  • IBOutlets are generally weak, because the object they point to is a subview of some other view (self.view in this case), and that view holds a strong pointer to the object. – rdelmar Apr 23 '13 at 00:09
  • Yes but your table view is being deallicated when you need it so saying strong is a way of saying you are going to decide when it gets deallicated. It's a way to keep it in memory so you can use it. – ejkujan Apr 23 '13 at 02:26
  • No, the error is saying that the controller, ProductListViewController, is being deallocated, not the table view. Even with a weak pointer the table view will live as long as the view it's a subview of. The OP's problem is not with the table view, but with the controller. – rdelmar Apr 23 '13 at 02:39
0

As far as I can tell from the link you provided, the controller you're segueing to is supposed to be pointed to by currentViewController. So, the problem is with this declaration:

@property(weak,nonatomic)UIViewController *currentViewController;

That needs to be strong, not weak. It's OK for the IBOutlets to be weak because the objects pointed to are subviews of another view which holds a strong reference to them. But, currentViewController should be strong to keep your controller from being deallocated (nothing else has a strong reference to it). I see from the link that he sets the currentViewController to the destination controller of the segue in the perform method -- if you're doing that, and you change the declaration to strong, you should be OK.

rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • That's it of course. Thank you rdelmar. I had in incorrectly assumed that the deallocated object was the tableView, not the parent view. And thanks for the deeper explanation of strong/weak referencing, that makes good sense. – Rozzadon Apr 23 '13 at 13:54