Fourth UITableViewCell reusing height from first cell - ios

I have a TableView that retrieves images from Parse. The image aspect ratios vary. I have it set up so that the width of the image is changed to match the width of the users device and the height will adjust to match the aspect ratio.
My problem is, whenever I run the application, the first three cells run great, but the fourth cell just takes the height from the first cell, so the image has a letter box effect. Same with the fifth and sixth cell and so on (fifth takes height of 2nd, 6th takes height of 3rd, seventh takes height of 4th which has the height of the 1st, and so on).
What would be the best way to fix this?
If you need to see more code than this, please let me know. I wasn't sure if anything else was relevant.
TableViewController
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! HomeScreenTableViewCell
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
//image retrieved from Parse
imageFiles[indexPath.row].getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!)
{
cell.postedImage.image = downloadedImage
}
}
cell.postId = postId[indexPath.row]
cell.username.text = usernames[indexPath.row]
cell.delegate = self
return cell
}

Are you implementing the heightForRowAtIndexPath method? Since you have variable heights, you have to also implement that method so whenever a new cell is created or reused, the correct height is set.
You can store all heights in an array or just get it from the array images that Parse is returning.
It'll be something like this in Objective C but I'm pretty sure you can do it on Swift too.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return images[indexPath.row].height;
}
Hope this helps.

if you want to maintain the aspect ration of the UIImageView you have to implement the heightForRow function in UITableViewDelegate like this
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
//imageView original width is 320
//imageView original height is 200
let screenWidth = UIScreen.mainScreen().bounds.width
return (screenWidth * 200) / 320
}

Related

Dynamic Table Image View in Swift

I have a very similar question as posted here (Dynamic UIImageView Size Within UITableView) where I'm trying to dynamically retrieve an image from Firebase and make the resulting tableview adjust to the height of the image given a fixed width across the screen based on the aspect ratio. All the articles I read says to make the cell calculation based on cellforRowAt, but my actual image is within the TableViewCell. Can someone please help?
Tableview controller:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FeedTableViewCell", for: indexPath) as! FeedTableViewCell
cell.configureCell(post: postArray[indexPath.row])
return cell
}
TableViewCell:
func configureCell(post: Post){
self.postImage.sd_setImage(with: URL(string: post.postImageURL),
placeholderImage: UIImage(named: "default"))
}
you have cell.setNeedsLayout() inside the completion handler of setting image method, like this:
cell. postImage.kf.setImage(with: URL) { _, _, _, _ in
cell.layoutIfNeeded()
}
First of all, you need to use Autolayout to calculate the proper cell height by creating a proper set of constraints that would determine the cell height based on content. I am going to assume you did that.
Then you have to tell the tableView you want it to use Autolayout to calculate height:
// my best estimation of the height
tableView.estimatedRowHeight = 144
// but the real height is calculated by autolayout
tableView.rowHeight = UITableViewAutomaticDimension
Now this would work if the autolayout could calculate the height correctly in cellForRowAt. But since the image is downloaded asynchronously, the image is set later, when the cell may be already presented. This requires you to provide a way in which a cell can tell the tableView that it has downloaded its content and its layout needs to be recalculated. To do so, use this method in the viewController with the tableView:
func recalculateTableViewLayout() {
self.tableView.beginUpdates()
self.tableView.setNeedsLayout()
self.tableView.endUpdates()
}
You will need to pass the reference to the viewController with the tableView to each cell (I recommend to use delegate pattern for that, here for brevity I will simply sketch it using it directly):
class FeedTableViewCell: UITableViewCell {
weak var feedViewController: FeedViewController?
// etc.
And in the cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FeedTableViewCell", for: indexPath) as! FeedTableViewCell
cell.feedViewController = self
cell.configureCell(post: postArray[indexPath.row])
return cell
}
Then use completion handler of sd_setImage to tell the tableView to recalculate its layout when the image gets downloaded:
func configureCell(post: Post){
self.postImage.sd_setImage(with: URL(string: post.postImageURL), placeholderImage: UIImage(named: "default")) { (image, error, cache, url) in
self.feedViewController?.recalculateTableViewLayout()
}
}

label having sizeToFits() goes out of bounds

I have a tableview in where each cell has a label. The datasource of this label is from the firebase api. Now, initially the label is loaded in a perfect form. As you scroll through and if any label is of a shorter text width, the rest of the cell, alter their labels to this size.
I even tried applying a stackview around it, but i couldn't help much
Below is the code of cellForRow
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let post = posts[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "FeedCell", for: indexPath) as? FeedCell {
cell.caption.text = ""
cell.configureCell(post: post)
cell.caption.sizeToFit()
cell.delegate = self
return cell
} else {
return FeedCell()
}
}
code for function in TableviewCell
func configureCell(post: Posts, img: UIImage? = nil) {
self.posts = post
self.caption.text = posts.caption
}
I am really unable to fix this. Any help is much appreciated.
How about using auto-layout to set a minimum width in percentage of a screen size IOS.
Auto-Layout constraint has that multiplier parameter that lets you use a fractional relationship between a superview and its subview.
While both the child view (Label) and its superview are selected, add "equal width". Then change the "multiplier" of the constraint you just added to the proportion you need. For example, for 30%

Swift - Dynamic UITableViewCell size based on image aspect ratio

