Wrong Images Loaded to TableView - swift

This is my first time using NSCache for a table view. For cellForRow I call an NSCache loading an image. The image sometimes is the wrong image. What can I do to fix this? If the cache does not contain it, I replace the photo with "randomguy".
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyMessagesTableViewCell", for: indexPath) as! MyMessagesTableViewCell
if let cachedImage = cache.object(forKey: urlArray[indexPath.row] as NSString) as? UIImage {
cell.proPicImageView.image = cachedImage
cell.proPicImageView.layer.cornerRadius =
cell.proPicImageView.frame.size.height / 2
cell.proPicImageView.layer.masksToBounds = true
cell.proPicImageView.layer.borderWidth = 0
cell.usernameLabel.text = usernameArray[indexPath.row]
cell.messageLabel.text = messageArray[indexPath.row]
cell.messageLabel.textColor = colorArray[indexPath.row]
cell.messageLabel.font = fontArray[indexPath.row]
cell.timeLabel.text = timeArray[indexPath.row]
} else {
cell.proPicImageView.image = UIImage(named: "randomguy")
cell.proPicImageView.layer.cornerRadius =
cell.proPicImageView.frame.size.height / 2
cell.proPicImageView.layer.masksToBounds = true
cell.proPicImageView.layer.borderWidth = 0
cell.usernameLabel.text = usernameArray[indexPath.row]
cell.messageLabel.text = messageArray[indexPath.row]
cell.messageLabel.textColor = colorArray[indexPath.row]
cell.messageLabel.font = fontArray[indexPath.row]
cell.timeLabel.text = timeArray[indexPath.row]
}
return cell
}
In view Did Load:
let cache = NSCache()
func photoQuery () {
for username in self.usernameArray {
let photoQuery = PFQuery(className: "UserPhoto")
photoQuery.whereKey("username", equalTo: username)
photoQuery.findObjectsInBackground(block: { (objects:
[PFObject]?,error: Error?) in
if let objects = objects {
for object in objects {
if error == nil {
let userImageFile = object["photo"] as? PFFileObject
let urlString = userImageFile?.url as! String
if let url = URL(string: urlString) {
let data = try? Data(contentsOf: url)
if let imageData = data {
self.messageImageArray.append(UIImage(data:imageData)!)
self.cache.setObject(UIImage(data:imageData)!, forKey: urlString as
NSString)
self.urlArray.append(urlString as NSString)
print(self.messageImageArray)
}
}
}
}
}
})
}
}

This can be one way where you can create your custom image view and have a function inside this that will take care of downloading and assigning image to the proper cell
let cache = NSCache<NSString, UIImage>()
class CustomImageView: UIImageView {
var imageURLString: String?
func startImageDownloadOperation(url: URL) {
imageURLString = url.absoluteString
if let cachedVersion = cache.object(forKey: url.absoluteString as NSString) {
self.image = cachedVersion
} else {
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard error == nil,
let response = response as? HTTPURLResponse,
response.statusCode == 200 else {
DispatchQueue.main.async {
self.image = UIImage(named: "default")
}
return
}
DispatchQueue.main.async {
if let data = data {
let imageToCache = UIImage(data: data)
if url.absoluteString == self.imageURLString {
self.image = imageToCache
cache.setObject(imageToCache!, forKey: url.absoluteString as NSString)
}
}
}
}
task.resume()
}
}
}

Related

how to set custom UIImage to tabbar item

