0

I have a UITableViewCell nib file in which there is a UIImageView and a UILabel. I have outlets for both of these to my controller as well as an outlet for the cell itself. I am setting the label and image programmatically, but the image does not show up.

So I went to test it, and even if I set the image itself in the nib file, it does not show up. If I set the background color, it shows up fine. Any ideas? I'm stuck.

It seems to be unrelated to code, in my mind, since it doesn't even work via nib file. But here it is anyway in case it helps somehow.

MyViewController.h

@interface MyViewController : UITableViewController

@property (strong, nonatomic) MyModel *myModel;
@property (strong, nonatomic) NSArray *tableViewCells;

@property (strong, nonatomic) IBOutlet UITableViewCell *tableViewCell;
@property (weak, nonatomic) IBOutlet UILabel *myLabel;
@property (weak, nonatomic) IBOutlet UIImageView *myImage;

@end

MyViewController.m

@interface MyViewController ()

- (void)bindMyModel:(MyModel*)model toView:(UITableViewCell*)view;

- (UITableViewCell*)copyUITableViewCell:(UITableViewCell*)cell;

@end

@implementation MenuViewController

@synthesize myModel = _myModel;
@synthesize tableViewCells = _tableViewCells;

@synthesize tableViewCell = _tableViewCell;
@synthesize myLabel = _myLabel;
@synthesize myImage = _myImage;

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSMutableArray *cells = [[NSMutableArray alloc] init];
    [MyModel loadAndOnSuccess:^(id data, id context) {
        self.myModel = data;
        for (MyModel *item in self.myModel.items) {
            [[NSBundle mainBundle] loadNibNamed:@"TableCellNib" owner:self options:nil];
            [self bindMyModel:item toView:self.tableViewCell];
            [cells addObject:[self copyUITableViewCell:self.tableViewCell]];
            self.tableViewCell = nil;
        }
        self.tableViewCells = [[NSArray alloc] initWithArray:cells];
    } onFail:^(NSString *error, id context) {
        NSLog(@"FAIL with error: %@", error);
    }];
}

- (void)viewDidUnload
{
    [self setTableViewCell:nil];
    [self setMyLabel:nil];
    [self setMyImage:nil];
    [super viewDidUnload];
}

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

- (void) bindMyModel:(MyModel*)model toView:(UITableViewCell*)view
{
    if (view) {
        self.myLabel.text = model.myLabelText;
        self.myImage.image = model.myImageResource;
        self.myLabel = nil;
        self.myImage = nil;
    }
}

- (UITableViewCell*)copyUITableViewCell:(UITableViewCell*)cell
{
    NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject: cell];
    return [NSKeyedUnarchiver unarchiveObjectWithData: archivedData];
}

#pragma mark - Table view data source

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [self.tableViewCells objectAtIndex:indexPath.row];
}

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

#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Irrelevant code here
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return ((UITableViewCell*)[self.tableViewCells objectAtIndex:indexPath.row]).frame.size.height;
}

