How do I use SDImageView to download URL within a TableViewCell? - swift

I am trying to use the SDImageView Cocoapod with a table view to retrieve a URL from a database and turning it into a viewable image. When I use the code bellow I don't get images in return, what is wrong with my code? Thanks!
var posts = [postStruct]()
override func viewDidLoad() {
super.viewDidLoad()
let ref = Database.database().reference().child("Posts")
ref.observeSingleEvent(of: .value, with: { snapshot in
print(snapshot.childrenCount)
for rest in snapshot.children.allObjects as! [DataSnapshot] {
guard let value = rest.value as? Dictionary<String,Any> else { continue }
guard let title = value["Title"] as? String else { continue }
guard let downloadURL = value["Download URL"] as? String else { continue }
let post = postStruct(title: title, downloadURL: downloadURL)
self.posts.append(post)
}
self.posts = self.posts.reversed(); self.tableView.reloadData()
})
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let imageView = cell?.viewWithTag(200) as! UIImageView
imageView.sd_setImage(with: URL(string: "downloadURL"), placeholderImage: UIImage(named: "placeholder.png"))
let label1 = cell?.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].title
return cell!
}

You need to change SDWebimage syntax as ->
var posts = [postStruct]()
var downloadURL : String = ""
override func viewDidLoad() {
super.viewDidLoad()
let ref = Database.database().reference().child("Posts")
ref.observeSingleEvent(of: .value, with: { snapshot in
print(snapshot.childrenCount)
for rest in snapshot.children.allObjects as! [DataSnapshot] {
guard let value = rest.value as? Dictionary<String,Any> else { continue }
guard let title = value["Title"] as? String else { continue }
downloadURL = value["Download URL"] as? String ?? ""
let post = postStruct(title: title, downloadURL: downloadURL)
self.posts.append(post)
}
self.posts = self.posts.reversed(); self.tableView.reloadData()
})
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let imageView = cell?.viewWithTag(200) as! UIImageView
imageView.sd_setImage(with: URL(string: downloadURL), placeholderImage: UIImage(named: "placeholder.png"))
let label1 = cell?.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].title
return cell!
}
Where downloadURL is url String.

You first need to pick the postStruct from the array and then the downloadURL. Change your override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell method with this.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let imageView = cell?.viewWithTag(200) as! UIImageView
let post = self.posts[indexPath.row];
imageView.sd_setImage(with: URL(string: post.downloadURL), placeholderImage: UIImage(named: "placeholder"))
let label1 = cell?.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].title
return cell!
}

Related

My Images are not loading in the correct order

I have a tableview called (events) where i am pulling images from firebase and loading them, The idea is to post events and add flyer images with the event post. it works perfectly but when i close the app and reopen the images are not in the correct order and some images are on the wrong event. Any help will be greatly appreciated i am very new to coding.
eventsDatabaseHandle = eventsRef?.child("Church Events").observe(.childAdded, with: { (snaphot) in
let eventPost = snaphot.value as! [String: Any]
var event = Event(title: eventPost["eventtitle"] as! String,
timestamp: eventPost["eventdate"] as! String,
location: eventPost["eventlocation"] as! String,
image: nil)
let task = URLSession.shared.dataTask(with: URL(string: eventPost["ImageUrl"] as! String)!) { data, _, error in
if let image: UIImage = UIImage(data: data!) {
event.image = image
DispatchQueue.main.async {
self.events.append(event)
self.tableView.reloadData()
}
}
}
task.resume()
})
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return events.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "events", for: indexPath) as! EventsTableViewCell
let event = events[indexPath.row]
cell.flyerImages.image = event.image
cell.eventTitle.text = event.title
cell.eventDate.text = event.timestamp
cell.eventLocation.text = event.location
Edit your Event object to take imageURL instead of UIImage, and try the following after download this lightweight library which handles downloading and caching your images
eventsDatabaseHandle = eventsRef?.child("Church Events").observe(.childAdded, with: { (snaphot) in
let eventPost = snaphot.value as! [String: Any]
var event = Event(title: eventPost["eventtitle"] as! String,
timestamp: eventPost["eventdate"] as! String,
location: eventPost["eventlocation"] as! String,
imageURL: eventPost["ImageUrl"])
self.events.append(event)
})
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "events", for: indexPath) as! EventsTableViewCell
let event = events[indexPath.row]
if let imgURL = URL(string:event.imageURL)
{
cell.flyerImages.kf.setImage(with:imgURL)
}
cell.eventTitle.text = event.title
cell.eventDate.text = event.timestamp
cell.eventLocation.text = event.location
}

