Access previous cell from layoutSubviews - ios

I've created a custom UITableViewCell class. In that class I adjust the height of the cell depending on the image inside it.
- (void)layoutSubviews {
[super layoutSubviews];
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
if (self.imageView.image) {
self.frame = CGRectMake(self.frame.origin.x,self.frame.origin.y,
self.frame.size.width, self.imageView.image.size.height);
}
}
But since they are all different heights some cells overlap each other.
The first image is the same height as the default cell height, therefore the second image is fine. The second image has a larger height which makes the third image to be positioned behind it.
Can I somehow access the previous cell from the layoutSubviews and adjust the y-position from that?

By doing this in layoutSubviews instead of tableView:heightForRowAtIndexPath:, your code depends on a side effect and is trying to defeat UITableView. You should fix this in tableView:heightForRowAtIndexPath: and let the UITableView do the work for you.

Instead of fixing a constant row height in the method
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 65; //For example
}
In the case, for example, that you have an array with the names of the images ordered (imageNamesArray), you can use something like this:
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
CGFloat desiredImageWidth = 50.0f;
UIImage *image = [UIImage imageNamed:[imageNamesArray objectAtIndex:indexPath.row]];
CGFloat ratio = image.size.height/image.size.width;
return desiredImageWidth * ratio;
}

Related

UIImageView auto resize inside a custom UITableViewCell with varying height

