質問

I'm trying to build a custom UITableCellView that will contain multiples subviews but right now I'm struggling with only two of them (the biggest ones): a UIImageView (I wouldn't mind to use UIWebView instead since the images are coming from internet) and a UITextView that should present all the text without scrolling, at once. With this scenario I started to build a custom cell using XIB file and subclassing UITableCellView (Autolayout disable) :

enter image description here

I have added two new properties (IBOutlet) to custom class (pretty simple):

@interface NewsFeedPostCell : UITableViewCell<UITextViewDelegate>
{
    UIImageView *picView;
    UITextView *postContent;
}

@property IBOutlet UIImageView *picView;
@property IBOutlet UITextView *postContent;

@end

and after that I'm using them to fill with data (I'm getting some stuff from iTunes that returns a beautiful JSON @"http://itunes.apple.com/search?term=ice%20age&country=us&entity=movie" ). For the moment I'm focused on showing data properly without getting exact sizes for each cell (apart from the size of the texts that are different) but I have lot of issues: content is not properly display because text is not all shown, first row doesn't show image, etc. I read a lot here at stackoverflow but I'm starting to feel pain about the many different ways that people solve it because I have tested also using only code to get it and I have had something similar or even worse.

My main question/complain here is what could be the best way to build a complex table with custom cells with different heights and subviews and how to calculate the sizes, or better than that, when and where (in the code) since heightForRowAtIndexPath is called before I have downloaded images to get their sizes. There are a lots of properties for layouts, redraw and "sizes-fitting" but any of those don't seem to work as I supposed they had to. I feel I'm too far to know how layout in iOS app works and seems to me that UI elements has no flexibility at all :(

This is source code used (by the way I'm using AFNetworking library in order to get images and load them into UIImageView):

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *simpleTableIdentifier = @"PostCell";
     NewsFeedPostCell *cell;

    NSDictionary *post = [self.posts objectAtIndex:indexPath.row];
    NSString *postText = [post objectForKey:@"longDescription"];

    cell= (NewsFeedPostCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];

    if (cell == nil) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"NewsFeedCell" owner:self options:nil];
        cell = [nib objectAtIndex:0];
    }

 CGRect picFrame = cell.picView.frame;
    picFrame = CGRectMake(100, 0, 300, 100);
    [cell.picView setFrame:picFrame];
    [cell.picView  setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[post objectForKey:@"artworkUrl100"]]]
                        placeholderImage:nil
                                 success:^(NSURLRequest *request , NSHTTPURLResponse *response , UIImage *image ){
                                     NSLog(@"Loaded successfully: %d", [response statusCode]);
                                     [cell.picView setImage:image];
                                     [cell.picView sizeToFit];

                                 }
                                 failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
                                     NSLog(@"failed loading: %@", error);
                                 }
     ];

CGSize stringSize = [self calculateTextHeight:postText];

    CGRect postFrame = cell.postContent.frame;
    postFrame = CGRectMake(10, 100, 300, stringSize.height);
    cell.postContent.frame = postFrame;

    cell.postContent.autoresizingMask = UIViewAutoresizingFlexibleHeight;

    cell.postContent.font = [UIFont systemFontOfSize:POST_CONTENT_FONT_SIZE];
    cell.postContent.text= postText;

    [cell.postContent setContentInset:UIEdgeInsetsZero];

    [cell.postContent sizeThatFits:cell.postContent.contentSize];


    return cell;
}

See issues in the screenshots (I'm using iOS simulator simulating an 3.5" retina device and using as target to build app iOS SDK 6.1)

First row (image is under top bar; you have to scroll to see it) This is first row

Another row that doesn't show text properly, I have to scroll down/up to get it: enter image description here

enter image description here

And it is impossible to get last row text complete: enter image description here

役に立ちましたか?

解決

The best way to make custom cells with different heights is:

make a NSMutableArray for cells:

@property (nonatomic,retain)NSMutableArray* cellsArray;

then in m.:

the first method which call is

-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{

make your cell here:

        -(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
            if (!self.cellsArray) {

                self.cellsArray = [[NSMutableArray alloc]init];

            }

            ListCell* cell = [[ListCell alloc] initWithFrame:CGRectMake(0, 0,, 50)];


           // IMPLEMENT your custom cell here, with custom data, with custom,different height:

           // after add this cell to your array:
            [self.cellsArray addObject:cell];

           // IMPORTANT STEP:  set your cell height for the new height:

            cell.frame = CGRectMake(cell.frame.origin.x, cell.frame.origin.y, 303, yourLastItemInCell.frame.origin.y + yourLastItemInCell.frame.size.height);


            return cell.frame.size.height;
        }

after you got the custom cells with different heights :

#pragma mark - conform to delegates

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

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

    return 10;
}

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


    return self.cellsArray[indexPath.row];

}

I hope it helps !

他のヒント