I'm trying to create dynamically sized UITableViewCells, changing the height based on the aspect ratio of an image downloaded from a server.
For example, if an image's height is double its width, I want the UITableViewCell's height to be double the screen width so that the image can take up the full width of the screen and maintain the aspect ratio.
What I've tried to do is add constraints to the cell and use UITableViewAutomaticDimension to calculate the height, but the problem I'm facing is that I cannot know the aspect ratio of the image until it is downloaded, and therefore the cells start off small and then once the tableView is refreshed manually the cell appears with the right size.
I don't feel like reloading each individual cell when it's image is downloaded is a great way to do things either.
Is this approach the best way to do it? I can't for the life of me think how else to do this, as I can't know the aspect ratio from within the cell itself when it's being initialized.
For achieve this I use first a dictionary [Int:CGFloat] to keep the calculated heigths of cells then in the heightForRowAtIndexpath method use the values keeped in your dictionary, in your cellForRowAtIndexpath method you should download your image, calculate the aspect ratio, multiply your cell width or your image width by your aspect ratio and put the height calculated in correspondent IndexPath number in your dictionary
In code something like this, this is an example of code using alamofire to load the images
var rowHeights:[Int:CGFloat] = [:] //declaration of Dictionary
//My heightForRowAtIndexPath method
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if let height = self.rowHeights[indexPath.row]{
return height
}else{
return defaultHeight
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "ImageCell") as? ImageCell
{
let urlImage = Foundation.URL(string: "http://imageurl")
cell.articleImage.af_setImage(withURL: urlImage, placeholderImage: self.placeholderImage, filter: nil, imageTransition: .crossDissolve(0.3), completion: { (response) in
if let image = response.result.value{
DispatchQueue.main.async {
let aspectRatio = (image! as UIImage).size.height/(image! as UIImage).size.width
cell.articleImage.image = image
let imageHeight = self.view.frame.width*aspectRatio
tableView.beginUpdates()
self.rowHeights[indexPath.row] = imageHeight
tableView.endUpdates()
}
}
})
}
I hope this helps

How to adjust label height and width in custom tableView Cell

I have a expandable tableView, in which when i expand a section, than there are three cell. On firth Cell there is only name and in second cell. It have a big content. Now I want to auto adjust this label height and width according to content.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tblView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomTableViewCell
let dataArrayTblView = dataArrayForTableView
let titleName = dataArrayTblView.valueForKey("name")
let dueDate = dataArrayTblView.valueForKey("deadlinedate")
let description = dataArrayTblView.valueForKey("description")
cell.titleLabel.text = titleName[indexPath.row] as AnyObject! as! String!
cell.dueDateLabel.text = dueDate[indexPath.row] as? String
cell.descriptionLabel.text = description[indexPath.row] as? String
cell.descriptionLabel.sizeToFit()
cell.textLabel?.backgroundColor = UIColor.clearColor()
cell.selectionStyle = .None
return cell
}
But not getting complete content
Try to set this. It will automatically adjust the height of the row for you. If it is not working, then you have something wrong with your constraints inside your storyboard.
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 40
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
You should use UITableViewAutomaticDimension as row height something like,
// this should be set in viewDidload
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 140
for that you must use autolayout and should set proper constraints to your label in cell.
Constraint should be in linear chain, I mean your label's top and bottom both constraint must be set and your label should resize according to content so your cell will resize accordingly!
You can refer Self-sizing Table View Cells by Raywenderlich !
Put below in viewDidLoad and set autolayout as per below screenshots.
tblview.rowHeight = UITableViewAutomaticDimension
tblview.estimatedRowHeight = 44
Screenshot 1
Screenshot 2
Screenshot 3
Screenshot 4
Use heightForRowAtIndexPath method to adjust height of row. Calculate
size of string with boundingRectWithSize this method. example:
Try This:
if let YOUR_STRING:NSString = str as NSString? {
let sizeOfString = ns_str.boundingRectWithSize(
CGSizeMake(self.titleLabel.frame.size.width, CGFloat.infinity),options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: lbl.font], context: nil).size
}

UITableViewCell ImageView not scaling properly?

I am populating a UITableView with images selected by the user. I'd like to the have thumbnails in the table cells all be the same size without affecting the aspect ratio so as not to stretch/skew the images, which sounds to me like ScaleAspectFill, however none of the UIViewContentMode selections seem to have an effect. There me conflicting methods, notable heightForRowAtIndexPath, but removing this makes my cells too small. The following are my didSelectRowAtIndexPath and heightForRowAtIndexPath methods, along with a screen shot from the simulator of my current code (using simulator stock images). Any help is appreciated.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//sets cell based on meme in array
let cell = tableView.dequeueReusableCellWithIdentifier("memeCell") as! UITableViewCell
let meme = self.memes[indexPath.row]
// Set the name and image
cell.textLabel?.text = meme.topText + " " + meme.bottomText
cell.imageView?.frame = CGRectMake(0, 0, 200, 100)
cell.imageView?.contentMode = UIViewContentMode.ScaleToFill //ScaleAspectFill is best, ScaleToFill used to exagerate that its not working
//cell.imageView?.backgroundColor = UIColor.grayColor() //legacy code, will be removed at commit
cell.imageView?.image = meme.origImage
return cell
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
//cell row won't be large enough unless this method is called
return 75.0
}
You can add a subclass of UITableViewCell, then overrides layoutSubviews method:
override func layoutSubviews() {
super.layoutSubviews()
self.imageView.frame = CGRect(0,0,200,100)
}
Check out https://github.com/mattgemmell/MGImageUtilities
You can use the crop function in UIImage+ProportionalFill, that scales the image proportionally to completely fill the required size, cropping towards its center, to crop all images to the same size before assigning the image.
You can use it like this:
cell.imageView?.image = meme.origImage. imageCroppedToFitSize(CGSize(width: 200, height: 100))

Resources