I have set up a custom cell prototype inside storyboard not using autolayout and i have run into a rather vexing problem where a UIImageView i have inside the cell seems to obey constraints all of it's own when i want it to reflect the size of the cell with a bit of padding.
Here is the setup in storyboard interface builder and view mode is set to "scale to fill"
And here is what i get back, with no editing of the placement or scaling of the image, it seems very much like it's using aspect fill.
I have tried various options and none seem to work for me so far, The only other way i can think is turn of auto resizing and set the rect myself manually when the cell is created any ideas?
and my code for setting the cell
- (void)setCell:(NSDictionary*)messageData
{
[_nameLabel setText:messageData[#"name"]];
[_messageLabel setText:messageData[#"message"]];
[_nameLabel sizeToFit];
[_messageLabel sizeToFit];
UIImage *image;
if([messageData[#"direction"] boolValue])
{
image= [[UIImage imageNamed:#"bubble_left"]resizableImageWithCapInsets:UIEdgeInsetsMake(18, 6, 6, 10) resizingMode:UIImageResizingModeStretch];
}
else
{
image= [[UIImage imageNamed:#"bubble_right"]resizableImageWithCapInsets:UIEdgeInsetsMake(18, 10, 6, 6) resizingMode:UIImageResizingModeStretch];
}
[_bubbleImage setImage:image];
}
and the protocol methods
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CUIChatCellTableViewCell *cell = [_chatTableView dequeueReusableCellWithIdentifier:#"CustomCell" forIndexPath:indexPath];
NSDictionary *messageData = _testDataArray[indexPath.row];
[cell setCell:messageData];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
_customCell = [_chatTableView dequeueReusableCellWithIdentifier:#"CustomCell"];
//configure cell
NSDictionary *messageData = _testDataArray[indexPath.row];
[_customCell setCell:messageData];
//Get height of the cell
CGFloat height = [_customCell.messageLabel frame].size.height+[_customCell.nameLabel frame].size.height+20;
return height;
}
Set autoresizing properties like this, may you get help from this.
I could not get AutoResizing to work and i tried another test project from scratch and it seemed to work, so it must have been certain settings, either way i ended up having to set the height manually for each cell based on it's content anyway here is the code to do so
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
_customCell = [tableView dequeueReusableCellWithIdentifier:#"CustomCell"];
//configure cell
CMessageInfo *messageData = _testDataArray[indexPath.row];
[_customCell setCell:messageData];
//Get height of the cell
CGFloat height = [_customCell.messageLabel frame].size.height+[_customCell.nameLabel frame].size.height+20;
return height;
}

Fitting UIImage inside a Table Cell

I have a table with a custom cell, which has only one UIImageView. I want each cell to have its own height based on an image height I will place in it. In my heightForRow method I have
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
FAppFormula *formula = self.formulas[indexPath.row];
UIImage *img = [formula getFormulaImageSmallSize];
if (img.size.height <= 100){
return 30;
}
return 75;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *cellIDSmall = #"FormulaCellSmall";
FormulaCell *cell = (FormulaCell *)[tableView dequeueReusableCellWithIdentifier:cellIDSmall
forIndexPath:indexPath];
cell.formulaImage.image = formulaImg;
return cell;
}
The cells themselves do get proper heights, however images don't get scaled appropriately. In storyboard cell prototype I set UIImageView's mode to Aspect Fit, however looks like all images get scaled based on a default(?) row height, but not on the one which heightForRowAtIndexPath returns...
Any ideas on why it happens and how to fix it? How it looks now img

how to set UITextView height dynamically inside a custom UITableViewCell with autolayout

I have UITableView, every tableViewCell is custom. Inside my customTableViewCell is a UITextView, TextViews frame is pin or same as its superView which is tableViewCell.
How Am I gonna set the UITableViewCell height dynamically that is proportional to the TextViews size which will also depends to content text that I get from internet.
(sample prototype)
// this is how I manipulate every cell height
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
// How do I set the height here, whats the best approach for this. with autolayout BTW
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// I initialize every cell here with my ArrayContainer, nothing much to refer here.
}
Follow this answer: Link reference
Add a height constraint to your UITextView and create an outlet to your custom cell class.
Use sizeThatFits: on UITextView to get the size that fits the content and set this value as a constant of the constraint described in 2. Do this in tableView:heightForRowAtIndexPath:.
One caveat is that if there're many cells (hundreds or thousands) then you may hit performance issues.
Primarily get the texts as NSArray and Assign it into the UITextView's as temporary, So that you will get the Height of Cell :
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGRect frame;
// set the height here, According to the NSArray of text
if(textView || section )
UITextView *tempTxtView = [[UITextView alloc] init];
tempTxtView.text = // Add the Text of index according to the Section
// Fit the UITextView to the Content
frame = tempTxtView.frame;
frame.size.height = tempTxtView.contentSize.height;
tempTxtView.frame = frame;
}
return frame.size.height;
}
Once got the UITableViewCell height : Then No need to worry about this, right ?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// I initialize every cell here with my ArrayContainer, nothing much to refer here.
self.textView = [[UItextView alloc] initWithFrame:CGRectMake(cell.frame.origin.x +20, cell.frame.origin.y +10, cell.frame.size.width - 20, cell.frame.size.height - 20)];
// ....... Do further with the NSArray of Text
}
Note : I Haven't Tested, Its just a thought. Hope this will help you
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGSize titlesize = [TxtValue.text sizeWithFont:[UIFont fontWithName:appdel.strFont size:25.0f] constrainedToSize:CGSizeMake(300, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping]; /// ----- -set width as per your requirement inplace of 300
return titlesize.height+10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
}

Resize tableview NSLayoutConstraint

Hi I have a tableView which I change the width programmatically by his constraint:
self.widthTableLeft.constant = self.view.frame.size.width;
I do this in the viewDidLoad.
In the delegate method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
I need the contentview frame of each cell to calculate the hight of the cell, but when get it, this is the old size, I mean the size if I wouldn't have done the resize.
When the table is showed the size of the table is right but the hight of the cell is wrong.
I have tried to call:
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];
The question is, before to show the view of my view controller, when is the best place to update the width constraint of a UITableView and to get the right contentView in the cells in the method heightForRowAtIndexPath.
Sorry but I'm lost, could you give me any ideas?
Thanksssss
UPDATE
This is the code in heightForRowAtIndexPath
static const NSInteger ROC_TITLE_LABEL_TAG = 2;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
ROCAgendaItemDetailEntity *agendaItemDetail = [self.listRight objectAtIndex:indexPath.row];
UITableViewCell *cell = [self.tableLeft dequeueReusableCellWithIdentifier:#"cellVideo"];
//I get the label which will make the cell bigger
UILabel *titleLabel = (UILabel *)[cell viewWithTag:ROC_TITLE_LABEL_TAG];
//We init the height title with the min height.
float extraSpaceHeightLabel = 0;
if (agendaItemDetail.agendaItem.title.length > 0) {
//I will use his width to get the hight that I need to write all text
float width = titleLabel.frame.size.width;
CGSize size = CGSizeMake(width, CGFLOAT_MAX);
//we get the size
CGSize sizeTitleLabel = [agendaItemDetail.agendaItem.title sizeWithFont:[ROCTextsInformation imagoMed:15] constrainedToSize:size lineBreakMode:titleLabel.lineBreakMode];
//we substract the current height of the label to know the extra space to write all text
sizeTitleLabel.height -= titleLabel.frame.size.height;
extraSpaceHeightLabel = sizeTitleLabel.height;
}
//The final hight will be the contenViewHeight pluss the extra space needed.
return cell.contentView.frame.size.height + extraSpaceHeightLabel;
}
and this is the cell in the table view
Thanks again
viewDidLoad is too early to try and access the geometries of your views. Instead, access self.view's width in viewDidLayoutSubviews.
#interface CustomViewController ()
#property (nonatomic) BOOL isFirstTimeViewDidLayoutSubviews; // variable name could be re-factored
#end
#implementation CustomViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.isFirstTimeViewDidLayoutSubviews = YES;
}
- (void)viewDidLayoutSubviews
{
// only after layoutSubviews executes for subviews, do constraints and frames agree (WWDC 2012 video "Best Practices for Mastering Auto Layout")
if (self.isFirstTimeViewDidLayoutSubviews) {
// execute geometry-related code...
self.widthTableLeft.constant = self.view.frame.size.width;
}
self.isFirstTimeViewDidLayoutSubviews = NO;
}

cellForRowAtIndexPath sees cell as different height when its loaded, then reloaded?

Preface: this is quite an abstract question.
I've got some cells in a UITableView that use a different image depending on the height of the cell (in 50px increments, from 50px up to 1000px high). I'm hitting a weird issue where when the app first loads, cellForRowAtIndexPath reports that the first three cells (regardless of whether or not all three are onscree) have a height of 100px, then when I scroll down and back up again, their height reverts to the correct value. There's no onscreen change of height, only the image used changes, and the value of cell.frame.size.height in my NSLog statements.
I've been logging in heightForRowAtIndexPath and cellForRowAtIndexPath, and I can verify that the heights are correct when they leave heightForRowAtIndexPath. But, when entering cellForRowAtIndexPath, those first three heights are all set uniformly to 100. I then scroll down until those three cells are off-screen, then scroll back up, and the .height value has changed to the correct value.
To summarise, my questions are:
Is there any code called between the very end of heightForRowAtIndexPath and the very beginning of cellForRowAtIndexPath that could be changing the cell.frame.size.height value of these first few cells?
Why would this issue only be occurring on the first THREE cells? And, why would they revert when reloaded a moment later? I thought the reuse identifier could be the cause, but they're still assigned that identifier when first loaded, so that shouldn't matter, should it?
EDIT: I think the 100px height is coming from the row height of the prototype cell in my storyboard, as if I change that to 101px, those first three cells are then initially at 101px.
EDIT: Here's the code I'm using to set the image, though this part is working, it's just got the wrong value to work with:
UIImage *cappedBG;
float cellHeight = cell.contentView.frame.size.height;
if (cellHeight <= 51) {
cappedBG = [[UIImage imageNamed: #"bg50.png"] resizableImageWithCapInsets: UIEdgeInsetsMake(25, 25, 25, 25) resizingMode: UIImageResizingModeStretch];
} else if (cellHeight <= 101) {
cappedBG = [[UIImage imageNamed: #"bg100.png"] resizableImageWithCapInsets: UIEdgeInsetsMake(25, 25, 25, 25) resizingMode: UIImageResizingModeStretch];
} else if (cellHeight <= 151) {
cappedBG = [[UIImage imageNamed: #"bg150.png"] resizableImageWithCapInsets: UIEdgeInsetsMake(25, 25, 25, 25) resizingMode: UIImageResizingModeStretch];
} else if (cellHeight <= 201) {
cappedBG = [[UIImage imageNamed: #"bg200.png"] resizableImageWithCapInsets: UIEdgeInsetsMake(25, 25, 25, 25) resizingMode: UIImageResizingModeStretch];
} else {
cappedBG = [[UIImage imageNamed: #"bg250.png"] resizableImageWithCapInsets: UIEdgeInsetsMake(25, 25, 25, 25) resizingMode: UIImageResizingModeStretch];
}
You just can't rely on the frame of the cell in tableView:cellForRowAtIndexPath:. The frame of the cell you configure in that method will be set after the code has returned from this method.
The values you get are not the actual heights of that particular row. You get the height from the cell that has just been dequeued. If there is no cell to dequeue the height from the cell that was just created is used. And 100 is probably the height of the cell in your storyboard/nib.
This height has nothing to do with the height of the cell that will be displayed.
Since you calculate the row height in tableView:heightForRowAtIndexPath: already, use this value.
float cellHeight = [self tableView:tableView heightForRowAtIndexPath:indexPath];
if (cellHeight <= 51) {
cappedBG = [[UIImage imageNamed: #"bg50.png"] resizableImageWithCapInsets: UIEdgeInsetsMake(25, 25, 25, 25) resizingMode: UIImageResizingModeStretch];
...
You shouldn't really be doing layout of a cell in cellForRowAtIndexPath.
The best way to do this is to create a UITableViewCell subclass with all the UI elements you need.
Then in the subclass you can override - (void)layoutSubviews.
This way you keep everything separate rather than putting EVERYTHING in the view controller.
Currently, after looking at your question, I think you are having trouble with different cell heights (when scrolled). I believe you have not given correct values in heightForRowAtIndexPath.
Following is the simple code that I have written. This code has displays 20 rows in the table and alternate row has different size.(I have used different colours to distinguish)
#pragma mark - TableView DataSource Methods
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row %2 == 0) return 50;
else return 100;
}
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 20;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *stringCellIdentifier = #"BookOutVehicleCell";
UITableViewCell *tableViewCell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:stringCellIdentifier];
if (tableViewCell == nil)
tableViewCell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:stringCellIdentifier] autorelease];
if(indexPath.row %2 == 0) tableViewCell.contentView.backgroundColor = [UIColor redColor];
else tableViewCell.contentView.backgroundColor = [UIColor greenColor];
return tableViewCell;
}
#pragma mark - TableView Delegate Methods
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
This is the output.
In this case, even if we scroll the tableview up and down, rows height does not change.
Hope this helps. And sorry If I have misunderstood your question. It would be nice if you provide a code snippet. It will give us better idea about the issue.
EDIT:
As per my understanding of the issue you are getting correct heights for each row/cell, however you have used an imageview in cell which will display different images as per the height of the cell. Check if following works for you:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *stringCellIdentifier = #"BookOutVehicleCell";
UITableViewCell *tableViewCell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:stringCellIdentifier];
if (tableViewCell == nil)
tableViewCell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:stringCellIdentifier] autorelease];
//Check your height here and use appropriate image
if(cell.contentView.frame.size.height <100)
{
image = "xxx.png";
}
else
if(cell.contentView.frame.size.height >=100 && cell.contentView.frame.size.height <200)
{
image = "yyy.png";
}
}
Thanks.

Resources