Calculate the width of a grouped tableview cell - uitableview

I'm trying to place two UILabel on the same vertical axis within a UITableViewCell, and have them take up 50% of the cell's width, when the cell is a grouped table cell.
I've tried self.frame.size.width / 2, but the width that self.frame.size.width looks like the width of the entire cell as if it were not a grouped cell. In other words, it seems to be the width of the whole screen, in a full screen table.
I also compared self.frame.size.width to self.contentView.frame.size.width and both appear to be the same, and are not the actual width of the grouped cell, I don't believe.
When I initialize the UILabel with a specified frame, and specify the x-axis to as 0, the label does place itself on the left edge of the grouped cell, which is great! That's what I want.
But, for the 2nd UILabel that I wish to be to the right of the 1st label, on the same horizontal axis, I do not know how to calculate where to start for the label's frame's x-axis.
Likewise, I do not know how to calculate width for either of these labels, because I can't seem to get the width of the grouped cell.
I feel like there must be a way to get the exact width, because for the first label, giving the label's frame an x-axis of 0 puts it right on the edge of the cell, which is exactly as i need. So I need like internally, this code know the width of that cell too, somewhere.

Ok, I figured out a good way to achieve this. If you're subclassing UITableViewCell and want to adjust the cell's sub-views, then you need to perform these calculations and frame adjustments inside of the layoutSubviews: method. Inside of this method, the cell's contentView will be the intended width, even when using a grouped cell. Below is what my layoutSubviews function looks like ...
- (void)layoutSubviews {
[super layoutSubviews];
// half of the cell's content view width
CGFloat halfCellWidth = self.contentView.frame.size.width / 2;
// the dimensions of the message label, primarily for the height
CGSize mesageLabelDimensions = [_message.text sizeWithFont:[UIFont systemFontOfSize:12]
constrainedToSize:CGSizeMake(self.contentView.frame.size.width - (LEFT_RIGHT_MARGIN * 2), 500.0)];
// adjust the frames of the cell labels
_source.frame = CGRectMake(LEFT_RIGHT_MARGIN, TOP_BOTTOM_MARGIN, halfCellWidth - LEFT_RIGHT_MARGIN, LABEL_LINE_HEIGHT);
_timestamp.frame = CGRectMake(halfCellWidth, TOP_BOTTOM_MARGIN, halfCellWidth - LEFT_RIGHT_MARGIN, LABEL_LINE_HEIGHT);
_message.frame = CGRectMake(LEFT_RIGHT_MARGIN, LABEL_LINE_HEIGHT + (TOP_BOTTOM_MARGIN * 2), mesageLabelDimensions.width, mesageLabelDimensions.height);
}

Related

How to get preferred AutoLayout height given a width for a collection view cell with multiline labels?

I'm building a list view with self-sizing in my app using UICollectionView. Getting UICollectionViewFlowLayout to do a vertical list layout is a pain, so I'm writing my own UICollectionViewLayout subclass to do it.
My cells look a lot like regular table view cells - an image view on the left, a couple of labels vertically stacked in the center, and an accessory view on the right. The labels can wrap to a few lines and grow in font size according to the system font size setting, which is why they need to be self-sizing. Constraints are sensible - left to image view, image view to label, label to accessory, accessory to right, label to top and bottom.
To size my cells via preferredLayoutAttributesFittingAttributes, I need to get the desired height of the cell given the width of the collection view. I'd expect to use systemLayoutSizeFittingSize:horizontalPriority:verticalPriority: for this, passing a size like {desiredWidth, crazyLargeHeight}, UILayoutPriorityRequired for horizontal priority, and 1 for vertical priority. But the size I get back from systemLayoutFittingSize isn't sensible. The width is some previous width value from the cell (which doesn't necessarily match self.bounds.size.width or the passed-in layoutAttributes' size.width), and a height that works with that width. I also have tried passing a small height instead of a large one, and it still doesn't return the size it actually needs.
So how do I get the preferred height for the cell given a width value?
Sample code:
-(UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
UICollectionViewLayoutAttributes *result = [super preferredLayoutAttributesFittingAttributes:layoutAttributes];
CGSize exampleSize = CGSizeMake(layoutAttributes.size.width, 20);
CGSize size = [self systemLayoutSizeFittingSize:exampleSize withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:1];
result.size = size;
return result;
}
Turns out if you ask the cell's content view for a size instead of the cell itself, it works. And you have to give a sample height that is smaller than will be needed.
-(UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
UICollectionViewLayoutAttributes *result = [super preferredLayoutAttributesFittingAttributes:layoutAttributes];
CGSize exampleSize = CGSizeMake(layoutAttributes.size.width, 20); //magic number for example purposes...my cell will definitely be taller
CGSize size = [self.contentView systemLayoutSizeFittingSize:exampleSize withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:1];
result.size = size;
return result;
}
I'm guessing that the content view isn't pinned to the cell's bounds the way I expected. And this exact implementation will likely fall down if the content view is inset for some reason. But the key to solving this was the fact that having the cell contents pinned to the cell's content view does not mean that the cell itself will compute a size as you expect.

