0

I'm creating an application which implements a custom UITableView. The way I have done this is as follows:

There is a UINavigationController that I created. This UINavigationController pushes a UIViewController implementing UITableViewDelegate and UITableViewDataSource. I create a .xib for this UIViewController therefore having 3 file for the UIViewController: TestTableViewController.h, TestTableViewController.m and TestTableViewController.xib.

The TestTableViewController.xib has only a UITableView placed linked to a IBOutlet UITableView called testTableView and this UITableView dataSource and delegate are linked to the File's Owner (the TestTableViewController).

Also, I have implemented custom UITableViewCell. I created a empty .xib called TestCell.xib where I placed a UITableViewCell and inserted a UIView, a UILabel, a UIImageView and a UISwitch. Then I created a class inherited from UITableViewCell as follows (I defined the TestCell.xib's view as TestCell class and set the identifier to TestCellIdentifier):

TestCell.h:

#import <UIKit/UIKit.h>

#define kTestHeight 40


@interface TestCell : UITableViewCell {
    UIImageView *testImage;
    UILabel *testLabel;
}

@property (nonatomic, retain) IBOutlet UIImageView *testImage;
@property (nonatomic, retain) IBOutlet UILabel *testLabel;

@end

TestCell.m:

#import "TestCell.h"


@implementation TestCell

@synthesize testImage, testLabel;

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
    }
    return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

- (void)dealloc
{
    [self.testLabel release];
    [self.testImage release];
    [super dealloc];
}

@end

I implement all the TestTableViewController as follows:

TestTableViewController.h:

#import <UIKit/UIKit.h>


@interface TestTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
    NSArray *labels;
    NSArray *images;
    UITableView *testTableView; // this is the UITableView linked in the NIB
}

@property (nonatomic, retain) NSArray *labels, *images;
@property (nonatomic, retain) IBOutlet UITableView *testTableView;

@end

TestTableViewController.m:

#import "TestTableViewController.h"
#import "TestCell.h"


@implementation TestTableViewController

@synthesize labels, images;
@synthesize testTableView;

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

- (void)dealloc
{
    [self.labels release];
    [self.images release];
    [self.testTableView release];
    [super dealloc];
}

- (void)didReceiveMemoryWarning
{
    // Releases the view if it doesn't have a superview.
    [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 from its nib.
    self.title = @"Test";
    self.labels = [[NSArray alloc] initWithObjects:
                    @"Test 1",
                    @"Test 2",
                    @"Test 3", 
                    @"Test 4",
                    @"Test 5",
                    @"Test 6",
                    @"Test 7",
                    @"Test 8",
                    @"Test 9",
                    @"Test 10",
                    @"Test 11",
                    @"Test 12",
                    @"Test 13",
                    @"Test 14",
                    @"Test 15",
                    @"Test 16",
                    @"Test 17", nil];
    self.images = [[NSArray alloc] initWithObjects:
                         [UIImage imageNamed:@"empty1.png"],
                         [UIImage imageNamed:@"empty2.png"],
                         [UIImage imageNamed:@"empty3.png"],
                         [UIImage imageNamed:@"empty4.png"],
                         [UIImage imageNamed:@"empty5.png"],
                         [UIImage imageNamed:@"empty6.png"],
                         [UIImage imageNamed:@"empty7.png"],
                         [UIImage imageNamed:@"empty8.png"],
                         [UIImage imageNamed:@"empty9.png"],
                         [UIImage imageNamed:@"empty10.png"],
                         [UIImage imageNamed:@"empty11.png"],
                         [UIImage imageNamed:@"empty12.png"],
                         [UIImage imageNamed:@"empty13.png"],
                         [UIImage imageNamed:@"empty14.png"],
                         [UIImage imageNamed:@"empty15.png"],
                         [UIImage imageNamed:@"empty16.png"],
                         [UIImage imageNamed:@"empty17.png"], nil];
    self.testTableView.separatorColor = [UIColor colorWithRed:0 green:0.21 blue:0.43 alpha:1];
}

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

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

#pragma mark - Table view data source methods

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return the cell's height
    return kTestCellHeight;
}

-  (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    return [self.labels count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *TestCellIdentifier = @"TestCellIdentifier";

    TestCell *cell = (TestCell *)[tableView dequeueReusableCellWithIdentifier:TestCellIdentifier];
    if (cell == nil) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"TestCell" 
                                                     owner:self 
                                                   options:nil];
        for (id oneObject in nib) {
            if ([oneObject isKindOfClass:[TestCell class]]) {
                cell = (TestCell *)oneObject;
            }
        }
    }

    NSInteger row = [indexPath row];
    cell.testLabel.text = [self.labels objectAtIndex:row];
    cell.testImage.image = [self.images objectAtIndex:row];

    return cell;
}

@end

I guess this is all the description of the context needed. Now the problem. When I set one of the switches in the table to OFF (they are by default ON) and then scroll down and then up again, the table starts tweaking random switches to OFF and ON. All this very randomly.

Anyone knows why this could be happening or how to avoid it?

jglievano
  • 501
  • 4
  • 21
  • 2
    ***NEVER USE PROPERTY ACCESSORS IN `-dealloc` or `-init`. EVER.*** – Jacob Relkin Mar 31 '11 at 22:03
  • 1
    You're saying that the UISwitch is appearing with random data, but I see nowhere that you're setting this data? Shouldn't there be a line in cellForRowAtIndexPath that sets that value? – mackworth Mar 31 '11 at 23:31
  • @jacob-relkin What's the reasoning behind your advice? Allowing property accessors in init can lead to less buggy code -- because your accessor manages the nature of the property correctly (retain vs assign), you can't forget to retain a property, and you can't introduce memory problems by altering the retain/assign aspect and then forgetting to also fix up the init method. Are you thinking about the risk of subclasses overriding setter methods and doing things when the super is in an inconsistent state? – occulus Apr 01 '11 at 01:50
  • Found more discussion on this here: http://stackoverflow.com/questions/1283419/valid-use-of-accessors-in-init-and-dealloc-methods – occulus Apr 01 '11 at 01:57
  • @mackworth - you are right, I was thinking that the switches would be persistent but now that you mention it is obvious you have to set it up in cellForRowAtIndexPath. I think I'll go and try that. – jglievano Apr 01 '11 at 12:41

1 Answers1

0

Mackworth was right. I just had to create an array with the boolean values for the state of each UISwitch and assign it in cellForrowAtIndexPath.

jglievano
  • 501
  • 4
  • 21