UIImage overlaps labels if it's set to .scaleAspectFill - ios

My app loads images from a backend and displays them in a UITableViewCell, that contains a UIImageView to display it and some labels and buttons.
I've added the suggested contraints to the UITableViewCell with the 'Reset to suggested contraints' option.
Here's some of the code after retrieving the data:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = PostTableViewCell()
if (self.posts.count == 0) { return cell }
let post = posts[indexPath.row]
// Instancia o reuse identifier
if post["post_image"] != nil {
cell = tableView.dequeueReusableCell(withIdentifier: Storyboard.PostWithImage, for: indexPath) as! PostTableViewCell
} else {
cell = tableView.dequeueReusableCell(withIdentifier: Storyboard.PostWithoutImage, for: indexPath) as! PostTableViewCell
}
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var cell = PostTableViewCell()
cell = tableView.dequeueReusableCell(withIdentifier: Storyboard.PostWithImage) as! PostTableViewCell
return cell.bounds.size.height;
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var cell = PostTableViewCell()
cell = tableView.dequeueReusableCell(withIdentifier: Storyboard.PostWithImage) as! PostTableViewCell
return cell.bounds.size.height;
}
private func configureCell(cell: PostTableViewCell, atIndexPath indexPath: IndexPath) {
cell.queue.cancelAllOperations()
let operation: BlockOperation = BlockOperation()
operation.addExecutionBlock { [weak operation] () -> Void in
DispatchQueue.main.sync(execute: { [weak operation] () -> Void in
if (operation?.isCancelled)! { return }
let post = self.posts[indexPath.row]
cell.accessibilityIdentifier = post.recordID.recordName
guard let postTitle = post["post_title"], let postBody = post["post_body"] else {
return
}
if let asset = post["post_image"] as? CKAsset {
self.imageCache.queryDiskCache(forKey: post.recordID.recordName, done: { (image, cachetype) in
if image != nil {
cell.postImageView.contentMode = .scaleAspectFill
cell.postImageView.autoresizingMask = [.flexibleBottomMargin,
.flexibleHeight,
.flexibleLeftMargin,
.flexibleRightMargin,
.flexibleTopMargin,
.flexibleWidth ];
cell.postImageView.image = image!
} else {
do {
let data = try Data(contentsOf: asset.fileURL)
let image = UIImage(data: data)
cell.postImageView.contentMode = .scaleAspectFill
cell.postImageView.autoresizingMask = [.flexibleBottomMargin,
.flexibleHeight,
.flexibleLeftMargin,
.flexibleRightMargin,
.flexibleTopMargin,
.flexibleWidth ];
cell.postImageView.image = image!
self.imageCache.store(image!, forKey: post.recordID.recordName)
} catch {
print("Error 1001 = \(error.localizedDescription)")
}
}
})
}
cell.titleLabel.text = postTitle as? String
cell.bodyLabel.text = postBody as? String
})
}
cell.queue.addOperation(operation)
}
Here's some prints from the app itself that shows the image overlapping over the labels.
It only overlaps if the image is in portrait mode, if the image was taken in landscape it suits well.
What's the best way to bypass this issue?

You can programmatically tell the image to draw only in the given image area. If your constraints are working properly and it is staying the correct size, the image may just be drawing beyond the View bounds because of the .scaleAscpedtFill setting.
Do this by using .clipToBounds = true.
cell.postImageView.clipToBounds = true
Or, you can set it in interface builder as well, per the image below.
Give that a try and see if that helps?

Related

I am using iOS' prefetching to download images for tableview, but why is the display still jerky?

