I have a class PostListTableCell, inheriting UITableViewCell, which is used as my custom cell in a UITableView.
I need to resize some labels in the cell, so I called following method in
- (void)layoutSubviews {
[super layoutSubviews];
[_titleLabel sizeToFit];
}
But the problem is, when the UITableView loaded, the _titleLabel does not resize:
But after I click this cell and select it, the title did resize:
Following is the code to load data:
- (PostListTableCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *PostListTableCellIdentifier = #"PostListTableCell";
PostListTableCell *cell = (PostListTableCell*) [tableView dequeueReusableCellWithIdentifier:PostListTableCellIdentifier];
if (cell == nil) {
cell = [[PostListTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:PostListTableCellIdentifier];
}
Post *post = (Post*)_posts[indexPath.row];
[cell loadPost:post];
return cell;
}
Could anyone help?
The problem is the autolayout that resize again the label for you with the constraints that you had set when you make the label in Interface Build, unfortunately in under autolayout the the hierarchy is Constraints > Frame
First of all, if you use Autolayout in (viewDidLoad) method which is related to UIViewController and its subclasses and (awakeFromNib) which is related to UIView and UITableViewCell etc all Views Frames' have not been rendered yet while those methods called.
So if you want to resize any of outlets you should call it in (ViewDidAppear) for UIViewController. In UIViews and cells you should use:
- (void)layoutSubViews
{
[super layoutSubviews];
}
And don't forget to set interval to your Method of resizing as in this answer.
set frame for your label
-(void)layoutSubviews
{
CGRect frame = CGRectMake(20, 30, 200, 50);
_titleLabel.frame = frame;
}
Related
Hi I'm able to achieve auto-resizing table view cells (based on amount of text) by pinning textLabel's top, bottom, left and right to content view's top, bottom, left and right. I was able to get different cell height for cell with different text length. But I want to achieve same auto-resizing result with button added as subview to cell. I have added exact same contrains to button as I have it with textLabel. Any help is greatly appreciate .
Summary : Here is an hand-made example about this problem.
First, read the answer about autoresizing tableviewcell with UILabel because you will do like that with a little changes.
The difference between UILabel and UIButton is UILabel has Intrinsic Content Size based on Width and Height. So you need to subclass UIButton and override method :
-(CGSize)intrinsicContentSize {
return CGSizeMake(self.frame.size.width, self.titleLabel.frame.size.height);
}
In your CustomCell class, override method
- (void)layoutSubviews{
[super layoutSubviews]; //btnTest is your Button
[self.contentView setNeedsLayout];
self.btnTest.titleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.btnTest.titleLabel.frame);
}
In your ViewController :
- (void)configureCell:(UITableViewCell *)cell forRowAtIndexPath: (NSIndexPath *)indexPath{
if ([cell isKindOfClass:[TestCell class]]) {
TestCell *testCell = (TestCell *)cell;
[testCell.btnTest setTitle:#"An example autoresizing UITableViewCell height based on UIButton added as subview by Matie.An example autoresizing UITableViewCell height based on UIButton added as subview by Matie.An example autoresizing UITableViewCell height based on UIButton added as subview by Matie." forState:UIControlStateNormal];
#warning You need to have this below line, to calculate height when the first time viewDidLoad.
testCell.btnTest.titleLabel.text = #"An example autoresizing UITableViewCell height based on UIButton added as subview by Matie.An example autoresizing UITableViewCell height based on UIButton added as subview by Matie.An example autoresizing UITableViewCell height based on UIButton added as subview by Matie.";
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
[self configureCell:self.prototypeCell forRowAtIndexPath:indexPath];
//
self.prototypeCell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.tableView.bounds), CGRectGetHeight(self.prototypeCell.bounds));
[self.prototypeCell setNeedsLayout];
[self.prototypeCell layoutIfNeeded];
CGSize size = [self.prototypeCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height +1;
}
I have a UITableView with multiple UILabels. The issue is that the text in these cells change dynamically as I receive data from the server. It works fine when I load the view controller. But as I scroll, the height of the cells are not updated as heightForRowAtIndexPath is only called once.
Here are the screenshots:
As I've shown in the screenshot, the question label reduces in size which leads to a gap (shown by arrow).
Here's my cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIndentifier = #"CustomCell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIndentifier];
if (cell == nil)
{
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIndentifier];
}
cell.question.autoDetectLinks = YES;
// Used to populate cell from NSDictionary
[self setDataToCell:cell AtIndexPath:indexPath];
return cell;
}
Here's my custom cell's layoutSubviews:
- (void) layoutSubviews
{
CGRect frame = self.question.frame;
frame.size.width = 277.0f; //you need to adjust this value
self.question.frame = frame;
self.question.numberOfLines = 2;
[self.question sizeToFit];
// Place time below question
CGRect timeFrame = self.time.frame;
timeFrame.origin.y = self.question.frame.origin.y + self.question.frame.size.height + 5;
self.time.frame = timeFrame;
[self.time sizeToFit];
}
So to tackle this situation I called
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[_tableIndexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
in - (void) scrollViewDidScroll:(UIScrollView *)scrollView
This solves my problem but reduces the performance and the elements jump around before settling even after setting the animation as UITableViewRowAnimationNone. Is there a better way of doing it? Should I call reloadRowsAtIndexPaths somewhere else?
Thanks.
Ok here come the edited answer:
The problem is your sizeToFit in your layoutSubviews. Just follow this link to resolve your issue:
here
If you also want the cell and the label to dynamically resize to their corresponding text but at the same time your timelabel to be directly underneath it you will have to determine the size of your uilabel based on the text, font and font-size.
See here for more information:
here
I created several cells with Interface Builder, and I'm using them to fill a UITableView. In other words, I have 3 classes for 3 different kinds of cell, and an other view which contains a UITableView.
- My UITableView containing different kinds of cells :
Here's my problem :
On the iPhone emulator, it looks great. But on the iPad emulator, the custom cells width is fixed. The UITableView width fits to the screen width, so it's good, but the UITableViewCells does not fit to the UITableView. I want to force the custom UITableViewCells to take the UITableView width.
Is there anything to do in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPathmethod, where I instanciate my custom cells ?
Or do I have to write a thing like self.fitToParent; in the custom cells header file ?
EDIT (schema) :
EDIT 2 (cellForRowAtIndexPath method) :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifierType1 = #"cellType1";
static NSString *cellIdentifierType2 = #"cellType2";
NSString *currentObjectId = [[myTab objectAtIndex:indexPath.row] type];
// Cell type 1
if ([currentObjectId isEqualToString:type1])
{
CelluleType1 *celluleType1 = (CelluleType1 *)[tableView dequeueReusableCellWithIdentifier:cellIdentifierType1];
if(celluleType1 == nil)
celluleType1 = [[CelluleType1 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifierType1];
celluleType1.lblAuteur.text = #"Type1";
return celluleType1;
}
// Cell type 2
else if ([currentObjectId isEqualToString:type2])
{
CelluleType2 *celluleType2 = (CelluleType2 *)[tableViewdequeueReusableCellWithIdentifier:cellIdentifierType2];
if(celluleType2 == nil)
celluleType2 = [[CelluleType2 alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifierType2];
celluleType2.lblAuteur.text = #"Type2";
return celluleType2;
}
else
return nil;
}
}
I think uitableviewcell's width is the same as the tableview's width.You can try to set cell's background color to test it. cell.backgroundColor = [UIColor redColor] ;
You should create a class which inherit from UITableViewCell and override it's method - (void)layoutSubviews , adjust your content's frame there.
I resolved my problem using the following code in each custom cell class. It's not very clean, but I can't spend one more day on this issue...
- (void)layoutSubviews
{
CGRect contentViewFrame = self.contentView.frame;
contentViewFrame.size.width = myTableView.bounds.size.width;
self.contentView.frame = contentViewFrame;
}
Thank you for your help KudoCC.
- (void)awakeFromNib {
[super awakeFromNib];
// anything you write in this section is taken with respect to default frame of width 320.
}
awakeFromNib is called when [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; is processed- anything you write in section is taken with respect to default frame of width 320.
You need to make another custom function and call it after cell gets initialized.
For eg:-
#implementation CheckinTableViewCell{
UILabel *NameLabel;
UILabel *rollLabel;
}
- (void)awakeFromNib {
[super awakeFromNib];
NameLabel = [[UILabel alloc] initWithFrame:CGRectZero];
rollLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.contentView addSubview:NameLabel];
[self.contentView addSubview:rollLabel];
}
-(void) bindView{
NameLabel.frame = CGRectMake(10, 10, self.contentView.frame.size.width-20, 20);
rollLabel.frame = CGRectMake(10, 30, NameLabel.frame.size.width, 20);
}
and call this function in tableview cellForRowAtIndex:-
-(UITableViewCell*) tableView: (UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
CheckinTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if(cell ==nil){
cell = [[CheckinTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.name = #"Harry";
cell.rollno = #"123456";
[cell bindView];
return cell;
}
I am using the following code to instance a UIView from a XIB and then add it to the selected UITableViewCell for displaying. When I run the code and touch the cell, I can walk through the code and see that everything is instanced correctly (nothing is nil) and yet the view is never displayed.
I have a UIView with a couple of buttons on it. In Interface Builder I set the UIView to use a sub-class of UIView which at the moment does not have any code in it, other than the boiler plate generated by Xcode. I'm hoping someone can point out any obvious errors I've made in using this code to get this to work.
Please note that at one point I had the UIView showing within the UITalbeViewCell, but I had messed some stuff up during some refactoring and ended up re-writing the code to handle this. When that happened, I could no longer display the UIView within the cell.
#implementation HZRunwayViewController
{
EKEvent *currentEvent;
BOOL editingEvent;
HZEventDrawerView *eventDrawer;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Check if we are touching an event
if (indexPath.section == 0 && [self.eventsForCurrentDay count]) {
// Grab the selected cell and make sure it's an event cell
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
if ([cell isKindOfClass:[HZEventTableViewCell class]]) {
// Setup our event cell and our action drawer for use.
HZEventTableViewCell *eventCell = (HZEventTableViewCell *)cell;
if (!eventDrawer) {
eventDrawer = (HZEventDrawerView *)[[NSBundle mainBundle] loadNibNamed:#"HZEventDrawerView" owner:self options:nil][0];
}
eventDrawer.bounds = eventCell.bounds;
NSLog(#"X:%f Y:%f Width: %f Height: %f", eventDrawer.bounds.origin.x, eventDrawer.bounds.origin.y, eventDrawer.bounds.size.width, eventDrawer.bounds.size.height);
[eventCell.contentView addSubview:eventDrawer];
[eventCell bringSubviewToFront:eventDrawer];
eventDrawer.hidden = NO;
}
}
}
Update
In order to test and see if it was my XIB file or sub-class causing the issue, I stopped using it, instanced a UIView, added a UIButton to it and then added it to the Cell.contentView subview. When I touch the cell, nothing happens.
// Grab the selected cell and make sure it's an event cell
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
if ([cell isKindOfClass:[HZEventTableViewCell class]]) {
// Setup our event cell and our action drawer for use.
HZEventTableViewCell *eventCell = (HZEventTableViewCell *)cell;
if (!eventDrawer) {
eventDrawer = [[UIView alloc] initWithFrame:eventCell.bounds];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(5, 5, 25, 44)];
[eventDrawer addSubview:button];
}
eventDrawer.frame = eventCell.bounds;
NSLog(#"X:%f Y:%f Width: %f Height: %f", eventDrawer.bounds.origin.x, eventDrawer.bounds.origin.y, eventDrawer.bounds.size.width, eventDrawer.bounds.size.height);
[eventCell.contentView addSubview:eventDrawer];
[eventCell bringSubviewToFront:eventDrawer];
eventDrawer.hidden = NO;
}
Instead of eventDrawer.bounds = eventCell.bounds; try eventDrawer.frame = eventCell.bounds;
The bounds of a view are viewed from inside, so the origin will be 0, 0. However the frame of a view is relative to it's superview, so the origin can differ from 0, 0.
Try to set in your custom uiview's xib file set class HZEventDrawerView and change line
eventDrawer = (HZEventDrawerView *)[[NSBundle mainBundle] loadNibNamed:#"HZEventDrawerView" owner:self options:nil][0];
On:
eventDrawer = [[HZEventDrawerView alloc] initWithNibNamed:#"HZEventDrawerView" bundle:nil];
The following line is the problem:
// Grab the selected cell and make sure it's an event cell
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
You are retrieving a new cell from the UITableViewDataSource!
Instead, you should get the cell from the UITableView directly:
// Grab the selected cell and make sure it's an event cell
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
Cheers!
I'm attempting to create a custom UITableViewCell with its own XIB, of which contains two important, editable components:
UILabel
UIImage with 'end-cap insets' - this image sits behind the label, and resizes with it
When I create my UITableViewCell I can set its label's text easily, but in the same method I also try to change the UIImage's frame to match that of the UILabel's. However, this is not happening until I 'reload' the cell by scrolling the table view to dequeue it and bring it back into view.
I create the custom table view cell in my view controller's .m:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *UITableViewCellIdentifier = #"msgCell";
RMMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:UITableViewCellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"MessageCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
[cell customInit]; //This sets up the ImageView's image etc
}
[cell setMessageValue:[_messages objectAtIndex:indexPath.row]]; //SEE BELOW
return cell;
}
...And in my custom cell's .m, I deal with setting the text label and resizing the image with this method:
- (void)setMessageValue:(NSString *)message {
_testLabel.text = message;
// Assume the text label's frame has already been set to the message's length
// (will add this in once I figure out how to change the imageView's
// frame correctly)
// Set the image view's frame to the label's
// THIS DOES NOT TAKE EFFECT UNTIL THE TABLE VIEW IS RE-SCROLLED
CGRect frame = _imageView.frame;
frame.size.width = _testLabel.frame.size.width + 8;
_imageView.frame = frame;
}
This is caused because a table view cell is created at one size, and then it is resized when it is displayed after being added to the table. If you log the bounds or frame of your cell, you will see that it is one size (smaller) when you load it from the nib, and another size (bigger) when you "refresh" the cell by scrolling it off of the screen and back on.
The simplest approach would be to make sure that the label and image both have the same auto-sizing rules, so that when the cell is resized, they both grow together.
you can use UITableView delegate method,
– (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
inside this you can set the height of row dynamically like..
// This is only for Example that you can set your cell height
-(CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *text;
text = [_messages objectAtIndex:indexPath.row];
CGSize constraint = CGSizeMake(280, 2000);
CGSize size = [text sizeWithFont:[UIFont boldSystemFontOfSize:12] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
return size.height + 5;
}
You should be able to control the size of the subviews if you programmatically create them and add them to your tableview cell (i.e.,so that they are not set in storyboard, and thus are not subjected to explicit auto-contraints).
In my implementation, I created a customCell subclass. In the .h:
#property (strong, nonatomic) UILabel *customLabel;
Then in the .m:
- (id)initWithCoder:(NSCoder *)aDecoder {
METHOD;
self = [super initWithCoder:aDecoder];
if (self) {
// Initialization code
self.customLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 40)];
[self addSubview:self.customLabel];
}
return self;
}
In your tableViewController's .m file, in the method tableView:cellForRowAtAtIndexPath:
theCell.customLabel.frame = CGRectMake(0, 0, 320, 40);
theCell.customLabel.text = #"Whatever text you want";
This will give you instant and complete control over your custom label right when the tableview and cell loads.
In response to #inafziger, I tried programmatically removing the constraints on the label but it did not resolve this auto-resizing issue.
Update: making a temporary reference to the subview, then removing it from superview, and then adding it back to the subview, this worked just as well, with the slight exception that the in my case where I'm using a label, the height of the label was fit to the height of the text it contained (whereas programmatically creating the label in my example above, the label will have whatever height you assign it.) The advantage being that you don't have to create any custom labels, or even touch the subclass tableviewCell. Just use whatever you created in storyboard. Here's tableView:cellForRowAtIndexPath:
UILabel *theLabel = theCell.theLabel;
[theCell.theLabel removeFromSuperview];
[theCell addSubview:theLabel];
theCell.theLabel.frame = CGRectMake(0, 0, 320, 40);