what im trying to achieve is to have custom profile IMG on a tabbar item
First: I have tried to add the IMG to the tabbar directly
let ProfileImg: UIImageView = {
let Img = UIImageView(image: UIImage(named: "PlaseHolder"))
Img.layer.cornerRadius = 20
Img.contentMode = .scaleAspectFit
Img.clipsToBounds = true
Img.layer.masksToBounds = true
Img.translatesAutoresizingMaskIntoConstraints = false
return Img
}()
override func viewDidLoad() {
super.viewDidLoad()
fetchUser()
}
func fetchUser() {
guard let uid = Auth.auth().currentUser?.uid else {return}
let userRef = Database.database().reference(withPath: "users").child(uid)
userRef.observeSingleEvent(of: .value, with: { (snapshot) in
print(snapshot.value ?? "")
let value = snapshot.value as? NSDictionary
guard let profileImageUrl = value?["profileImageUrl"] as? String else {return}
guard let url = URL(string: profileImageUrl) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
if let err = err {
print("unable to get profile image", err)
}
guard let data = data else {return}
let image = UIImage(data: data)
DispatchQueue.main.async {
self.tabBarItem.image = image?.withRenderingMode(.alwaysOriginal)
self.tabBarItem.selectedImage = image?.withRenderingMode(.alwaysOriginal)
}
}.resume()
}) { (err) in
print("fail to fetch user", err)
}
}
and this was the result :
iphone simulator image
the image is too big for a tabbar item image
but then I found another method I couldn't dead it or get it, it did add the image but I couldn't make it fit inside the tabbar + the corner radius is not working
extension UITabBarController {
func addSubviewToLastTabItem(_ imageName: UIImage) {
if let lastTabBarButton = self.tabBar.subviews.last, let tabItemImageView = lastTabBarButton.subviews.last {
if let accountTabBarItem = self.tabBar.items?.first {
accountTabBarItem.selectedImage = nil
accountTabBarItem.image = nil
}
let imgView = UIImageView()
imgView.frame = tabItemImageView.frame
imgView.layer.cornerRadius = tabItemImageView.frame.height/2
imgView.layer.masksToBounds = true
imgView.contentMode = .scaleAspectFill
imgView.clipsToBounds = true
imgView.image = imageName
self.tabBar.subviews.last?.addSubview(imgView)
}
}
}
and this is how to call it
self.tabBarController?.addSubviewToLastTabItem(image!)
Result : iphone simulator image
please any idea to accomplish this
use this extintion to resize your image
extension UIImage {
func resize(targetSize: CGSize) -> UIImage {
return UIGraphicsImageRenderer(size:targetSize).image { _ in
self.draw(in: CGRect(origin: .zero, size: targetSize))
}
}
}
here is how to use it in dispatch method
DispatchQueue.main.async { [weak self] in
guard let data = data else {return}
self?.ProfileImg.image = UIImage(data: data)?.resize(targetSize: CGSize(width: 33, height: 33)).roundMyImage.withRenderingMode(.alwaysOriginal)
self?.tabBar.items?[0].selectedImage = self?.ProfileImg.image
self?.tabBar.items?[0].image = self?.ProfileImg.image
}
}.resume()

download images from url to image array and show in table view swift [duplicate]

This question already has answers here:
Loading/Downloading image from URL on Swift
(39 answers)
Closed 3 years ago.
I am facing trouble getting image from my json url.
this is my json:
"bank_details": [
{
"id": 1,
"logo": "http://mortgagemarket.ae/webApi/public/mortgage_bank_icons/noorebank.png",
"name": abc company
}
]
my swift code to parse the image is this:
import UIKit
class BanksViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
final let BANKS_URL = "http://www.mortgagemarket.ae/webApi/api/manage_interest_rates"
#IBOutlet weak var tableView: UITableView!
var bankicon = [String]()
var bankname = [String]()
var bankid = [Int]()
let stringid: String = ""
override func viewDidLoad() {
super.viewDidLoad()
self.displayFromDb()
tableView.dataSource = self
tableView.delegate = self
}
func displayFromDb()
{
let tokensp = UserDefaults.standard.string(forKey: "tokenKey")
let url = NSURL(string: BANKS_URL+"?token="+tokensp!)
print(url)
URLSession.shared.dataTask(with: (url as?URL)!, completionHandler: {(data,response,error) ->
Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary
{
print(jsonObj.value(forKey: "bank_details")!)
if let messageArray = jsonObj.value(forKey: "bank_details") as? NSArray
{
print(jsonObj.value(forKey: "bank_details")!)
for message in messageArray
{
if let messageDict = message as? NSDictionary
{
if let data = data {
if let bankname = messageDict.value(forKey: "bank_name")
{
self.bankname.append(bankname as! String)
print(bankname)
}
if let banklogo = messageDict.value(forKey: "logo")
{
self.bankicon.append(banklogo as! String)
print(banklogo)
}
if let bankid = messageDict.value(forKey: "id")
{
self.bankid.append(bankid as! Int)
print(bankid)
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
}
}
}
}
}).resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (bankname.count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! BanksTableViewCell
cell.bankicon.image = bankicon[indexPath.row] as? UIImage
cell.bankname.text = bankname[indexPath.row]
return (cell)
}
}
now When I run this code it is showing blank table cells. I dont know how to get image from url and display the images in table view cell. Please someone help me.
this is my whole code to get the all the json data into table view cell. Please someone help me
imageicon[indexPath.row] gives a urlStringand not the instance ofUIImage. You need to fetch the image from server using this urlString.
Use URLSession to fetch the image from server like,
if let url = URL(string: imageicon[indexPath.row]) {
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let data = data {
DispatchQueue.main.async {
cell.imageicon.image = UIImage(data: data)
}
}
}.resume()
}
Your models should be like this:
/// Your response models
struct BankDetails: Codable {
let bank_details: [ImageUrl]
}
struct ImageUrl: Codable {
let logo: String
}
And then in your cell:
class MyCell: UITableViewCell {
/// create dataTask for cancel in prepareForReuse function
private var dataTask: URLSessionDataTask?
/// Like this
override public func prepareForReuse() {
super.prepareForReuse()
dataTask?.cancel()
}
func populate(with model: YourModel) {
/// You should set url in indexPath of your logo array([ImageUrl])
let url = model.url /// It's sample url for explain this is an url of your current index model
if let imageUrl = url {
downloaded(from: imageUrl)
}
}
func downloaded(from url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
contentMode = mode
dataTask = URLSession.shared.dataTask(with: url) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() {
self.yourImageView.image = image
}
}
dataTask?.resume()
}
}
In your Controller's tableView cellForRowAt function:
let model = models[indexPath.row]
cell.populate(with: model)
return cell
You can use the above models, and create displayFromDb like this:
func displayFromDb() {
let tokensp = UserDefaults.standard.string(forKey: "tokenKey")
let url = NSURL(string: BANKS_URL+"?token="+tokensp!)
if let myUrl = url {
URLSession.shared.dataTask(with: myUrl) { (data, response , error) in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let data = try decoder.decode(BankDetails.self, from: data)
print("my logo array is: \(data.bank_details)")
// TODO: - So you get urls
} catch let err {
print("Err", err)
}
}.resume()
}
}