Resizing UIView causes child table UITableView to disappear

I have a UIView which contains a label, a button, and a UITableView which populates its data dynamically from a server. I am having trouble resizing the parent UIView to fit its content after the content has dynamically populated. For the purpose of demonstrating my issue, I have made the background of the containing UIView blue.
After populating the TableView with data, the UIView's height does not adjust causing the Tableview data to overflow, seen in the diagram below.
I have set the bottom, leading and trailing space constraints of the TableView to the superview, and top space constraint to the button. The UIView itself has no height constraints set.
I implemented a function to manually recalculate the height of the UIView after populating the content of the TableView. Code for the function below:
func resizeToFitSubviews()
{
var w: CGFloat = self.frame.size.width,
h: CGFloat = 0
for view in subviews {
if view.frame.origin.y + view.frame.height > h { h = view.frame.origin.y + view.frame.height }
}
self.frame.size = CGSize(width: w, height: h)
}
This function works. The UIView resizes to what seems to be the right size, but the TableView disappears after doing so:
Completely lost as to why this occurs. The label and button seem unaffected. I either need to make it so autolayout automatically adjusts the height of the UIView, or make it so that resizing the UIView does not cause the TableView to disappear.
In the View Debugger, the TableView is returning a height of 0 (while the rows are returning 130 as expected given that is what I return in my heightForRowAt function).
Thanks
Don't adjust the view frame if you are using Auto Layout.
Make a height constraint and adjust that to your calculated value.
-
Also it might be easier to manually calculate this height.
height = numberOfRows * heightPerRow

How to break line of a UILabel dynamically with text?