@end
Alec
  • 1,646
  • 3
  • 19
  • 35
  • Whether label shows the text or not? – rishi Jun 29 '12 at 16:11
  • Also have you connected these outlets to xib file properly. – rishi Jun 29 '12 at 16:12
  • The label shows up fine. And the outlets are connected properly. Like I said, even if I set the UIImageView image in the nib file itself, it doesn't show up. But the background color will, if I set that! – Alec Jun 29 '12 at 16:14
  • Have you added test_image.png file in your project and is this in the project folder? – rishi Jun 29 '12 at 16:15
  • Yes it is there. If I go to the UIImageView in Interface Builder, go to the Attributes Inspector, and click the drop down next to Image, it shows up. It also shows up in the preview of my nib file. It just doesn't display when I run. – Alec Jun 29 '12 at 16:17
  • @lu yuan, it is necessary for the other code I'm doing, but pretty irrelevant to this question. Like I said, I don't think my code is the problem. At least not this code haha. I can set and see the image in the nib preview, and it doesn't show up on run, even if I comment out all the outlet code. – Alec Jun 29 '12 at 16:18
  • have you tried to comment it? – lu yuan Jun 29 '12 at 16:23
  • What does `cellForRowAtIndexPath` look like? – Phillip Mills Jun 29 '12 at 16:24
  • @luyuan: Yes I have. @PhillipMills: I am actually loading my cells in the `viewDidLoad:` method. My table is exceptionally small so I don't need any cell re-use. I also need to load it ahead of time because I do model binding and need to know my variable cell height ahead of time for `tableView:heightForRowAtIndexPath:`. So my `cellForRowAtIndexPath` is just pulling the appropriate cell from an array. – Alec Jun 29 '12 at 16:49
  • I added my entire implementation code. Just for you guys =). So I suppose if you have critiques on that, feel free to add them in ^^. – Alec Jun 29 '12 at 17:00
  • Did you run your app in a simulator or an iphone? – lu yuan Jun 29 '12 at 17:03
  • Which SDK are you using? Prior to iOS 5.1 UIImage (which UIImageView uses) was not NSCoding compliant. Also why are you setting self.myImage to nil? Is self.myImage the image failing to appear? – Robotic Cat Jun 29 '12 at 17:12
  • @luyuan: I am running it on an iPad emulator through Xcode. @RoboticCat: I'm using iOS 5.1. I set self.myImage to nil because (if you look at the new code I added), I am actually loading the same nib file multiple times. I don't know if that actually helps, but when I was looking at apple documentation for loading a custom `UITableViewCell` from a nib file, they reset the outlet to nil afterward. It seems to be it doesn't provide any extra safety, but I have also tried commenting it out, and there is no change. And yes, self.MyImage is the image that is failing to appear. – Alec Jun 29 '12 at 18:11
  • Okay, progress. The problem was with my trying to construct a deep copy of my `UITableViewCell` when it does not implement `NSCopying`. Using the archive method apparently doesn't archive the pointer to an image, so that got lost in the process. So I don't need a deep copy, I just need to change it into a regular `NSArray` instead of `NSMutableArray` so that each image persists in its own `UITableViewCell`. Thanks for everyone's help! – Alec Jun 29 '12 at 19:51
  • The way you are creating your cells is kind of mind boggling. Why are you using an IBOutlet on your view controller to point to an image and a label that is repeated in each cell? You should subclass `UITableViewCell` and create the outlets as properties of the cell and then set them in the cell. Your `bindMyModel:toView:` method is not binding things to the `UITableViewCell` it is binding them to the `UIViewController` IBOutlets. – Justin Paulson Jun 29 '12 at 19:54
  • No it is binding them to the cell. The outlets are just pointers to the cell, and the outlet changes which cell it points to each time I initialize from the nib file. – Alec Jun 29 '12 at 20:08
  • Check out [Apple Documentation](http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7) of how to create a custom UITableViewCell from a nib file. Listing 5-5 has an IBOutlet to a single UITableViewCell that they reuse because it points to a different cell every time the `loadNibNamed:` method is called. The only difference is whereas in the example they load the subviews using `viewWithTag:`, I think managing tags is way harder than just having another outlet. Not very OO though. – Alec Jun 29 '12 at 20:13
  • You have to use `viewWithTag` because you can't use the same outlet for all of the different labels. It is different than what apple is doing with the cell outlet in that documentation. They don't even use that cell outlet if you follow their `cellForRowAtIndexPath` method in 5-5. They only use it to create a cell if for some reason the dequeue returns a nil cell, which is probably not going to happen. – Justin Paulson Jun 29 '12 at 20:32
  • You definitely can use an outlet for all the different labels. I don't ever need them all at once. I just need to access the one that was last created. – Alec Jun 29 '12 at 20:45

1 Answers1

1

You are trying to use two IBOutlets on your UITableViewController to populate a multitude of UILabels and UIImageViews that are part of the UITableViewCell. You need to create a custom subclass of UITableViewCell and add the IBOutlets to that subclass.

@interface CustomCell : UITableViewCell

@property (weak, nonatomic) IBOutlet UILabel *myLabel;
@property (weak, nonatomic) IBOutlet UIImageView *myImage;

@end

then in your bindMyModel:toView:

- (void) bindMyModel:(MyModel*)model toView:(CustomCell*)view
{
    if (view) {
        view.myLabel.text = model.myLabelText;
        view.myImage.image = model.myImageResource;
    }
}

Now you have independent IBOutlets for each of your Cells. You will also need to change some of your bindings as well. This is a fix, but honestly I would rewrite a lot of the code and just use dequeueReusableCellWithIdentifier in your cellForRowAtIndexPath call, and keep a pool of CustomCells that you will reuse, and just set up the myLabel.text and myImage.image in the cellForRowAtIndexPath call.

Justin Paulson
  • 4,388
  • 1
  • 23
  • 28
  • But then won't calculating row height be much more complex if I use dequeueable cells? My thinking was (since I have very few rows) if I construct them ahead of time, I can just check the view frame. And I ended up getting it working, but a custom view cell is infinitely more object oriented than what I was doing. – Alec Jun 29 '12 at 20:06
  • I wouldn't say it is "infinitely more object oriented" and either way, object oriented is what you want when you are programming, it is how this stuff works. If you want to bind things with IBOutlets, you don't want the IBOutlets to point to more than 1 single object. Calculating row height is not that much more complex, you still have all of your data for each row, you can calculate it the same way you do now (although I really don't see where you are calculating it) – Justin Paulson Jun 29 '12 at 20:10
  • See my comment on your comment on how Apple documentation has an outlet being reused. And obviously not "infinitely" =P. And to calculate row height if I create the cell view in `cellForRowAtIndexPath`, then I have to actually calculate heights of text, and add in margins and padding myself. If the view is created, I can just use the frame property. At least that's how it looks to me. – Alec Jun 29 '12 at 20:15
  • How is it creating the height now exactly?? And I wasn't as much concerned with the IBOutlet of the Cell, but more so of the IBOutlet of the label and imageview. If you look at the documentation, it uses reusable cells and pulls the label and imageview out of each cell using `viewWithTag.` This is an easy method and doesn't require you to subclass `UITableViewCell` if you are not comfortable with it. Plus, your bindMyModel does not do anything. You pass in the cell as `view` but you don't even use it! – Justin Paulson Jun 29 '12 at 20:27
  • Oh yeah I actually pass in the cell outlet in my code, I messed it up when I rewrote to post. Whoops. – Alec Jun 29 '12 at 20:44