UIImage keeps loading all time when scroll even store in NSCache - swift

I am new in iOS programming. I am creating a simple app which loads image from a particular link ( firestore ). The images are completely downloaded from the server and visible on each cell of collectionview as usual. But the problem is that when when I scroll up or down then those images keeps loading again. I think it starts downloading again because when I turn off internet connection, those images are not being loaded anymore.
Here is how i set images in each cell
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CollectionCell
let explore = dataAppend[indexPath.item]
//cell.imageDisplay.text = explore.title
if let imageUrl = explore.image {
cell.imageDisplay.loadImageWithData(urlString: imageUrl)
}
//print(explore.image)
return cell
}
Here is how loading images look like loadImageWithData(urlString: imageUrl)
let imageCache = NSCache<NSString, UIImage>()
class CustomImageView : UIImageView {
var imageUrlString: String?
func loadImageWithData (urlString: String) {
imageUrlString = urlString
if let imageFromCache = imageCache.object(forKey: urlString as NSString){
self.image = imageFromCache
}
image = nil
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if let err = error {
print(err.localizedDescription)
}
if let data = data {
DispatchQueue.main.async {
let imageToCache = UIImage(data: data)
if self.imageUrlString == urlString {
self.image = imageToCache
}
imageCache.setObject(imageToCache!, forKey: urlString as NSString)
}
}
}).resume()
}
}
var imageCache = NSMutableDictionary()
class CustomImageView: UIImageView {
func loadImageUsingCacheWithUrlString(urlString: String) {
self.image = nil
if let img = imageCache.valueForKey(urlString) as? UIImage{
self.image = img
return
}
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(NSURL(string: urlString)!, completionHandler: { (data, response, error) -> Void in
if(error == nil){
if let img = UIImage(data: data!) {
imageCache.setValue(img, forKey: urlString) // Image saved for cache
DispatchQuee.main.asyn{
self.image = img
}
}
})
task.resume()
}
}
}
You can instead use the Kingfisher library , handles the image caching itself you don't need to worry about it. For implementing see :
https://github.com/onevcat/Kingfisher
with just one line of code you can set the image
imgView.kf.setImage(with: ImageResource(downloadURL: URL(string: imgUrl)!))

Images in table view reload automatically