I have a UILabel and I want to show some text in this label. I want to increase the label width at most 70% of the full screen of my device. If text length of that label doesn't fit this 70% of size then the label automatically goes to the next line as long as the text length. Every time the label length cross the 70% width of main screen then lines break as well. I have tried several ways but unable to solve yet. Please help me to solve this.
Thanks in advance;
Drag a label to your storyboard and add top and leading constraints to it.
Now select the label and control drag to the view holding the label (in your case view of ViewController) you will see the pop up and then select equal width
Now your Label's width is equal to your view's width :) That's not you want you want ur label width to be 70% of your view. So select the equal constraint of label, go to property inspector and change the multiplier to 0.7
Now your label width is 70% of your view!
But you don't want it to be 70% always. It can be at max 70% of screen, so
now change the relationship of constraint from being equal to less than or equal to.
select label and change number of lines to 0.
That's it :) have fun :)
Sample O/P:
When text is short - vs - long:
- - -
EDIT:
Not using a storyboard? Not a problem; write the same constraint programmatically and apply it to label simple enough. If you need help lemme know :)
EDIT:
As you have specified that you want to leave the gap at the beginning of each line in label you can achieve it by using Edge insets
- (void)drawTextInRect:(CGRect)rect {
UIEdgeInsets insets = {0, 5, 0, 0};
[super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
}
You must have forgotten to increase the label's height.
The code below is for allowing the UILabel to have multiple lines:
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakByWordWrapping;
Then you have to make sure the UILabel's frame has enough height to show the lines. You can achieve this by calculating the required height for the given text (NSString):
NSString *text = #"YourText";
CGFloat your70Width; // whatever your width is
CGSize constraintSize = CGSizeMake(your70Width, MAXFLOAT);
UIFont *yourLabelFont; // whatever your font is
CGRect requiredFrame = [text boundingRectWithSize:constraintSize options:NSStringDrawingUsesFontLeading attributes:#{NSFontAttributeName:yourLabelFont} context:nil];
// Keeps the old x,y coordinates and replaces only the width and height.
CGRect oldLabelFrame = label.frame;
label.frame = CGRectMake(oldLabelFrame.origin.x, oldLabelFrame.origin.y, requiredFrame.size.width, requiredFrame.size.height);
Now the label will be shown nicely in multiple lines.
To increase the height of the label according to the content if you are using storyboard. Give the label FOUR constraints (Top, Bottom, Leading, Trailing) then go to Attribute Inspector and make lines to 0 and in line break do it WORD WRAP.

heightForRowAtIndexPath with custom cell and label [duplicate]

This question already has answers here:
Calling heightForRowAtIndexPath from within cellForRowAtIndexPath?
(3 answers)
Closed 10 years ago.
If I'm making a custom UITableViewCell and I need it to have a variable height depending on the length of the input (for instance, for a Twitter reader or something), how can I make this work?
I've found lots of other examples here that can set it for a the standard, non-custom cell, but my cell's main text label is smaller, etc., so when I try to use any of those methods on my cell, they give a variety of weird results (text overlapping the bottom of the cell, etc.)
Is there a standardized way of designing the cell (for example, how tall should I make it in Interface Builder?), and let's say my label was half the width of that cell.. how would I go about calculating the height the cell would need to be to display the string loaded into that label? Here's a method that I found here which works fine on the normal cell, but screws up custom ones with weird heights, overlapping text, etc: (I have absolutely NO idea what the 300 / 200000 do here, if anyone could explain that I'd be grateful, too!)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGSize textSize = {300.f, 200000.0f};
CGSize size = [[_quoteStringsFromPlist objectAtIndex: indexPath.row] sizeWithFont: [UIFont systemFontOfSize:12.0f] constrainedToSize: textSize lineBreakMode: NSLineBreakByWordWrapping];
size.height += 5.0f;
float result = MAX(size.height, 32.0f);
return result;
}
Something like this should work.
CGRect labelFrame = // enter default frame of the label here
UIFont *labelFont = // enter label font here
CGFloat labelBottomMargin = // enter the space between the bottom of the label and the bottom of the cell here
CGSize size = [labelString sizeWithFont:labelFont constrainedToSize:CGSizeMake(labelFrame.size.width, MAX_FLOAT)];
size.height += labeFrame.origin.y;
size.height += labelBottomMargin;
size.height = MAX(size.height, 32.0f);
return size.height;
First, head over to your xib file and see how large are the top/bottom margins of your text area, and how wide it is.
After that, you need to calculate the height needed for it with the width you have available, and then add that value to the top/bottom margins your text area already has.
The result should look correctly regardless of the size of each cell in IB or the text you are trying to put in them.
EDIT
Imagine your cell size is {700, 300} in your IB, and your text area is located on {{100, 100}, {300,100}}, your text area has a 100px margin top and 100px margin bot, and it's width is 300.
When you calculate the height you require for your text, you calculate it with an available 300 width. It will return a size, something like {300, 250}. That 250 is the height required by your text, but your cell has other stuff in it and you need to add those top and bot margins to that, so the result is 450.
Remember to set an autoresizing mask or autolayout so that your text area stretches vertically and the margins are fixed (UIViewAutoresizingFlexibleHeight)

How to properly add custom TableViewCell?

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.

Resources