I am making app in which there is a chat window. In this window there is one image and a label on that image in custom cell.
I have take two custom cells, one for sender and other for receiver. Both cell are same with left and right alignment.
I want that when the length of comment is increased then whole comment shows in multiline within that image(increases the image size also) .
how can I handle this situation?
I am using setVariable method to set content on cell. I am trying comment code for framing like below comment code but it doesn't work
- ( void ) setComment : ( NSString* ) Comment
{
[ txtComment setText : Comment ] ;
/*CGRect frame1 = txtComment.frame;
frame1.size.height = txtComment.contentSize.height;
txtComment.frame=frame1;*/
}
I would suggest you use auto layout to define the custom cell height. It will help you create a dynamic cell height depending on the length of the comment. You can read about using dynamic height using auto layout in this link
To make this done you can do the following:
Subclass UITableViewCell and also create xib file.
Go to the xib file and add UIImageView and UILabel objects to your cell. Also create an outlet for the label.
As Pavan Kotesh mentioned the easiest way is to use auto layout.
Add top space and bottom space constraints to the cell content view for both image view and label. Then set constraints for x position for both subviews and finally set width and height constraints.
Height constraints must be "Greater of equal" type to let you change size of the views.
Having done that in Interface Builder all you need is to add one method to your subclass for setting a message.
- (void) setMessage: (NSString*) message
{
CGFloat oldLabelSize = _label.frame.size.height;
_label.text = message;
[_label sizeToFit];
CGFloat newLabelSize = _label.frame.size.height;
CGRect frame = self.frame;
frame.size.height += newLabelSize - oldLabelSize;
self.frame = frame;
}
After calling this method add the cell as subview to you chat view.
EDIT:
I think your -cellForRowAtIndexPath method implementation is wrong. What does [ChatViewCell send] perform?
I would have done it like this:
First declare two arrays in your table view controller class. The first is for storing the text of messages and the second for storing cell heights. Also you can create some structure to store those values. After user has finished inputing a message you should somehow estimate cell height and put both height and cell's message to the appropriate arrays. After that you should insert new row(section) in your tableview using insertRowsAtIndexPaths:withRowAnimation: method.
Then in your cellForRowAtIndexPath method:
If dequeueReusableCellWithIdentifier returns nil you should just initialise new ChatViewCell and after if (cell == nil) statement set its message from the appropriate array (due to reuse you should set cell's content every time it goes on screen).
In your heightForRowAtIndexPath: method return values from array that stores cell heights.
Related
I have used tableview. On tableviewcell, I used uiview.
But I want to change the x position accroding to need.
I have used this code:-
if ([[[allDetailArr objectAtIndex:indexPath.row]valueForKey:#"sender_name"] isEqual: #"Admin"] ) {
[cell.MnView setFrame:CGRectMake(37,8,275,76)];
}
else
{
[cell.MnView setFrame:CGRectMake(5,8,275,76)];
}
When we run, it will perfect but when we scroll tableview for seeing the next rows the view position in not show perfectly.
Instead of giving hardcoded values of frames programmatically, you can take the IBoutlet of layout constrains of MnView in customCellView class and then adjust that constraints accordingly in your if-else code.
I am creating a chat based app, which uses a UITableView to hold all the messages within a conversation. To create a chat bubble, I have a wrapper view (wrapView) which is constrained to the top, bottom, right, and left of the TableViewCell. As a subview I have a UILabel which holds the message which was sent. I used preferredMaxLayoutWidth for the UILabel to ensure the text within the label won't go beyond the parent View. The number of lines for the UILabel is also set to 0. I set the following parameters within the cellForRowAtIndexPath function:
// Get the cell
let cell = tableView.dequeueReusableCellWithIdentifier("LeftBubbleText") as! LeftTextTableViewCell
// Set the message
cell.messageLabel.text = message
// Set the message label's max width
cell.messageLabel.preferredMaxLayoutWidth = cell.wrapView.frame.width - 16
When the conversation first loads, the text within the UILabel is not wrapped properly. It is truncated at the end of the wrapper view. But when the cell is scrolled off screen and then re-appears, the text is wrapped just fine after the cell is reused.
How can I ensure the text within the UILabel is wrapped properly on the initial load and not just when the cell is reused?
The issue is likely to be that the frame width you are using is wrong. Cells created using deqeueReusableCellWithIdentifier have no size class associated with them because they have no parent view. Hence if you have constraints in your cell, trying to calculate layout sizes manually does not work properly.
You should be using dequeueReusableCellWithIdentifier:forIndexPath to dequeue your cells
I have a UITableViewCell subclass which has an image, title and description.
I am supposed to resize the cell height according to the description content length i.e. if it spans more than 5 lines, I should extend it (+other subviews like image etc) till it lasts.
The next coming cells should begin only after that.
I have my UITableViewCell subclass instantiated from xib which has a fixed row height = 160.
I know this is pretty standard requirement but I am unable to find any guidelines.
I already extended layoutSubViews like this:
- (void) layoutSubviews
{
[self resizeCellImage];
}
- (void) resizeCellImage
{
CGRect descriptionRect = self.cellDescriptionLabel.frame;
CGRect imageRect = self.cellImageView.frame;
float descriptionBottomEdgeY = descriptionRect.origin.y + descriptionRect.size.height;
float imageBottomEdgeY = imageRect.origin.y + imageRect.size.height;
if (imageBottomEdgeY >= descriptionBottomEdgeY)
return;
//push the bottom of image to the bottom of description
imageBottomEdgeY = descriptionBottomEdgeY;
float newImageHeight = imageBottomEdgeY - imageRect.origin.y;
imageRect.size.height = newImageHeight;
self.cellImageView.frame = imageRect;
CGRect cellFrame = self.frame;
cellFrame.size.height = imageRect.size.height + imageRect.origin.y + 5;
CGRect contentFrame = self.contentView.frame;
contentFrame.size.height = cellFrame.size.height - 1;
self.contentView.frame = contentFrame;
self.frame = cellFrame;
}
It pretty much tells that if description is taller than image, we must resize the image as well as cell height to fit the description.
However when I invoke this code by doing this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.cellDescriptionLabel.text = #"Some long string";
[cell.cellDescriptionLabel sizeToFit];
[cell setNeedsLayout];
return cell;
}
It appears that while cell frame is changed due to layoutSubViews call, other cells do not respect it. That is, they appear on the same position had the previous cell would not have resized itself.
Two questions:
How to make it possible what I want?
Am I doing right by calling setNeedsLayout within cellForRowAtIndexPath?
P.S.: I know heightForRowAtIndexPath holds key to changing the cell height, but I feel that the data parsing (not shown here) that I do as part of cellForRowAtIndexPath would be an overkill just to calculate height. I need something that can directly tell the UITableViewCell to resize itself according to content needs.
-tableView:heightForRowAtIndexPath: is by design how variable sized cells are calculated. The actual frame of a cell is of no importance and is changed by the table view to fit its needs.
You are sort of thinking of this backwards. The delegate tells the table view how cells need to be drawn, then the table view forces cells to fit those characteristics. The only thing you need to provide to the cell is the data it needs to hold.
This is because a table view calculates all the heights of all the cells before it has any cells to draw. This is done to allow a table view to size it's scroll view correctly. It allows for properly sized scroll bars and smooth quick-pans through the table view. Cells are only requested when a table view thinks a cell needs to be displayed to the screen.
UPDATE: How Do I Get Cell Heights
I've had to do this a couple of times. I have my view controller keep a cell which is never used in the table view.
#property (nonatomic) MyTableViewCell *standInCell;
I then use this cell as a stand in when I need measurements. I determine the base height of the cell without the variable sized views.
#property (nonatomic) CGFloat standInCellBaseHeight;
Then in -tableView:heightForRowAtIndexPath:, I get the height for all my variable sized views with the actual data for that index path. I add the variable sized heights to my stand in cell base height. I return that new calculated height.
Note, this is all non-autolayout. I'm sure the approach would be similar, but not identical to this, but I have no experience.
-tableView:heightForRowAtIndexPath: is the preferred way to tell tableview the size of its cells. You may either precalculate and cache it in a dictionary and reuse, or alternatively in ios7, you can use -tableView:estimatedHeightForRowAtIndexPath: to estimate the sizes.
Take a look at this thread - https://stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights, the answer points to a very good example project here - https://github.com/caoimghgin/TableViewCellWithAutoLayout.
Sorry, but as far as I know you have to implement tableView:heightForRowAtIndexPath:. Warning, in iOS 6 this gets called on every row in you UITableView right away, I think to draw the scrollbar. iOS7 introduces tableView:estimatedHeightForRowAtIndexPath: which if implemented allows you to just guess at the height before doing all the calculation. This can help out a lot on very large tables.
What I found works well is just have your tableView:heightForRowAtIndexPath: call cellForRowAtIndexPath: to get the cell for that row, and then query that cell for it's height cell.bounds.size.height and return that.
This works pretty well for small tables or in iOS7 with tableView:estimatedHeightForRowAtIndexPath implemented.
I want to use auto-layout for UITableViewCells. These table cells have a dynamic height (depending on text length).
I'm using [UIView -systemLayoutSizeFittingSize:] to calculate the appropriate cell height (to return in [UITableView -heightForRowAtIndexPath:]) but I keep getting the following results:
If I pass UILayoutFittingCompressedSize, I get back a CGSize of (0,0).
If I pass UILayoutFittingExpandedSize, my app crashes with this error:
*** Assertion failure in -[NSISLinearExpression incrementConstant:], /SourceCache/Foundation_Sim/Foundation-1043.1/Layout.subproj/IncrementalSimplex/NSISLinearExpression.m:620
(My guess is that this means some number is infinite.)
My implementation is simple. I calculate the height for each object, and then cache it:
MessageCell *cell = // allocate a new cell...
// set up the cell (assign text, images, etc)
CGSize size = [cell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
self.cellHeight = size.height; // size always equals (0, 0)
I hypothesize that this is a problem with the constraints I set, but:
If I manually set cellHeight to a large value, the cells all look fine except the height is wrong.
Interface Builder gives me no warnings about ambiguous restraints
[cell hasAmbiguousLayout] returns NO.
My cell has, among other things, an image set at 48x48, so a size of (0, 0) shouldn't satisfy all the constraints.
Any ideas?
This is what works for me (note 'contentView')
CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
EDIT: I answered a similar question and provided a complete example, here:
How to resize superview to fit all subviews with autolayout?
It is hard to say something concrete basing on your post because you didn't post constraints that you use.
Apple Documentation:
systemLayoutSizeFittingSize:
Returns the size of the view that satisfies the constraints it holds.
Maybe you created constraints that can be interpreted in the way the size of the cell is equal to (0,0).
There is another way you can check the height of the cell with the text. You can put your text in the UITextView and then:
UITextView textView;
textView.text = #"l";
textView.font = [UIFont fontWithName:FONT_NAME size:FONT_SIZE];
//some code here
//
CGFloat cellHeight = textView.contentSize.height;
It is important to set the text and font (and every other property that can cause the change of the height of the UITextView) of the UITextView before using contentSize property. Also you must first add UITextView to the view.
////// EDIT
The problem with your approach with using constraints can be that you want to measure the cell which ISN'T added to the view so the system don't have all the informations it needs. It doesn't know how much space that will be for the space etc because it doesn't know the surrounding area of the cell
I have a grouped tableView in my iPad-app, and I've been trying to set cell.imageView.center = cell.center to center the image instead of putting it to the leftmost position. This is apparently not possible without a subclass of the UITableviewCell(If someone could explain why, that'd also be appreciated.. For now I just assume they are 'private' variables as a Java-developer would call them).
So, I created a custom tableViewCell, but I only want to use this cell in ONE of the rows in this tableView. So in cellForRowAtIndexPath I basically write
cell = [[UITableViewCell alloc]initWith//blahblah
if(indexPath.row == 0)
cell = [[CustomCell alloc]initWith//blahblah
This is of course not exactly what I'm writing, but that's the idea of it.
Now, when I do this, it works, but the first cell in this GROUPED tableView turns out wider than the rest of them without me doing anything in the custom cell. The customCell class hasn't been altered yet. It still has rounded corners though, so it seems it knows it's a grouped tableView.
Also, I've been struggling with programmatically getting the size of a cell, in cellForRowAtIndexPath, I've tried logging out cell.frame.size.width and cell.contentView.frame.size.width, both of them returning 320, when I know they are a lot wider.. Like, all the rows are about 400 wide, and the first cell is 420 or something. It still writes out 320 for all the cells..
This code will not work for a couple of reasons:
cell.imageView.center = cell.center;
Firstly, the center is relative to its superview. I believe the cells superview is the tableView. The imageView's superview will be the content view of the cell. Therefore the coordinate systems are different so the centens will be offset. E.g. the 3rd cell down will have a center of 0.5 widths + 3.5 heights. You should be able to ge around this issue by doing:
cell.imageView.center = CGPointMake( width / 2 , height / 2 );
The second issue is related to how the table view works. The table view manages its cells view's. The width of a cell is defined by the table view's width and the height is defined by the table view's row height property. This means the cell itself has no control over its size.
You can however size its subviews, but you must do this after the cells size has been set (otherwise you can get strange results). You can do this in layout subviews (of the custom UITableViewCell class). See this answer.
- (void)layoutSubviews {
[super layoutSubviews];
self.imageView.frame = ....
}
When layoutSubviews is called the cells frame has been set, so do your view logging here instead of cellForRowAtIndexpath.
As for the GROUPED style. Im not sure if this is designed to work with custom views. I suspect it sets the size of its cells to its own width minus a 20 pixel margin on each size, then applies a mask to the top and bottom cells in a section to get the rounded effect. If you are using custom view try to stick with a standard table view style.