The instance I've successfully called the images from array JSON returned object the UICollection is very slow to load especially if it has main images.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let childDict: NSDictionary = subCategoryData .object(at: indexPath.row) as! NSDictionary
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "listcollectionview", for: indexPath) as! subCategoryCollectionViewCell
subCategoryTable.register(UINib(nibName: "subCategoryCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "listcollectionview")
let subimages = childDict.object(forKey: "image") as! String!
let data = NSData(contentsOf: NSURL(string: subimages!)! as URL)
cell.backgroundImageView.image = UIImage(data: data! as Data)
cell.categoryName.text = (subCategoryMenuData [indexPath.row] as? String)
cell.categoryName?.textColor = UIColor.white
cell.categoryName?.backgroundColor = UIColor().HexToColor(hexString: GREYBLACK)
return cell;
}
I tried as well the dispatch.queue in didselectitemat when calling the segue but this didn't solve the problem
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let childDict: NSDictionary = subCategoryData .object(at: indexPath.row) as! NSDictionary
if (childDict.object(forKey: "children") as! NSArray).count > 0{
let sb = UIStoryboard(name: "Main", bundle: nil)
let initViewController: subCategory? = (sb.instantiateViewController(withIdentifier: "subCategory") as? subCategory)
initViewController?.subCategoryData = (childDict.object(forKey: "children") as! NSArray)
initViewController?.subName = childDict.object(forKey: "name") as! String!
initViewController?.subId = childDict.object(forKey: "path") as! String!
initViewController?.modalTransitionStyle = .flipHorizontal
self.navigationController?.pushViewController(initViewController!, animated: true)
}else{
categoryName = childDict .object(forKey: "name") as! String
categoryId = childDict .object(forKey: "path") as! String
DispatchQueue.main.async {
self.performSegue(withIdentifier: "productCategorySegue",sender: self)
}
}
}
It some times take 30 seconds to load
You load your image in cell for index you can use sdwebimage library install theough pods for lazy loading. It will definitely resolved your issue.
Oh I just found the solutions thanks to #abhishekkharwar post here
I converted the ObjC to Swift 3 to resolve the issue.
DispatchQueue.global(qos: .default).async(execute: {() -> Void in
var image = UIImage(contentsOfFile: frontPath)
DispatchQueue.main.async(execute: {() -> Void in
frontButton.setBackgroundImage(image, for: .normal)
})
})
Using AlamofireImage library:
let subimages = childDict.object(forKey: "image") as! String!
if let imageUrl = URL(string: subimages){
cell.backgroundImageView.af_setImage(withURL: imageUrl, placeholderImage: nil)
}
-----Swift 4-----
Step 1: Add this Extension : if you dont know about extension , check this : Swift extension example
extension UIImageView {
func downloadImage(from url:String){
if url != ""{
let urlRequest = URLRequest(url: URL(string:url)!)
let task = URLSession.shared.dataTask(with: urlRequest){(data,response,error) in
if error != nil{
return
}
DispatchQueue.main.async {
self.image = UIImage(data: data!)
}
}
task.resume()
}
}
}
Step 2 : Using UIImageViewExtension for Downloading Image :
In your 'cellForItemAt indexPath' method use this code :
cell.backgroundImageView.downloadImage(from: data)
Additional: check your image file size. if it is big in size thats obiously takes time to load . You can add this smooth image appearing animation before your image load for making it cool.
UIView.transition(with: cell.backgroundImageView,
duration: 0.5,
options: .transitionCrossDissolve,
animations: { cell.backgroundImageView.downloadImage(from: data) },
completion: nil)
Related
I am new to swift language so not sure how to resolve this issue. Here I am trying to display images using uicollectionview. But I not getting the proper output as it does not show anything on collection view when executed. Need help friends.
View Did Load Function
override func viewDidLoad() {
super.viewDidLoad()
ImageGet()
}
Collection View
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print(defectImages.count) // returns zero value here
return defectImages.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCell", for: indexPath) as! ImageCell
cell.image.image = defectImages[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let largeVC = mainStoryBoard.instantiateViewController(withIdentifier: "ImageDisplayVC") as! ImageDisplayVC
largeVC.imgImage = defectImages[indexPath.row]
self.navigationController?.pushViewController(largeVC, animated: true)
}
Alamofire to get images
func ImageGet() {
let imageId = Int(details.id!)
let para: Parameters = ["imageId": imageId]
Alamofire.request(URL_IMG_List, method: .post, parameters: para).responseJSON { response in
if((response.result.value) != nil) {
let swiftyJsonVar = JSON(response.result.value!)
if let resData = swiftyJsonVar["data"].arrayObject {
self.arrRes = resData as! [[String:AnyObject]]
for index in 0..<self.arrRes.count{
self.imageData.file_name = self.arrRes[index]["file_name"] as! String
self.completeImagePath = self.get_image_path + self.imageData.file_name
self.imgpath.append(self.completeImagePath)
guard let url = URL(string: self.completeImagePath) else {return}
print(url)
if let data = try? Data(contentsOf: url) {
guard let image: UIImage = UIImage(data: data) else {return}
print(image)
self.defectImages.append(image as UIImage)
}
}
print(self.defectImages.count)
}
}
}
}
You just need to reload your collectionView once you fetch data from API and please check that you set your collectionView dataSource and delegate from storyBoard. if not than write below lines in viewDidLoad() before ImageGet().
self.collectionView.dataSource = self
self.collectionView.delegate = self
Replace below code with yours.
func ImageGet() {
let imageId = Int(details.id!)
let para: Parameters = ["imageId": imageId]
Alamofire.request(URL_IMG_List, method: .post, parameters: para).responseJSON { response in
if((response.result.value) != nil) {
let swiftyJsonVar = JSON(response.result.value!)
if let resData = swiftyJsonVar["data"].arrayObject {
self.arrRes = resData as! [[String:AnyObject]]
for index in 0..<self.arrRes.count{
self.imageData.file_name = self.arrRes[index]["file_name"] as! String
self.completeImagePath = self.get_image_path + self.imageData.file_name
self.imgpath.append(self.completeImagePath)
guard let url = URL(string: self.completeImagePath) else {return}
print(url)
if let data = try? Data(contentsOf: url) {
guard let image: UIImage = UIImage(data: data) else {return}
print(image)
self.defectImages.append(image as UIImage)
}
self.collectionView.reloadData() // RELOAD COLLECTIONVIEW
}
print(self.defectImages.count)
}
}
}
}
How I represent the image in the firebase storage scored in a node called users with path name pc.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : viewTableViewCell = tableView.dequeueReusableCell(withIdentifier: "opa", for:indexPath) as! viewTableViewCell
if let post = posts[indexPath.row] {
cell.post.text = post
cell.name.text = self.loggedinuserdata!["name"] as! String
cell.handle.text = self.loggedinuserdata!["handle"] as! String
cell.pc.image = self.loggedinuserdata!["pc"] as! UIImage
}
return cell
}
Thank you in anticipation ))
First You need to write this function and pass url as parameter
func downloadImageFromUrl(imageUrl: String, completion: #escaping (_ success: UIImage) -> Void) {
let url = URL(string: imageUrl)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard let data = data, error == nil else { return }
completion(UIImage(data: data)!)
}
task.resume()
}
and Call it
self.downloadImageFromUrl(imageUrl: self.loggedinuserdata!["pc"] as! String , completion: { image in
cell.pc.image = image
})
Try this :
cell.pc.image = UIImage.init(named: self.loggedinuserdata!["pc"] as! String)
If image fetch from url
cell.pc.sd_setImage(with: URL(string: self.loggedinuserdata!["pc"] as! String), placeholderImage: nil)
SDWebImage Source
I have these two functions:
func callAlamo(url: String){
Alamofire.request(url).responseJSON(completionHandler: {
response in
self.parseData(JSONData: response.data!)
})
}
func parseData(JSONData: Data) {
do {
var readableJSON = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as! JSONStandard
if let tracks = readableJSON["tracks"] as? JSONStandard{
if let items = tracks["items"] as? [JSONStandard]{
for i in 0..<items.count{
let item = items[i]
let name = item["name"] as! String
if let album = item["album"] as? JSONStandard{
if let images = album["images"] as? [JSONStandard]{
let imageData = images[0]
let mainImageUrl = URL(string: imageData["url"] as! String)
let mainImageData = NSData(contentsOf: mainImageUrl!)
let mainImage = UIImage(data: mainImageData as! Data)
posts.append(post.init(image: mainImage, name: name))
self.tableView.reloadData()
}
}
}
}
}
}
catch{
print(error)
}
}
That retrieve song information from Spotify using alamofire. These functions are set up inside of a UITableViewController class. I have a separate UITableViewCell class set up in the same file where I set up this image:
let albumCoverImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
How would I use get the retrieved information from the two functions into that image? Because I cannot access the functions from inside the image imitation.
Use "SDWebImage" library for downloading images asynchronously.
Refer below link :-
https://github.com/rs/SDWebImage
Usage :-
Objective-C:
#import <SDWebImage/UIImageView+WebCache.h>
[imageView sd_setImageWithURL:[NSURL
URLWithString:imageURL]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
Swift:
import SDWebImage
imageView.sd_setImage(with: URL(string: imageURL), placeholderImage: UIImage(named: "placeholder.png"))
var imgFinal = [String:UIImage]()
Store your images in "imgFinal" array in func parseData():
self.imgArrList[i] = imageData["url"]
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CategoryTableViewCell
load_image((imgArrList[indexPath.row] as? String)!, imageview: cell.titleImage, indVal: indexPath.row)
return cell
}
Add this method to download image:
func load_image(link:String, imageview:UIImageView, indVal:Int)
{
let url:NSURL = NSURL(string: link)!
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: url)
request.timeoutInterval = 10
let task = session.dataTaskWithRequest(request) {
(
let data, let response, let error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
return
}
var image = UIImage(data: data!)
if (image != nil)
{
func set_image()
{
//self.images_cache[link] = image
self.imgFinal[link] = image
imageview.image = image
}
dispatch_async(dispatch_get_main_queue(), set_image)
}
}
task.resume()
}
I have an app that is using Swift 3 and Alamofire. The data is connected to two cell?.viewWithTag(2) and cell?.viewWithTag(1) which is an image (from url) and text. So when I run the project nothing is showing in my App. I have tested the JSON with print(readableJSON) and the JSON is getting printed into the console. So I am a little confused really. My swift looks like this:
SWIFT
import UIKit
import Alamofire
struct postinput {
let mainImage : UIImage!
let name : String!
}
class TableViewController: UITableViewController {
var postsinput = [postinput]()
var mainURL = "https://www.example.api.com"
typealias JSONstandard = [String : AnyObject]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
callAlamo(url: mainURL)
}
func callAlamo(url : String){
Alamofire.request(url).responseJSON(completionHandler: {
response in
self.parseData(JSONData: response.data!)
})
}
func parseData(JSONData : Data) {
do {
var readableJSON = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as! JSONstandard
print(readableJSON)
if let posts = readableJSON["posts"] as? [JSONstandard] {
for post in posts {
let title = post["title"] as! String
print(title)
if let imageUrl = post["image"] as? JSONstandard {
let mainImageURL = URL(string: imageUrl["url"] as! String)
let mainImageData = NSData(contentsOf: mainImageURL!)
let mainImage = UIImage(data: mainImageData as! Data)
postsinput.append(postinput.init(mainImage: mainImage, name: title))
self.tableView.reloadData()
}
}
}
}
catch {
print(error)
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return postsinput.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
// cell?.textLabel?.text = titles[indexPath.row]
let mainImageView = cell?.viewWithTag(2) as! UIImageView
mainImageView.image = postsinput[indexPath.row].mainImage
let mainLabel = cell?.viewWithTag(1) as! UILabel
mainLabel.text = postsinput[indexPath.row].name
return cell!
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
JSON
{
"posts" : [{
"id": "000000",
"url": "/content/interview2",
"date": "2016-11-03 09:01:41",
"modified": "2016-11-03 09:03:47",
"title": "An interview",
"image": "https://www.example.com/sites/default/files/oregood.jpeg",
"summary": {
"value": "<p>Latin text here</p>",
"format": "filtered_html"
}
}]
}
From your JSON response image key contains String not Dictionary also you need to reload your tableView outside for loop not every time inside the loop, so try like this.
if let posts = readableJSON["posts"] as? [JSONstandard] {
for post in posts {
let title = post["title"] as! String
if let imageUrl = post["image"] as? String {
let mainImageURL = URL(string: imageUrl as! String)
let mainImageData = NSData(contentsOf: mainImageURL!)
let mainImage = UIImage(data: mainImageData as! Data)
postsinput.append(postinput.init(mainImage: mainImage, name: title))
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
Suggestion : Instead of downloading image using NSData(contentsOf:) on main thread batter to use library like SDWebImages or you can create your own async image downloader.
because you are not displaying your JSON in table cells. You crate a copied object called "mainImageView" and never assign it as actual imageView of table cell, instead try:
(cell?.viewWithTag(2) as? UIImageView).image = postsinput[indexPath.row].mainImage
postsinput contains image strings or images?
EDIT:
so it would be:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
// cell?.textLabel?.text = titles[indexPath.row]
//let mainImageView = cell?.viewWithTag(2) as! UIImageView
//mainImageView.image = postsinput[indexPath.row].mainImage
(cell?.viewWithTag(2) as? UIImageView).image = postsinput[indexPath.row].mainImage
//let mainLabel = cell?.viewWithTag(1) as! UILabel
//mainLabel.text = postsinput[indexPath.row].name
(cell?.viewWithTag(1) as? UILabel).text = postsinput[indexPath.row].name
return cell!
}
How to set it such that images are downloaded one by one and those that are downloaded are loaded first? Also, how to handle more image downloads as the user scrolls down while purging or clearing those on top?
Here's my code to download those images asynchronously from Firebase:
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("palettes").queryOrdered(byChild: "top").queryEqual(toValue: "#000000").observeSingleEvent(of: .value, with: { (snapshot) in
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict as [String:AnyObject]{
let URL = each.value["URL"] as! String
if let url = NSURL(string: URL) {
if let data = NSData(contentsOf: url as URL){
let image = UIImage(data: data as Data)
self.imageArray.append(image!)
self.collectionView?.reloadData()
}
}
}
}
})
And here's my code for populating collectionView:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let textLabel = cell.viewWithTag(2)
let ootdImage = cell.viewWithTag(4) as! UIImageView
ootdImage.image = imageArray[indexPath.row]
textLabel?.backgroundColor = averageColor
return cell
}
Edit: Right now, as my JSON tree only contains three entries, only three images are downloaded. But they are downloaded altogether and thus I figure it must be the reason why it takes a few seconds before all three images are downloaded and appear in the same instant.
func scrollViewDidScroll(scrollView: UIScrollView)
{
if scrollView.contentSize.height == scrollView.bounds.size.height + scrollView.bounds.origin.y {
//call web service here
}
}
Also add UIScrollViewDelegate in your class.
I found a solution to this:
Using alamofireImage,
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("palettes").queryOrdered(byChild: "top").queryEqual(toValue: "#000000").observeSingleEvent(of: .value, with: { (snapshot) in
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict as [String:AnyObject]{
let URL = each.value["URL"] as! String
self.URLArrayString.append(URL)
print(self.URLArrayString.count)
self.collectionView?.reloadData() //Reloads data after the number and all the URLs are fetched
}
}
})
after fetching all the URLs and total number of URLs, it will reload the cell immediately (images are not yet downloaded at this point, we let alamofireImage handle the image downloads one by one) which brings us to the next code:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let textLabel = cell.viewWithTag(2)
let ootdImage = cell.viewWithTag(4) as! UIImageView
// find out how to make it such that the data is gathered before being displayed.
//ootdImage.image = imageArray[indexPath.row]
let url = NSURL(string: URLArrayString[indexPath.row])
let placeholderImage = UIImage(named: "Rectangle")!
let filter = AspectScaledToFillSizeWithRoundedCornersFilter(
size: ootdImage.frame.size,
radius: 0
)
ootdImage.af_setImage(withURL: url as! URL, placeholderImage: placeholderImage, filter: filter, imageTransition: .crossDissolve(0.2)
)
textLabel?.backgroundColor = averageColor
return cell
}