I have an app which fetches RSS feeds and then uses image URLs from those feeds to download and populate an ImageView. I had a go at implementing iOS prefetching (see code below), but it's still glitchy, like it pauses briefly when scrolling (even when scrolling at a 'reasonable speed). I'm not sure what the best way is to debug this - any advice?
This is the TableViewController's cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "NewsTableViewCell", for: indexPath) as! NewsTableViewCell
if let item = rssItems?[indexPath.item] {
cell.item = item
cell.selectionStyle = .none
cell.descriptionText.text = item.isExpanded ? item.description : ""
cell.descriptionText.isHidden = item.isExpanded ? false : true
cell.delegate = self
if let imageView = cell.viewWithTag(100) as? UIImageView {
if (rssItems![indexPath.row].imageData != nil) {
imageView.contentMode = .scaleAspectFill
imageView.image = UIImage(data: item.getImageData()!)
} else {
imageView.image = nil
if cell.item.imageLink == "" {
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named:item.logoImage!)
} else {
if let imageUrl:URL = URL(string: cell.item.imageLink!) {
cell.item.imageData = try? Data(contentsOf: imageUrl)
imageView.image = UIImage(data: cell.item.imageData!)
}
}
}
}
}
return cell
}
Here is the prefetching extension (I didn't implement the cancel download part):
// MARK: - UITableViewDataSourcePrefetching
extension NewsTableViewController: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
// print("prefetchRowsAt \(indexPaths)")
indexPaths.forEach { self.rssItems![$0.row].setImageData() }
}
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
// print("cancelPrefetchingForRowsAt \(indexPaths)")
// indexPaths.forEach { self.cancelDownloadingImage(forItemAtIndex: $0.row) }
}
}
Then the setImageData function comes from a class which makes up an object representing a feed item along with the URL to the image for the given cell:
func setImageData() {
if self.imageLink != nil {
guard let imageUrl:URL = URL(string: self.imageLink!) else {
return
}
guard let imageData = try? Data(contentsOf: imageUrl) else {
return
}
self.imageData = imageData
}
}

Stack spacing is ignored

I've an image which is downloaded async. This image is resized to fit the screen by a constraint. This seems to break the vertical spacing between the image and label in the table cell which is set by the stack view.
Table view
xCode constrains
Result iPhone 5S
Code to set the image height:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "overview", for: indexPath) as! OverviewCell
let upload = images[indexPath.row]
cell.titleLabel.text = upload.image.title
let cachedImage = cachedImages[upload.image.imageUrl]
if let cachedImage = cachedImage {
var cellFrame = cell.frame.size
cellFrame.width = cellFrame.width - 30
let resizedImage = cachedImage.resize(width: cellFrame.width)
cell.imageView!.image = resizedImage
cell.heightConstrain.constant = resizedImage.size.height
}
else {
DispatchQueue.global().async {
let data = try! Data.init(contentsOf: URL.init(string: upload.image.imageUrl)!)
DispatchQueue.main.sync {
self.cachedImages[upload.image.imageUrl] = UIImage.init(data:data)!
self.tableView.beginUpdates()
self.tableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.fade)
self.tableView.endUpdates()
}
}
}

images repeating cells when scrolled in tableview

