I have a custom tableview and under each cell i have image-view. I am receiving images of different sizes from the service and i want to adjust my frame according to that.
Whenever there is a picture smaller than the frame size i want to adjust the height according to its height.
So i am comparing the current frame size and image size and trying to update the imageview frame,but its not working.
cell.image.contentMode = UIViewContentModeScaleAspectFit;
if(cell.image.image.size.height<cell.image.frame.size.height)
{
cell.image.frame = CGRectMake(cell.image.frame.origin.x, cell.image.frame.origin.y,cell.image.frame.size.width, cell.image.image.size.height);
}
what needs to be done to overcome the problem??
You would require to change cell height according to image in UITableView datasource like this
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (image.size.width > CGRectGetWidth(self.view.bounds)) {
CGFloat ratio = image.size.height / image.size.width;
return CGRectGetWidth(self.view.bounds) * ratio;
} else {
return image.size.height;
}
}
EDIT
Incase you are looking out for resizing image and not tableview cell then these are Resize UIImage with aspect ratio? and Resize UIImage with aspect ratio? very good references. Also you should understand UIImageView Scaling Explained Visually
If the code above is in cellForRowAtIndexPath and the cell that you have initialized inside cellForRowAtIndexPath is an instance of ItemCell (for instance), then in your ItemCell class, you will have to override layoutSubviews and check the size of the image subview inside layoutSubviews and adjust the height of the frame of the cell accordingly inside layoutSubviews. This ensures that every time for cellForRowAtIndexPath is called, the layout of the UITableViewCell subclass (ItemCell) happens before the cell gets rendered by cellForRowAtIndexPath.
Btw, the fact that you have set the contentMode of the cell to ScaleAspectFit means that regardless of what you do to the frame of the imageView inside the cell, the image will always be scaled to fit the entire view available to it, i.e. in this case, the dimensions of the imageView inside the cell. Maybe you should try moving the contentMode line
cell.image.contentMode = UIViewContentModeScaleAspectFit;
after the conditional where you set the frame size of the imageView :
if(cell.image.image.size.height<cell.image.frame.size.height)
{
cell.image.frame = CGRectMake(cell.image.frame.origin.x, cell.image.frame.origin.y,cell.image.frame.size.width, cell.image.image.size.height);
}
cell.image.contentMode = UIViewContentModeScaleAspectFit;
Ofcourse, this would probably not solve the problem if you aren't overriding layoutSubview to adjust the dimensions of the imageView frame there.
Related
I want to display image in cell, and because I need to download it first so I would like to display (for question simplicity) black view with the same width and height like image should be displayed.
Because I want to stretch image to same width as cell width, I only need aspect ratio for setting height and this is provided in my code when cellForRowAt is called.
I decided to achieve "black view" before downloading image I only need one UIImageView with black background, and resizing it when cellForRowAt is called. But here is the problem, because using code from similiar questions is not working for me.
I tried something like this in cellForRowAt method:
var frame cell.imageView.frame
frame.size.height = CGFloat(aspectRatio) * frame.size.width
cell.imageView.frame = frame
EDIT
As a result I want something like facebook, where we have photo with the same width as cell and height accordingly to aspect ratio. For simplicity cell can only have UIImageView.
Since you're using auto layout, in the storyboard you should set two things:
First, set the width of the UIImageView to be the full width of the cell
Second, set the aspect ratio of the UIImageView to 1:1 - this says the height is always the same as the width.
If you're using the UITableView's Automatic Dimensions for cell heights, this should be all you need; no code anywhere, everything else is as normal.
If you're not using the automatic cell height feature, then either set the cell height once with self.tableView.rowHeight = self.tableView.bounds.size.width if they're all the same, or, if the cells can be different heights:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if (<indexPath IS IMAGE ROW>)
return tableView.bounds.size.width
return <default height>
}
Where I've written <indexPath IS IMAGE ROW>, you should replace that with whatever condition you need to detect if this particular cell is an image type.
Also, as Rikh pointed out, where I've written <default height>, you should replace that with the height of the cells that are not the image type.
Of course, you may have a much more complex design for your table; this just covers the case you were asking about.
here my solution
imageView.contentMode = UIViewContentModeCenter;
if (imageView.bounds.size.width > ((UIImage*)imagesArray[i]).size.width && imageView.bounds.size.height > ((UIImage*)imagesArray[i]).size.height) {
imageView.contentMode = UIViewContentMode.ScaleAspectFit;
}
I'm trying to achieve something similar to the 9gag app where the cells automatically resize based in the size of the image. The width hugs the sides but the height is resized as needed.
I have a Label that consists of the title above it as well as a UIImageView and below the image I have share icons etc. I've set the constraints for the image to be the UIImageView and to the lower share icons as well as to both leading and trailing spaces. It seems like I've tried every combination of switching "Mode" of the view (i.e. aspect fill, aspect fit) when I do aspect fit it sizes the images correctly but adds a bunch of padding to both the lower and upper parts of the image (screenshot included below)
The label and icons all have the appropriate constraints set (as far as I can tell)
I've searched for hours trying to find someone with a similar issue but to no avail I can't seem to find anything in regards to self sizing cells that use images that may vary in size.
Can anyone point me in the right direction or know of the solution to my problem? All help is very much appreciated as always!
As fas as I've seen, you can't really do image resizing from Interface Builder. You would have to manually write the code in your ViewController.m file. Have something like this:
- (void) adjustImageSize{
CGSize imageSize = self.imageView.image.size;
CGSize imageViewSize = self.imageView.frame.size;
float imageAspectRatio = imageSize.width/imageSize.height;
CGRect frame = self.statusImage.frame;
frame.size.height =imageViewSize.width/imageAspectRatio;
self.imageView.frame = frame;
// set your Cell's frame here, based on your other constraints (labels/buttons etc), after you have resized your image
}
You can call this from your tableView datasource function. And then add this delegate function to tell the tableView the dynamic height of each cell.
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
return cell.frame.size.height;
}
This is how I did dynamic resizing of images for a NewsFeed-based app. Hope this helps.
This occurs due to the UIImageView intrinsic content size (image size).
While your image view's width gets resized by leading and trailing space constraints, nothing limits its height so it remains equal to the original image height.
The solution is to calculate an aspect ratio of the image and set it as a ratio constraint to the UIImageView width
#interface PostImageViewCell()
#property (nonatomic, strong) NSLayoutConstraint *aspectConstraint;
#end
#implementation PostImageViewCell
- (void) setAspectConstraint:(NSLayoutConstraint *)aspectConstraint {
if (_aspectConstraint != nil) {
[self.postImage removeConstraint:_aspectConstraint];
_aspectConstraint = nil;
}
if (aspectContraint != nil) {
[self.postImage addConstraint:aspectConstraint];
_aspectConstraint = aspectConstraint
}
}
- (void)setPicture:(UIImage *)image {
CGFloat aspect = image.size.width / image.size.height;
self.aspectConstraint = [NSLayoutConstraint constraintWithItem:self.postImage
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.postImage
attribute:NSLayoutAttributeHeight
multiplier:aspect
constant:0.0];
[self.postImage setImage:image];
}
#end
I have the same problem.
I noticed that the cell will calculate its size based on the image in your imageView and that it ignores that the UIImageView will change the image's size to make it fit its bounds.
Example: Your image has the size 1000x1000. If you set your content mode to 'Aspect Fit' the UIImageView will display the image with the width of itself. So the displayed image might have the width of your iPhone - e.g. 750px and the height 750px (it's a square).
Now the cell will resize itself as if the imageView would display the image in its original format; 1000x1000. Which leads to unwanted white space.
My workaround right now is to size down the image manually but this is very costly and it also creates some other problems with my UI.
Did you find a better solution?
This is really a n00b question, I am learning iOS while trying to build an app
I need to show an image, label on UITableViewCell. The following code does that for me
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.imageView.image = [self getImageNameForRow:indexPath.row];
cell.textLabel.text = self.features[(NSUInteger) indexPath.row];
cell.imageView.contentMode = UIViewContentModeScaleAspectFit;
return cell;
}
The problem is that image size comes bigger that I expect. so I tried to increase the height of row as
self.tableView.rowHeight = 80;
But then the image also scales up.
How can I keep my image size fixed while increasing (or changing) the size of the row?
The problem is that you are using a default table view cell style. That style comes with a built-in textLabel and an imageView, and the latter is a UIImageView with constraints so that it is resized to fill the height of the cell. But you have also said
cell.imageView.contentMode = UIViewContentModeScaleAspectFit
Which means that as the image view grows, the image grows with it - exactly what you are seeing.
The solution, as I explain here, is to size the image down to the actual size that you want it - and set the image view's contentMode to center. Something like this:
UIImage* im = [self getImageNameForRow:indexPath.row];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(36,36), YES, 0);
[im drawInRect:CGRectMake(0,0,36,36)];
UIImage* im2 = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
cell.imageView.image = im2;
cell.imageView.contentMode = UIViewContentModeCenter;
Change that 36,36 to the size you actually want.
This is good practice anyway. It is a terrible waste of memory to hold onto an image at a larger size than needed for actual display (the amount of wasted memory grows exponentially, because area is on the order of the square of a single dimension). So you should always size images down to their actual display size. There's lots and lots of code on Stack Overflow showing many other ways to do that.
I believe your main problem here is the image is too large. If the image were only 40x40, it would appear as half the tableViewCell's height (when it's 80). IIRC the UIImageView in that UITableViewCell stretches to the height of the cell, and images will always fill it if they're large enough.
Three things you could do:
1) Shrink the size of the image to the size you want.
2) Change the frame of the imageView manually like so:
cell.imageView.image = [self getImageNameForRow:indexPath.row];
CGPoint center = cell.imageView.center;
CGRect frame = cell.imageView.frame;
frame.size.width = 40;
frame.size.height = 40;
cell.imageView.frame = frame;
cell.imageView.center = center;
I'm not entirely certain if you need to cache the center and re-set it after the frame change or not (the UITableViewCell might do this automatically).
3) Make a custom UITableViewCell subclass that has a fixed size UIImageView. I've detailed how to do this on my blog here.
I recommend 1 or 3.
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 learn a common and right way of calculation of height for custom cells.
My cells are loaded from nib, they have two multiline UILabels one above other.
At the moment I create special configuration cell in viewDidLoad and use it in heightForRowAtIndexPath.
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
[self configureCell:self.configurationCell forIndexPath:indexPath];
CGRect configFrame = self.configurationCell.frame;
configFrame.size.width = self.view.frame.size.width;
self.configurationCell.frame = configFrame;
[self.configurationCell layoutSubviews];
UILabel *label = (UILabel *)[self.configurationCell viewWithTag:2];
float height = label.frame.origin.y + label.frame.size.height + 10;
return height;
}
It works but seems to be a bad manner. I think that heights must be precalculated for each item and for each orientation. But I can't find a way to make it nice and straightforward.
Could you help me?
Cell Label's style (like font and offsets from the screen borders) must be loaded from nib file (cell is inside nib).
Added cell's layourSubviews method:
-(void) layoutSubviews {
[super layoutSubviews];
[self.shortDescriptionLabel resize];
CGRect longDescriptionFrame = self.longDescriptionLabel.frame;
longDescriptionFrame.origin.y = self.shortDescriptionLabel.frame.origin.y + self.shortDescriptionLabel.frame.size.height + 5;
self.longDescriptionLabel.frame = longDescriptionFrame;
[self.longDescriptionLabel resize];
}
resize method of label simply increases it's height to fit all the text. So height of cell is calculated as longDescriptionLabel.bottom + 10. Simple code but not very beautiful.
It appears that you are trying to create subviews inside heightForRowAtIndexPath. View creation is supposed to be done in cellForRowAtIndexPath.
According to your implementation, you can only determine the height of the cell after it's been laid out. This is no good because UITableView calls heightForRowAtIndexPath for every cell, not just the visible ones upon data reload. As a result, subviews of all cells are created even if they aren't required to be visible.
To optimize this implementation, you have to work out some kind of formula to allow determination of height without laying out views. Even if your layout is elastic or has variable height, given text rectangle, you can still determine its height. Use [NSString sizeWithFont:] to determine its displayed rectangle. Record this information in your data delegate. When heightForRowAtIndexPath is called, return it directly.