Thanks to all. I tried yours solutions and they work but no completely. The problem with the height is almost solved but I'm experiencing an issue with cell visualization because, when they are a little bit longer, they are not shown completely (images neither texts). The screenshots show first and second rows. You can see that until you scroll down a bit the second row is not visible and, in order to see it, you should scroll down until image is only displayed partially. This is not being so easy as I thought in the beginning. Thank you for your comments and help.

enter image description here enter image description here

Talking about code, I have used incmiko's solution but I still have a problem downloading images because I should do it asynchronously and after that Table View must be redrawn to fit image sizes but I'm not sure how to do it.

Custom Cell:

@interface NewsFeedCell : UITableViewCell<UITextViewDelegate>
{
    UIImageView *picView;
    UITextView *postContent;
    float cellHeight;
}

@property  (nonatomic, retain) UIImageView *picView;
@property (nonatomic, retain) UITextView *postContent;
@property  (nonatomic) float cellHeight;

//-(id)initWithImageUrlString:(NSString *)imgUrl AndText:(NSString *)postText;
-(void)setPostImage:(NSString *)imgUrl;
-(void) setPostContenText:(NSString *)postText;
-(void) setcontentHeight;
-(float) getCellHeight;

@end

Custom cell is iniatilized only with a TextView since some of the future content won't have any image (I know I will have to take care about this while calculating height and Content View size).

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
        [self setFrame:CGRectMake(CELL_LEFT_MARGIN, 0, CELL_TOTAL_WIDTH, 50)];


        postContent = [[UITextView alloc] initWithFrame:CGRectMake(CELL_LEFT_MARGIN, 0, CELL_TOTAL_WIDTH, 100)];
        [self.contentView addSubview:postContent];
    }
    return self;
}

I'm using AFNetworking to download images:

-(void)setPostImage:(NSString *)imgUrl
{
    picView =[[UIImageView alloc] initWithFrame:CGRectMake(0,0, 50, 50)];
    [picView setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:imgUrl]] placeholderImage:nil success:^(NSURLRequest *request , NSHTTPURLResponse *response , UIImage *pic )
     {
        //NSLog(@"Loaded successfully: %d", [response statusCode]);

         [picView setImage:pic];
         [self.contentView addSubview:picView];

         CGRect frame = CGRectMake((self.bounds.size.width/2-pic.size.width/2), 0, pic.size.width, pic.size.height);

         [picView setFrame:frame];

         frame = self.contentView.frame;
         frame.size.height = picView.frame.size.height;
         self.contentView.frame = frame;


    }
    failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
        NSLog(@"failed loading: %@", error);
    }
    ];


}

-(void)setPostContenText:(NSString *)postText
{
    CGSize stringSize = [self calculateTextHeight:postText];

    CGRect frame = postContent.frame;
    frame = CGRectMake(CELL_LEFT_MARGIN, picView.frame.origin.y+picView.frame.size.height, CELL_TOTAL_WIDTH, stringSize.height);
    postContent.frame = frame;
    postContent.autoresizingMask = UIViewAutoresizingFlexibleHeight;
    postContent.font = [UIFont systemFontOfSize:POST_CONTENT_FONT_SIZE];
    postContent.text= postText;
    postContent.textColor=[UIColor blackColor];
    [postContent setContentInset:UIEdgeInsetsZero];
    [postContent setBackgroundColor:[UIColor whiteColor]];
    [postContent setEditable:NO];

    frame.size.height = stringSize.height + 5.0f;


}

-(void) setcontentHeight
{
    if(self)
    {
    CGRect frame = self.contentView.frame;
    frame = self.contentView.frame;
    frame.size.height = postContent.frame.size.height + picView.frame.size.height;
    self.contentView.frame = frame;
    self.cellHeight = self.contentView.frame.size.height + 10;

    NSLog(@"picView height: %f", picView.frame.size.height);
    NSLog(@"textview height: %f", postContent.frame.size.height);
    }
    else
        self.cellHeight = 44.0f;

}

And Controller that calls Custom cells:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (self.cellsArray && self.cellsArray.count) {
        return self.cellsArray.count;
    } else {
        return 0;
    }
}

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

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [self.cellsArray[indexPath.row] getCellHeight];
}


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

#pragma mark -
-(void)createCellArray
{
    static NSString *simpleTableIdentifier = @"PostCell";
    if (!self.cellsArray) {
        self.cellsArray = [[NSMutableArray alloc]init];
    }

    for(NSDictionary *post in posts)
    {
        NSString *postText = [post objectForKey:@"longDescription"];//[posts objectAtIndex:indexPath.row];
        NewsFeedCell *cell= [[NewsFeedCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
        if(cell)
        {
            [cell setPostImage:[post objectForKey:@"artworkUrl100"]];
            [cell setPostContenText:postText];
            [cell setcontentHeight];            
            [cellsArray addObject:cell];
        }

    }
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top