images are not showing properly in tableview , I have two Json Api (Primary/high) school. I can append the Both Json api Data and display into tableview,
tableview working fine it's showing both(primary/high) school data. when I can scroll the tableview images are jumping and images loading very slow in image view at tableview.
Before scrolling tableview its showing like this
After scrolling the tableview it's shows like this
after scrolling images are jumping,
this is the code
var kidsdata = [KidDetails]()
func getprimarydata(_firsturl: String,firstid:String,updatedate:String)
{
if errorCode == "0" {
if let kid_list = jsonData["students"] as? NSArray {
self.kidsdata.removeAll()
for i in 0 ..< kid_list.count {
if let kid = kid_list[i] as? NSDictionary {
let imageURL = url+"/images/" + String(describing: kid["photo"]!)
self.kidsdata.append(KidDetails(
name:kid["name"] as? String,
photo : (imageURL),
standard: ((kid["standard"] as? String)! + "std" + " " + (kid["section"] as? String)! + " section ")
))}}}}
}
func gethighdata(_secondurl:String ,secondid:String,updatedate:String)
{
if errorCode == "0" {
if let kid_list = jsonData["students"] as? NSArray {
for i in 0 ..< kid_list.count {
if let kid = kid_list[i] as? NSDictionary {
let imageURL = url+"/images/" + String(describing: kid["photo"]!)
self.kidsdata.append(KidDetails(
name:kid["name"] as? String,
photo : (imageURL),
standard: ((kid["standard"] as? String)! + "th" + " " + (kid["section"] as? String)! + " section ")
)
)
}
}
self.do_table_refresh()
}
}
}
func do_table_refresh()
{
DispatchQueue.main.async(execute: {
self.TableView.reloadData()
return
})
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell =
tableView.dequeueReusableCell(
withIdentifier: "cell", for: indexPath) as! DataTableViewCell
cell.selectionStyle = .none
cell.ProfileImage?.image = nil
let row = (indexPath as NSIndexPath).row
let kid = kidsdata[row] as KidDetails
cell.NameLabel.text = kid.name
cell.ProfileImage.image = UIImage(named: "profile_pic")
cell.ProfileImage.downloadImageFrom(link:kid.photo!, contentMode: UIViewContentMode.scaleAspectFill)
cell.ClassNameLabel.text = kid.standard
return cell
}
where I did mistake pls help me....!
AlamofireImage handles this very well. https://github.com/Alamofire/AlamofireImage
import AlamofireImage
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! DataTableViewCell
cell.selectionStyle = .none
let kid = kidsdata[indexPath.row] as KidDetails
cell.NameLabel.text = kid.name
cell.ClassNameLabel.text = kid.standard
// assuming cell.ProfileImage is a UIImageView
cell.ProfileImage.image = nil
let frame = CGSize(width: 50, height: 50)
let filter = AspectScaledToFillSizeWithRoundedCornersFilter(size: frame, radius: 5.0)
cell.ProfileImage.af_setImage(withURL: urlToImage, placeholderImage: nil, filter: filter,
imageTransition: .crossDissolve(0.3), runImageTransitionIfCached: false)
return cell
}
All we need to do is use the prepareForReuse() function. As discussed in this medium article, This function is called before cell reuse, letting you cancel current requests and perform a reset.
override func prepareForReuse() {
super.prepareForReuse()
ProfileImage.image = nil
}

Show wrong images in UICollectionView