I have a TabBarController with some viewControllers. In one, I have a UITableview with some images but when I insert a new image and go back in the feed section, I see the new image but when I scroll through the various images, the images reload automatically; they do not remain fixed but are recharged when I run. Solutions?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as! PostCell
cell.postID = self.posts[indexPath.row].postID
cell.userImage.layer.cornerRadius = 25.0
cell.userImage.clipsToBounds = true
cell.author.text = self.posts[indexPath.row].nameuser
cell.userImage.downloadImage(from: self.posts[indexPath.row].userimage)
cell.postImage.downloadImage(from: self.posts[indexPath.row].pathToImage)
cell.caption.text = self.posts[indexPath.row].caption
return cell
}
func loadPosts() {
followpeople()
Database.database().reference().child("posts").observe(.childAdded) { (snapshot: DataSnapshot) in
if let dict = snapshot.value as? [String: Any] {
let captionText = dict["caption"] as! String
let photoUrlString = dict["photoUrl"] as! String
let photoimage = dict["profileImageUrl"] as! String
let author = dict["Author"] as! String
let postid = dict["postID"] as! String
let uda = dict["uid"] as! String
let like = dict["likes"] as! Int
let date = dict["date"] as! String
let posst = Post()
posst.nameuser = author
posst.likes = like
posst.caption = captionText
posst.postID = postid
posst.pathToImage = photoUrlString
posst.userimage = photoimage
posst.userID = uda
self.posts.append(posst)
self.tableView.reloadData()
}
}
}
func downloadImage(from imgURL: String!) {
let url = URLRequest(url: URL(string: imgURL)!)
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
if (error != nil) {
print(error!)
return
}
DispatchQueue.main.async {
self.image = UIImage(data: data!)
}
}
task.resume()
}
The problem because of these 2 lines
cell.userImage.downloadImage(from: self.posts[indexPath.row].userimage)
cell.postImage.downloadImage(from: self.posts[indexPath.row].pathToImage)
they will fetch the images again even if they just been downloaded when you scroll consider using SDWebImage instead to cache the image after first download
1- install SDWebImage by adding a pod for it
2- replace the 2 lines with
cell.userImage.sd_setImage(with: URL(string: self.posts[indexPath.row].userimage), placeholderImage: UIImage(named: "placeholder.png"))
cell.postImage.sd_setImage(with: URL(string: self.posts[indexPath.row].pathToImage), placeholderImage: UIImage(named: "placeholder.png"))

How to show image view in table view from json

To parse json i have following function
func single_news(userid: Int) {
var request = URLRequest(url: URL(string: news_url)!)
request.httpMethod = "POST"
//Pass your parameter here
let postString = "userid=\(userid)"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print("error=(error)")
return
}
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data, options: [])
print("abcnews")
//here is your JSON
print(json)
let jsonValue : NSDictionary = json as! NSDictionary
self.results = jsonValue.object(forKey: "data") as! [[String:String]]
self.DiscoveryNewsTableView.delegate = self
self.DiscoveryNewsTableView.dataSource = self
self.DiscoveryNewsTableView.reloadData()
// let _ = getData.shared.getDataForTableView(dict: json)
}
catch
{
return
}
guard let server_response = json as? NSDictionary else
{
return
}
}
task.resume()
}
To get data the class is created
class getData: NSObject {
var descriptionn : String = ""
var image : String = ""
// static let shared = getData()
func getDataForTableView(results: [[String:String]], index : Int){
var productArray = [String:String]()
productArray = results[index]
descriptionn = productArray["description"]!
image = productArray["images"]!
}
}
To display data in table view
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "discoveryNewscell") as! DiscoveryNewsTableViewCell
// if results.count > 0{
classObject.getDataForTableView(results: results, index: indexPath.row)
cell.sneakerImageView.image=filteredsneakernews[indexPath.row].image
print("abc image"+classObject.image)
cell.newsTitle.text = classObject.descriptionn
// }
return cell
}
How to display the image .Image(classObject.image) in string format how to display image view on table view ?you can download the code from this link .https://drive.google.com/file/d/1bVQsuSQINSa6YRwZe2QwEjPpU_m7S3b8/view?usp=sharing
You're wanting to display an image but you only have the URL to that image and not the image itself so you'll need to download it, then display it. I have a class I use a lot that allows you to simply call one line to download AND cache the image so you'll be able to do something like this:
classObject.getDataForTableView(results: results, index: indexPath.row)
let image_url = filteredsneakernews[indexPath.row].image
cell.sneakerImageView.loadImageUsingCacheWithUrlString(urlString: image_url!)
To do this, you'll have to copy the class below and inside your cell class, you’ll want to change the imageView type from a standard UIImageView to a CustomImageView for example:
let imageView: CustomImageView!
//
import UIKit
let imageCache = NSCache<NSString, UIImage>()
class CustomImageView: UIImageView {
var imageUrlString: String?
func loadImageUsingCacheWithUrlString(urlString: String) {
imageUrlString = urlString
if let cachedImage = imageCache.object(forKey: urlString as NSString) {
self.image = cachedImage
return
}
self.image = nil
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil { return }
DispatchQueue.main.async {
if let downloadedImage = UIImage(data: data!) {
if self.imageUrlString == urlString {
if self.imageUrlString != "" {
self.image = downloadedImage
} else {
self.image = nil
}
}
imageCache.setObject(downloadedImage, forKey: urlString as NSString)
}
}
}).resume()
}
}