Index out of range custom cell TableViewController

I'm trying to populate 3 custom cells into a TableViewController.
but I always get index out of range error. I`m not sure whats wrong with my code. anyone can help me, I'm newbie in swift.
but when i use 0 for numberOfRowsInSection return, the output is the first cell.
here's my code :
class testResize: UITableViewController {
#objc var comments = [AnyObject]()
#objc var images = [UIImage]()
var getImg = [String]()
override func viewDidLoad() {
super.viewDidLoad()
loadPosts()
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let getCom = comments[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeHeadCell.self), for: indexPath) as! testResizeHeadCell
let user = getCom["nickname"] as! String
let ava = getCom["ava"] as! String
if ava != "" {
let resource = ImageResource(downloadURL: URL(string: ava)!, cacheKey: ava)
cell.avaImg.kf.setImage(with: resource)
}
cell.username.text = user
return cell
}else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeCell.self), for: indexPath) as! testResizeCell
cell.setCustomImage(image: images[indexPath.row])
return cell
}else {
let getCom = comments[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testRezieTextCell.self), for: indexPath) as! testRezieTextCell
let text = getCom["text"] as! String
cell.explaination.text = text
return cell
}
}
here is my load function :
#objc func loadPosts() {
let uuid = "959D1073"
let url = URL(string: "some/url.php")!
self.tableView.reloadData()
var request = URLRequest(url: url)
request.httpMethod = "POST"
let body = "uuid=\(uuid)"
//print(body)
request.httpBody = body.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
if error == nil {
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
self.comments.removeAll(keepingCapacity: false)
self.images.removeAll(keepingCapacity: false)
self.tableView.reloadData()
guard let parseJSON = json else {
print("Error While Parsing")
return
}
guard let posts = parseJSON["posts"] as? [AnyObject] else {
print("Error while parseJSONing")
return
}
self.comments = posts.reversed()
print(self.comments)
for i in 0 ..< self.comments.count {
let path = self.comments[i]["path"] as? String
self.getImg = [path!]
if !path!.isEmpty {
let url = NSURL(string: path!)!
let imageData = try? Data(contentsOf: url as URL)
let image = UIImage(data: imageData! as Data)!
self.images.append(image)
} else {
let image = UIImage()
self.images.append(image)
}
}
self.tableView.reloadData()
//print(posts)
} catch {
print(error)
}
}else{
print(error!)
}
})
}.resume()
}
i think you have a single comment and 3 cell type and when you use indexPath.row happen some thing like this :
for example :
comments = {[{nickname : "mahdi" , ava : "url"} ]}
if indexPath.row == 0 {
let getCom = comments[0]
let user = getCom["nickname"] as! String
let ava = getCom["ava"] as! String
}else if indexPath.row == 1 {
images[1]
}else {
let getCom = comments[2]
let text = getCom["text"] as! String
}
but you have just one comment and when you call comments[1] or commens [2] , you get index out of range error
please try this code :
class testResize:UITableViewController {
#objc var comments = [AnyObject]()
#objc var images = [UIImage]()
var getImg = [String]()
override func viewDidLoad() {
super.viewDidLoad()
loadPosts()
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (self.comments.count == 0 ? 0 : self.comments.count + 2)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let getCom = comments[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeHeadCell.self), for: indexPath) as! testResizeHeadCell
let user = getCom["nickname"] as! String
let ava = getCom["ava"] as! String
if ava != "" {
let resource = ImageResource(downloadURL: URL(string: ava)!, cacheKey: ava)
cell.avaImg.kf.setImage(with: resource)
}
cell.username.text = user
return cell
}else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeCell.self), for: indexPath) as! testResizeCell
cell.setCustomImage(image: images[indexPath.row - 1])
return cell
}else {
let getCom = comments[indexPath.row - 2]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testRezieTextCell.self), for: indexPath) as! testRezieTextCell
let text = getCom["text"] as! String
cell.explaination.text = text
return cell
}
}
and change your numberOfRowInSection :
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return (self.comments.count == 0 ? 0 : self.comments.count + 2)
}
I am assuming that you load your posts asynchronously.
But you do not check if there are actually enough elements in the array. You should check if there are actually enough elements in the array before you access it with a fixed index.
Additionally, you should change your numberOfRows method to the following:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return self.comments.count
}
After you have loaded your posts, you can then call
self.tableView.reloadData()

Firebase tableview not populating, Swift

I have data in my db and can search for an individual record, that's working fine. But when I try to simply populate a tableview with all of the db records its not receiving/displaying any data.
here is my code:
struct drinkStruct {
let pub: String!
let rating: String!
let price: String!
}
override func viewDidLoad() {
super.viewDidLoad()
loadDrinks()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func homeClicked(_ sender: Any) {
homeClicked()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let label1 = cell.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].pub
let label2 = cell.viewWithTag(2) as! UILabel
label2.text = posts[indexPath.row].rating
let label3 = cell.viewWithTag(3) as! UILabel
label3.text = posts[indexPath.row].price
return cell
}
func loadDrinks(){
let databaseRef = Database.database().reference().child("Drinks")
ref = Database.database().reference()
databaseRef.queryOrderedByKey().observe(.childAdded, with: { (snapshot) in
if let valueDictionary = snapshot.value as? [AnyHashable:String]
{
let pub = valueDictionary["pub"]
let rating = valueDictionary["rating"]
let price = valueDictionary["price"]
self.posts.insert(drinkStruct(pub: pub, rating: rating, price: price), at: 0)
}
})
self.tableview.reloadData()
}
And here is my db structure:
Am I doing something blatantly obviously wrong? Or can anyone see what's causing no data to load?
There are no errors/unused variables etc etc.
Thanks in advance!
I think the following should do the job.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
//getting a reference to the node //
databaseRef = Database.database().reference().child("Drinks")
//observing the data changes
databaseRef.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
// clearing the list //
self.posts.removeAll()
// iterating through all the values //
for drinks in snapshot.children.allObjects as! [DataSnapshot] {
let drinkObject = drinks.value as! [String: AnyObject]
let drinkPub = drinkObject["pub"]
let drinkRating = drinkObject["rating"]
let drinkPrice = drinkObject["price"]
//creating a drinkStruct object with the model //
let drinkModel = drinkStruct(pub: drinkPub as! String?, rating: drinkRating as! String?, price: drinkPrice as! String?)
//appending it to list
self.posts.append(drinkModel)
}
// reloading data //
self.tableView.reloadData()
}
})
}
var posts = [drinkStruct]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! YourCustomTableViewCell
let drink: drinkStruct
drink = posts[indexPath.row]
cell.label1.text = drink.pub
cell.label2.text = drink.rating
cell.label3.text = drink.price
return cell
}
}
For the newbie that's here in my footsteps, I solved this by doing a lot of things.
You need to create the tableview & cell layout in the storyboard. Then you need a cell class that dictates/assigns what's happening in each cell(imageviews, labels etc) as well as a model class for whatever you're looking up, whatever the object may be.
This is the code I used for my function in which I populate the info in the cells with the data from Firebase:
func loadDrinks(){
Database.database().reference().child("Drinks").observe(.childAdded) { (snapshot: DataSnapshot) in
if let dict = snapshot.value as? [String: Any] {
let pub = dict["pub"] as! String
let rating = dict["rating"] as! String
let price = dict["price"] as! String
let drink = Drink(pub: pub.capitalized, rating: rating.capitalized, price: price.capitalized)
self.drinks.append(drink)
print(self.drinks)
self.tableview.reloadData()
}
}
}
This was a Newbie 101 question - my bad.

Cell casting throws exception

I am trying to load information to a tableView, and I get an exception because some information in the cell isn't initialized when I cast to it. and this is my code :
The code for the view containing the tableView:
private func populateActiveChats()
{
let loggedOnUserID = Auth.auth().currentUser?.uid
let ref = Constants.refs.databaseChatsLite.child(loggedOnUserID!)
ref.observe(.value, with:
{ (snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot]
{
if (self.chatsDictionary.keys.contains(child.key) == false)
{
let chatValueDictionary = child.value as? NSDictionary
self.AddChatToCollections(chatAsDictionary: chatValueDictionary)
self.DispatchQueueFunc()
}
}
})
}
func AddChatToCollections(chatAsDictionary: NSDictionary!)
{
if chatAsDictionary == nil
{
return
}
let contactName =
chatAsDictionary[Constants.Chat.ChatRoomsLite.CONTACT_NAME] as! String
let newMsgs = chatAsDictionary[Constants.Chat.ChatRoomsLite.NUM_OF_UNREAD_MSGS] as! Int
let contactID = chatAsDictionary[Constants.Chat.ChatRoomsLite.CONTACT_ID] as! String
let chatToAdd = PrivateChatLiteObject(chattingWith: contactName, ContactID: contactID, unreadMessages: newMsgs, LastMSG: "")
chatsDictionary[contactID] = chatToAdd
chatsIndex.append(contactID)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = ChatsTableView.dequeueReusableCell(withIdentifier: "chat_room_cell", for: indexPath) as! PrivateChatUITableViewCell
let indexedID = chatsIndex[indexPath.row]
cell.ContactName.text = chatsDictionary[indexedID]?.GetContactName()
cell.ContactID = chatsDictionary[indexedID]?.GetContactID()
return cell
}
And in my PrivateChatUITableViewCell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = ChatsTableView.dequeueReusableCell(withIdentifier: "chat_room_cell", for: indexPath) as! PrivateChatUITableViewCell
//cell.ContactImageView = UIImageView.loadImageUsingUrlString(contactImg)
let indexedID = chatsIndex[indexPath.row]
cell.ContactName.text = chatsDictionary[indexedID]?.GetContactName()
cell.ContactID = chatsDictionary[indexedID]?.GetContactID()
//cell.PopulateCell()
return cell
}
public func PopulateCell()
{
let currentID = Constants.refs.currentUserInformation?.uid
Constants.refs.databaseChatsLite.child(currentID!).child(ContactID!).observeSingleEvent(of: .value, with: {(snapshot) in ...})
}
The code crashes when it reaches the Constants.refs.databaseChatsLite.child(currentID!).child(ContactID!)
line because ContactID isn't initialized.
This is being called when casting cell to PrivateChatUITableViewCell
I haven't changed my code and this used to work, so I am not sure what changed or what I am doing wrong. Where should my code be fixed?

How to cache the image in swift using SDWebImage?

I am fetching the image from firebase storage and want to cache it using SDWebImage, but in my ViewController it gets keep on downloaded. Please guide me how to cache the images
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! demoCell
cell.textDemo.text = images[indexPath.row]
var images_list = [String]()
images_list.append(images[indexPath.row])
images_list.append(images[indexPath.row] + "1")
let storage = Storage.storage().reference()
var imagesarray = [URL]()
for x in images_list{
let storageRef = storage.child("images/\(x).jpg")
storageRef.downloadURL { (url, error) in
if let error = error{
print(error.localizedDescription)
}
else if let downloadURL = url?.absoluteString{
cell.imageDemo.image = SDImageCache.shared().imageFromCache(forKey: downloadURL)
print("Image Cached")
}
else{
cell.imageDemo.sd_setImage(with: url!, completed: nil)
}
}
}
return cell
}
Rather than writing :
cell.imageDemo.image = SDImageCache.shared().imageFromCache(forKey: downloadURL)
Try this :
cell.imageDemo.sd_setImage(with: URL(string: downloadURL), placeholderImage: UIImage(named: "Your Default Image"))
SDWebImage will handle the caching part here.
This was my solution to the issue
var value : Any?
var vc = ViewController()
var images = [String]()
var downloads_array = [URL]()
override func viewDidLoad() {
super.viewDidLoad()
getImageNames()
downloadImages()
}
func downloadImages(){
let storage = Storage.storage().reference()
for x in images{
let storageRef = storage.child("images/\(x).jpg")
storageRef.downloadURL { (url, error) in
if let error = error{
print(error.localizedDescription)
}
else{
self.downloads_array.append(url!)
self.tableView.reloadData()
}
}
}
}
func getImageNames(){
images = vc.images
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return downloads_array.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! demoCell
cell.textDemo.text = images[indexPath.row]
if let downloadURL = SDImageCache.shared().imageFromCache(forKey: downloads_array[indexPath.row].absoluteString){
cell.imageDemo.image = downloadURL
}
else{
cell.imageDemo.sd_setImage(with: downloads_array[indexPath.row], completed: nil)
}
return cell
}