Swift : Why did my IOS application slow down - swift

I use the WebServices(SOAP) to reach the data. I take the image urls from database , download them and show in TableView. here is my codes.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "gorevlerCell",for: indexPath) as! GorevlerCell
cell.contentView.backgroundColor = UIColor(white: 0.95, alpha: 1)
// Fil the gorevler Cell
cell.txtGorevAdi.text = (gorevler.object(at: indexPath.row) as AnyObject).value(forKey: "Gorev_Adi") as? String
cell.txtGoreviAlan.text = (gorevler.object(at: indexPath.row) as AnyObject).value(forKey: "Gorevi_Alan") as? String
cell.txtGoreviVeren.text = (gorevler.object(at: indexPath.row) as AnyObject).value(forKey: "Gorevi_Veren") as? String
cell.txtTarih.text = (gorevler.object(at: indexPath.row) as AnyObject).value(forKey: "Tarih") as? String
cell.txtOncelikDurumu.text = (gorevler.object(at: indexPath.row) as AnyObject).value(forKey: "Oncelik_Durumu") as? String
// The Image File
let imgUrl2 = (gorevler.object(at: indexPath.row) as AnyObject).value(forKey: "Profil_Url") as? String
let trimmedImgUrl = imgUrl2?.trimmingCharacters(in: .whitespaces)
let url = NSURL( string : "http://"+trimmedImgUrl! )
let data = NSData(contentsOf: url! as URL)
let img = UIImage(data: data! as Data)
cell.profileImage.image = img
cell.profileImage.layer.cornerRadius = 35
cell.profileImage.layer.borderWidth = 2.0
cell.profileImage.layer.borderColor = UIColor.lightgray.cgColor
cell.profileImage.clipsToBounds = true
return cell}
So, When i added the image in the TableView and scrolled it , the application began slow down. My question is that why the application is slow down and how to faster it.
Note : i can try with low resolution image but still slow.

Its slow because you are download image Synchronously.
You need to download it Asynchronously. Consider below example:
Add below extension in your app.
extension UIImageView {
func downloadedFrom(url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {
contentMode = mode
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.image = image
}
}.resume()
}
func downloadedFrom(link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
downloadedFrom(url: url, contentMode: mode)
}
}
And you can use it in your case:
cell.profileImage.downloadedFrom(link: "http://"+trimmedImgUrl!)
Replace above line with:
let url = NSURL( string : "http://"+trimmedImgUrl! )
let data = NSData(contentsOf: url! as URL)
let img = UIImage(data: data! as Data)
cell.profileImage.image = img
For more info check original post.

Related

Wrong Images Loaded to TableView

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()
}
}
}

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()
}
}

UIImage init with real data returns nil

Good day! It makes me mad, 'cos I do not understand what's going on.
I've got 2 TableViewControllers with some similar logics.
I work with 1 Data Model. This model contains user info and each entity always has a photo. To init it, I use the code below :
var partnerPhoto: UIImage? = nil
if let imageData = partners[indexPath.item].userPhoto {
partnerPhoto = UIImage(data: imageData as! Data)
}
In debug partners[indexPath.item].userPhoto has real data and even imageData shows 4213 bytes.But, app crashes with typical error:
fatal error: unexpectedly found nil while unwrapping an Optional value
EDIT:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if dialogs[indexPath.item].fromID == profileID {
let cell = tableView.dequeueReusableCell(withIdentifier: "dialogMeCell", for: indexPath) as! CellForDialogMe
var partnerPhoto: UIImage? = nil
if let imageData = partners[indexPath.item].userPhoto {
partnerPhoto = UIImage(data: imageData as! Data)
}
cell.fillWithContent(partnerPhoto: partnerPhoto!, selfPhoto: profilePhoto!)
return cell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "dialogHimCell", for: indexPath) as! CellForDialogHim
var partnerPhoto: UIImage? = nil
if let imageData = partners[indexPath.item].userPhoto {
partnerPhoto = UIImage(data: imageData as! Data)
}
cell.fillWithContent(partnerPhoto: partnerPhoto!)
return cell
}
SOLUTION:
In fact I have not found concrete solution for this problem, I have just moved some logic from model to view controller. In model I did load from URL to data. Now I do it right in ViewController and it works perfect, but it is odd, very odd for me, 'cos I just did cmd-x cmd-v.
Cast your imageData before the scope:
if let imageData = partners[indexPath.row].userPhoto as? Data {
partnerPhoto = UIImage(data: imageData)
} else {
partnerPhoto = UIImage() //just to prevent the crash
debugPrint("imageData is incorrect")
//You can set here some kind of placeholder image instead of broken imageData here like UIImage(named: "placeholder.png")
}
cell.fillWithContent(partnerPhoto: partnerPhoto)
return cell
Also, it would be helpful if you'll provide more details - how and when profilePhoto is init-ed and
cell.fillWithContent(partnerPhoto: partnerPhoto!, selfPhoto: profilePhoto!) code.
Also, you can set breakpoint on your cell.fillWithContentand check if partnerPhoto and/or profilePhoto is nil before functions is called.
Try this:
var partnerPhoto: UIImage?
guard let imageData = partners[indexPath.item].userPhoto else {return}
guard let image = UIImage(data: imageData) else {return}
partnerPhoto = image
In my case return nil because I try to download image from FirebaseStorage by Alamofire and than save to Core Data. Into Core Data save correct and my photoData is not nil, but UIImage(data: photoData as Data) return nil.
I resolve this problem by retrieving image native method FirebaseStorage:
func retrieveUserPhotoFromStorage(imageUID: String?, completion: #escaping (Result<Data, DomainError>) -> Void) {
guard let imageID = imageUID else { return }
let storageRef = storage.reference(withPath: DatabaseHierarhyKeys.users.rawValue).child(imageID)
storageRef.getData(maxSize: Constant.maxSize) { data, error in
guard let error = error else {
if let data = data {
completion(.success(data))
return
}
completion(.failure(.domainError(value: "errorRetrieveImage data nil")))
return
}
completion(.failure(.domainError(value: "errorRetrieveImage \(error.localizedDescription)")))
}
}
Than save it to Core Data and UIImage(data: photoData as Data) already is not nil.