there! I need your help. I have UIImage inside UICollectionView which lying inside UITableView. When I get data from API at the first time it shows images right, but when I start to scroll down and come back, it shows wrong images. My code looks like this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! AllPostsOfUserCollectionViewCell
let post = allPostsOfUserArray[collectionView.tag]
if post.imageLinks.count != 0 {
let imageLink = post.imageLinks[indexPath.row]
if imageLink.imageLink != nil {
let url = URL(string: imageLink.imageLink!)
cell.imageOfAnimalInCollectionView.sd_setImage(with: url!, placeholderImage: UIImage(named: "App-Default"),options: SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
})
}
}
return cell
}
My model looks like this:
class UserContent {
var name = ""
var imageLinks = [UserImages]()
init(name: String, imageLinks: [UserImages]?) {
self.name = name
if imageLinks != nil {
self.imageLinks = imageLinks!
}
}
init() { }
deinit {
imageLinks.removeAll()
}
}
class UserImages {
var imageLink: String?
init(imageLink: String?) {
self.imageLink = imageLink
}
init() { }
deinit {
imageLink = ""
}
}
What I do in UITableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let index = indexPath.row
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! AllPostsOfUserTableViewCell
cell.collectionView.tag = index
let post = allPostsOfUserArray[index]
if post.imageLinks.count == 0 {
cell.collectionView.isHidden = true
} else {
cell.collectionView.isHidden = false
}
return cell
}
UPD: I added cell.collectionView.reloadData() to func tableView(cellForRowAt indexPath). Now it works fine.
Could you try this code:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! AllPostsOfUserCollectionViewCell
cell.imageOfAnimalInCollectionView.image = UIImage(named: "App-Default")
let post = allPostsOfUserArray[collectionView.tag]
if post.imageLinks.count != 0 {
let imageLink = post.imageLinks[indexPath.row]
if imageLink.imageLink != nil {
let url = URL(string: imageLink.imageLink!)
cell.imageOfAnimalInCollectionView.sd_setImage(with: url!, placeholderImage: UIImage(named: "App-Default"),options: SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
})
}
}
return cell
}
This problem is something like race condition.
You can reference my answer in similair question
You might need to find another module to download image and set to cell manually.
cell.imageOfAnimalInCollectionView.image = nil
if post.imageLinks.count != 0 {
let imageLink = post.imageLinks[indexPath.row]
if imageLink.imageLink != nil {
let url = URL(string: imageLink.imageLink!)
cell.imageOfAnimalInCollectionView.sd_setImage(with: url!, placeholderImage: UIImage(named: "App-Default"),options: SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
})
}
}
(Not tested, but should work. You can also use sd_image's method to assign an empty url, with a placeholder image to just show the placeholder image. Or just give an empty url string '' without the placeholder image)
Whenever you scroll, the cells are re-used, and the image assigned to the cell reappears because it is still on the cell.
You have to remove those images by using else clause for every if where you are assigning an image to the imageView.
Hope this helps.
You have to write else conditions like Below ..
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! AllPostsOfUserCollectionViewCell
let post = allPostsOfUserArray[collectionView.tag]
if post.imageLinks.count != 0 {
let imageLink = post.imageLinks[indexPath.row]
if imageLink.imageLink != nil {
let url = URL(string: imageLink.imageLink!)
cell.imageOfAnimalInCollectionView.sd_setImage(with: url!, placeholderImage: UIImage(named: "App-Default"),options: SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
})
}
else{
cell.imageOfAnimalInCollectionView = "defaultImage"
}
}
else{
cell.imageOfAnimalInCollectionView = "defaultImage"
}
return cell
}
Reason
after dequeueing cell cellectionView reuses the created cell for all next cells so if you not write an else condition or not use prepareForReuse method it will always data of previous cell that is in its cache.

Struct check type | Swift

Have the structure where contains 2 types - image and text. Have an array, where it will be added. How to make type check in cellForRowAtIndexPath?
struct typeArray {
var text: String?
var image: UIImage?
init(text: String){
self.text = text
}
init(image: UIImage){
self.image = image
}
}
var content = [AnyObject]()
Image add button:
let obj = typeArray(image: image)
content.append(obj.image!)
self.articleTableView.reloadData()
Text add button:
let obj = typeArray(text: self.articleTextView.text as String!)
self.content.append(obj.text!)
self.articleTableView.reloadData()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
if content[indexPath.row] == {
let cell = self.articleTableView.dequeueReusableCellWithIdentifier("Text Cell", forIndexPath: indexPath) as! TextTableViewCell
cell.textArticle.text = content[indexPath.row] as? String
return cell
}
else if content[indexPath.row] == {
let cell = self.articleTableView.dequeueReusableCellWithIdentifier("Image Cell", forIndexPath: indexPath) as! ImageTableViewCell
cell.backgroundColor = UIColor.clearColor()
cell.imageArticle.image = content[indexPath.row] as? UIImage
return cell
}
return UITableViewCell()
}
You should declare your content array to hold instances of your typeArray struct;
var content = [typeArray]()
Then add instances of the struct to the array:
let obj = typeArray(image: image)
content.append(obj)
self.articleTableView.reloadData()
Then you can use this in your cellForRowAtIndexPath -
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let rowStruct = content[indexPath.row] {
if let text = rowStruct.text {
let cell = self.articleTableView.dequeueReusableCellWithIdentifier("Text Cell", forIndexPath: indexPath) as! TextTableViewCell
cell.textArticle.text = text
return cell
} else if let image = rowStruct.image {
let cell = self.articleTableView.dequeueReusableCellWithIdentifier("Image Cell", forIndexPath: indexPath) as! ImageTableViewCell
cell.backgroundColor = UIColor.clearColor()
cell.imageArticle.image = image
return cell
}
return UITableViewCell()
}

Resources