I currently have a tableView being loaded from Firebase. This content includes a picture which, when the user scrolls, will change until it settles on its final image. I would assume this would be assigning an image to each cell before it can succesfully load each cell, but have not been able to come up with a work around.
The current code that I have to populate the tableView is as follows:
TableView
import UIKit
import FirebaseAuth
import FirebaseStorage
class Articles: UITableViewController {
var vcType:String = "Home"
var rooms = [Room]()
var articleCell = ArticlesCell()
#IBOutlet weak var menuButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
if self.vcType == "Home"
{
self.rooms += ArticlesManager.sharedClient.rooms
}
else
{
if let obj = ArticlesManager.sharedClient.catRooms[self.vcType.lowercased()] //as? [Room]
{
self.rooms += obj
}
}
self.tableView.reloadData()
ArticlesManager.sharedClient.blockValueChangeInRoomArray = {
newRoom in
if self.vcType == "Home"
{
self.rooms.append(newRoom)
self.rooms.sort(by: {
if $0.created_Date == nil
{
return false
}
if $1.created_Date == nil
{
return true
}
return $0.created_Date.compare($1.created_Date) == ComparisonResult.orderedDescending
})
}
else
{
if self.vcType.lowercased() == newRoom.category
{
self.rooms.append(newRoom)
self.rooms.sort(by: {
if $0.created_Date == nil
{
return false
}
if $1.created_Date == nil
{
return true
}
return $0.created_Date.compare($1.created_Date) == ComparisonResult.orderedDescending
})
self.tableView.reloadData()
}
}
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rooms.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath as NSIndexPath).row == 0 {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "featured", for: indexPath) as! featuredCell
let room = rooms[(indexPath as NSIndexPath).row]
cell2.configureCell(room)
return cell2
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ArticlesCell
let room = rooms[(indexPath as NSIndexPath).row]
cell.configureCell(room)
return cell
}
Data
import Foundation
import Firebase
import FirebaseAuth
let roomRef = FIRDatabase.database().reference()
class Data {
static let dataService = Data()
fileprivate var _BASE_REF = roomRef
fileprivate var _ROOM_REF_ = roomRef.child("rooms")
fileprivate var _BASE_REF2 = roomRef
fileprivate var _ROOM_REF_2 = roomRef.child("contents")
var BASE_REF: FIRDatabaseReference {
return _BASE_REF
}
var ROOM_REF: FIRDatabaseReference {
return _ROOM_REF_
}
var storageRef: FIRStorageReference {
return FIRStorage.storage().reference()
}
var fileURL: String!
func fetchData(_ callback: #escaping (Room) -> ()) {
Data.dataService.ROOM_REF.observe(.childAdded, with: { (snapshot) in
print("snapshot.value - \(snapshot))")
let room = Room(key: snapshot.key, snapshot: snapshot.value as! Dictionary<String, AnyObject>)
callback(room)
})
}
}
TableViewCell
import UIKit
import FirebaseStorage
class featuredCell: UITableViewCell {
#IBOutlet weak var featuredImage: UIImageView!
#IBOutlet weak var featuredTitle: UITextView!
#IBOutlet weak var featuredAuthor: UITextField!
#IBOutlet weak var featuredDate: UITextField!
#IBOutlet weak var featuredContent: UITextView!
class var defaultHeight: CGFloat { get { return ((UIScreen.main.fixedCoordinateSpace.bounds.height - 64) / 5) * 3}}
func configureCell(_ room: Room) {
self.featuredTitle.text = room.title
self.featuredDate.text = room.date
self.featuredAuthor.text = room.author
self.featuredContent.text = room.story
if let imageURL = room.thumbnail {
if imageURL.hasPrefix("gs://") {
FIRStorage.storage().reference(forURL: imageURL).data(withMaxSize: INT64_MAX, completion: { (data, error) in
if let error = error {
print("Error downloading: \(error)")
return
}
self.featuredImage.image = UIImage.init(data: data!)
})
} else if let url = URL(string: imageURL), let data = try? Foundation.Data(contentsOf: url) {
self.featuredImage.image = UIImage.init(data: data)
}
}
}
}
Thank you for your help!
This is a normal behavior of tableviews when working with async downloading during tableview load or scrolling. You've to just clear your image before loading and keep the images datas in Cache when they've downloaded once for a efficient network charge. If you do not use caching, tableview always "re-download" the image when the cell is going to reused. Please see the example;
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
let urle = self.filteredFlats[indexPath.row].flatThumbnailImage?.imageDownloadURL
if let url = urle
{
cell.imgv.image = nil
if let imagefromCache = imageCache.object(forKey: url as AnyObject) as? UIImage
{
cell.imgv.image = imagefromCache
}
else
{
cell.imgv.image = nil
let urlURL = URL(string: (url))
URLSession.shared.dataTask(with: urlURL!, completionHandler: { (data, response, error) in
if error != nil
{
print(error.debugDescription)
return
}
DispatchQueue.main.async {
let imagetoCache = UIImage(data:data!)
self.imageCache.setObject(imagetoCache!, forKey: url as AnyObject)
cell.imgv.image = imagetoCache
}
}).resume()
}
}
return cell
}
this code is copied from my project. So i'm caching the image when it is already downloaded. Also i do not redownload it when cell is reused. Just checking if it is cached or not. So you can use this apporach for a behaviour that satisfy your requirements.
Related
I have a problem with passing the data
To make it clear, I will explain the idea of what I worked on, and what is the problem.
The idea is in the first view controller the user will enter the title and description and then chooses from the options of the pop-up button, the options are (exchange, borrow, donation, sell). The data entered will be saved in the option chosen by the user. then the data will be displayed in the second view controller in the table view. If the user chooses the exchange option and enters the data, his data will be displayed in the table view in the exchange (index 0) and this works for me the data is displayed in the table view in the correct form as I want.
The problem I am experiencing is when I pass the data to the other view controller.
When the user clicks on any cell, it will pass the same data regardless of the difference in the index. If the user chooses the borrow (index 1) and clicks any cell, it'll display the exchange (index 0) data. No matter what indexes you choose and the cell you click on it will pass the same data!!!!!
first view controller
here I'm entering the data
it's shown in the table view in the right index of the segment no problem with that
after I click it pass the right data
look here if I change the index and click to any cell it will pass the same data!!
look here if I change the index and click to any cell it will pass the same data!!
Here's my code for the first vc
import UIKit
import FirebaseFirestore
class ViewController4: UIViewController {
#IBOutlet weak var mssglabel: UILabel!
#IBOutlet weak var selectservice: UIButton!
#IBOutlet weak var titleTextField: UITextField!
#IBOutlet weak var descriptionTextField: UITextView!
#IBOutlet weak var custombtun: UIButton!
let db = Firestore.firestore()
var chooseOption = ""
override func viewDidLoad() {
super.viewDidLoad()
setpopupbutn()
selectservice.layer.cornerRadius = 25
descriptionTextField.layer.cornerRadius = 25
custombtun.layer.cornerRadius = 25
}
#IBAction func containbutn(_ sender: Any) {
let vc = (storyboard?.instantiateViewController(withIdentifier: "vc3"))!
navigationController?.pushViewController(vc, animated: true)
spcificOption()
}
func saveDataDonation() {
if let description = descriptionTextField.text,
let tittle = titleTextField.text{
// Save Data to Database
db.collection("userDonationDatabase")
.addDocument(data: [
"description" : description,
"BookTitle": tittle ]) {
(error) in
if let err = error {
print(err.localizedDescription)
}else {
print("تم حفظ البيانات بنجاح")
print(description)
print(tittle)
}
} // end of closure
}
}
func saveDataSale() {
if let description = descriptionTextField.text,
let tittle = titleTextField.text{
// Save Data to Database
db.collection("userSaleDatabase")
.addDocument(data: [
"description" : description,
"BookTitle": tittle ]) {
(error) in
if let err = error {
print(err.localizedDescription)
}else {
print("تم حفظ البيانات بنجاح")
print(description)
print(tittle) }
}
}
}
func saveDataExchange() {
if let description = descriptionTextField.text,
let tittle = titleTextField.text {
// Save Data to Database
db.collection("userExchangeDatabase")
.addDocument(data: [
"description" : description,
"BookTitle": tittle ]) {
(error) in
if let err = error {
print(err.localizedDescription)
}else {
print("تم حفظ البيانات بنجاح")
print(description)
print(tittle) }
}
}
}
func saveDataBorrow() {
if let description = descriptionTextField.text,
let tittle = titleTextField.text {
// Save Data to Database
db.collection("userBorrowDatabase")
.addDocument(data: [
"description" : description,
"BookTitle": tittle]) {
(error) in
if let err = error {
print(err.localizedDescription)
}else {
print("تم حفظ البيانات بنجاح")
print(description)
print(tittle) }
}
}
}
func setpopupbutn () {
let option = {( ACTION : UIAction ) in
self.chooseOption = ACTION.title
print("حفظ الداتا في ",self.chooseOption)}
selectservice.menu = UIMenu (children : [
UIAction (title : "تبرع" , state: .on , handler: option),
UIAction (title : "بيع" , handler: option),
UIAction (title : "تبادل" , handler: option),
UIAction (title : "إستعارة" , handler: option),
])
saveDataDonation()
selectservice.showsMenuAsPrimaryAction = true
selectservice.changesSelectionAsPrimaryAction = true
}
func spcificOption() {
if chooseOption == ("تبرع") {
saveDataDonation()
} else if chooseOption == ("بيع") {
saveDataSale()
} else if chooseOption == ("تبادل") {
saveDataExchange()
} else if chooseOption == ("إستعارة") {
saveDataBorrow()
}
}
}
and this is the second vc (Table view)
import UIKit
import FirebaseFirestore
import Firebase
class ViewController3: UIViewController, UITableViewDataSource, UITableViewDelegate {
let db = Firestore.firestore()
var exchange : [exchange] = []
var borrow : [borrow] = []
var donation : [donation] = []
var sale : [sale] = []
#IBOutlet weak var segmentOutlet: UISegmentedControl!
#IBOutlet weak var userDataTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
userDataTableView.dataSource = self
userDataTableView.delegate = self
getDataDonation()
getDataSale()
getDataExchange()
getDataBorrow()
userDataTableView.reloadData()
}
#IBAction func serviceSeg(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
getDataExchange()
}
else if sender.selectedSegmentIndex == 1 {
getDataBorrow()
}
else if sender.selectedSegmentIndex == 2 {
getDataDonation()
}
else if sender.selectedSegmentIndex == 3 {
getDataSale()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if segmentOutlet.selectedSegmentIndex == 0 {
return exchange.count
} else if segmentOutlet.selectedSegmentIndex == 1 {
return borrow.count
}else if segmentOutlet.selectedSegmentIndex == 2 {
return donation.count
} else if segmentOutlet.selectedSegmentIndex == 3 {
return sale.count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if segmentOutlet.selectedSegmentIndex == 0 {
cell.textLabel?.text = exchange [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 1 {
cell.textLabel?.text = borrow [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 2 {
cell.textLabel?.text = donation [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 3 {
cell.textLabel?.text = sale [indexPath.row].passTitle
}
return cell
}
func getDataDonation(){
donation.removeAll()
db.collection("userDonationDatabase")
.getDocuments { querySnapshot, error in
if let err = error { print(err.localizedDescription)}
else {
for document in querySnapshot!.documents {
let data = document.data()
print( data["BookTitle"] as! String )
self.donation.append(finalProject.donation(passTitle3:data["BookTitle"] as! String , passDes3: data["description"] as! String))
}
DispatchQueue.main.async {
self.userDataTableView.reloadData()
}
}
}
}
func getDataSale(){
sale.removeAll()
db.collection("userSaleDatabase")
.getDocuments { querySnapshot, error in
if let err = error { print(err.localizedDescription)}
else {
for document in querySnapshot!.documents {
let data = document.data()
print( data["BookTitle"] as! String )
self.sale.append(finalProject.sale(passTitle4:data["BookTitle"] as! String , passDes4: data["description"] as! String))
}
DispatchQueue.main.async {
self.userDataTableView.reloadData()
}
}
}
}
func getDataExchange(){
exchange.removeAll()
db.collection("userExchangeDatabase")
.getDocuments { querySnapshot, error in
if let err = error { print(err.localizedDescription)}
else {
for document in querySnapshot!.documents {
let data = document.data()
print( data["BookTitle"] as! String )
self.exchange.append(finalProject.exchange(passTitle1:data["BookTitle"] as! String , passDes1: data["description"] as! String))
}
DispatchQueue.main.async {
self.userDataTableView.reloadData()
}
}
}
}
func getDataBorrow(){
borrow.removeAll()
db.collection("userBorrowDatabase")
.getDocuments { querySnapshot, error in
if let err = error { print(err.localizedDescription)}
else {
for document in querySnapshot!.documents {
let data = document.data()
print( data["BookTitle"] as! String )
self.borrow.append(finalProject.borrow(passTitle2:data["BookTitle"] as! String , passDes2: data["description"] as! String))
}
DispatchQueue.main.async {
self.userDataTableView.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier:"vc10") as? ViewController10 {
vc.recivedE = exchange[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
}
}
Note... I tried to do this but it didn't work
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier:"vc10") as? ViewController10 {
vc.recivedE = exchange[indexPath.row]
vc.recivedB = borrow[indexPath.row]
vc.recivedD = donation[indexPath.row]
vc.recivedS = sale[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
}
This is the struct,
I created a struct for each index
public struct exchange {
var passTitle : String
var passDes : String
init (passTitle1:String, passDes1:String) {
self.passTitle = passTitle1
self.passDes = passDes1
}
}
public struct borrow {
var passTitle : String
var passDes : String
init (passTitle2:String, passDes2:String) {
self.passTitle = passTitle2
self.passDes = passDes2
}
}
public struct donation {
var passTitle : String
var passDes : String
init (passTitle3:String, passDes3:String) {
self.passTitle = passTitle3
self.passDes = passDes3
}
}
public struct sale {
var passTitle : String
var passDes : String
init (passTitle4:String, passDes4:String) {
self.passTitle = passTitle4
self.passDes = passDes4
}
}
this is the last vc
import UIKit
import FirebaseStorage
import Firebase
import FirebaseFirestore
import SDWebImage
class ViewController10: UIViewController {
#IBOutlet weak var userBookTitle: UILabel!
#IBOutlet weak var userBookDescription: UILabel!
var recivedE:exchange?
var recivedB:borrow?
var recivedD:donation?
var recivedS:sale?
override func viewDidLoad() {
super.viewDidLoad()
userBookTitle.text = recivedE?.passTitle
userBookDescription.text = recivedE?.passDes
}
}
Note... I tried to do this but it didn't work
override func viewDidLoad() {
super.viewDidLoad()
if let et = recivedE?.passTitle ,
let ed = recivedE?.passDes{
userBookTitle.text = et
userBookDescription.text = ed
}
else if let bt = recivedB?.passTitle ,
let bd = recivedB?.passDes {
userBookTitle.text = bt
userBookDescription.text = bd
}
else if let dt = recivedD?.passTitle ,
let dd = recivedD?.passDes {
userBookTitle.text = dt
userBookDescription.text = dd
}
else if let st = recivedS?.passTitle ,
let sd = recivedS?.passDes {
userBookTitle.text = st
userBookDescription.text = sd
}
}
and this also not working
override func viewDidLoad() {
super.viewDidLoad()
userBookTitle.text = recivedE?.passTitle
userBookDescription.text = recivedE?.passDes
userBookTitle.text = recivedB?.passTitle
userBookDescription.text = recivedB?.passDes
userBookTitle.text = recivedD?.passTitle
userBookDescription.text = recivedD?.passDes
userBookTitle.text = recivedS?.passTitle
userBookDescription.text = recivedS?.passDes
}
help me, please
In both of your numberOfRowsInSection and cellForRowAt functions, you are checking the selected segment index to determine which data to use:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if segmentOutlet.selectedSegmentIndex == 0 {
return exchange.count
} else if segmentOutlet.selectedSegmentIndex == 1 {
return borrow.count
}else if segmentOutlet.selectedSegmentIndex == 2 {
return donation.count
} else if segmentOutlet.selectedSegmentIndex == 3 {
return sale.count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if segmentOutlet.selectedSegmentIndex == 0 {
cell.textLabel?.text = exchange [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 1 {
cell.textLabel?.text = borrow [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 2 {
cell.textLabel?.text = donation [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 3 {
cell.textLabel?.text = sale [indexPath.row].passTitle
}
return cell
}
However, in didSelectRowAt, you only use the exchange data:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier:"vc10") as? ViewController10 {
vc.recivedE = exchange[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
}
If you implement the same if / else if structure in didSelectRowAt you should get the desired results.
I am trying to learn firebase.I have two folders on firebase
1)VDBackgroundFrames/
2)VDFrames/
In both folders ,we have 4 images - VDBG2.png,VDBG3.png,VDBG4.png,VDBG5.png.
I am able to access one image at a time from firebase using the following code:-
func firebaseSetUp(){
let store = Storage.storage()
let storeRef = store.reference()
let userProfilesRef = storeRef.child("VDBackgroundFrames/VDBG11.jpg")
userProfilesRef.downloadURL { (url,error) in
if error != nil {
print("error?.localizedDescription",error?.localizedDescription)
return
}else{
print("url",url!)
}
}
}
//==========updated code ====//
func firebaseSetUp(){
let store = Storage.storage()
let storeRef = store.reference()
let userProfilesRef = storeRef.child("VDBackgroundFrames/")
userProfilesRef.observe(.childAdded, with: { [weak self] (snapshot) -> Void in
guard let strongSelf = self else { return }
//Logic to extract urls...
}, changeHandler: (StorageReference, NSKeyValueObservedChange<Value>) -> Void)
}
Output that I am obtaining is as follows:-
URL
https://firebasestorage.googleapis.com/v0/b/celebrations-8edf8.appspot.com/o/VDBackgroundFrames%2FVDBG11.jpg?alt=media&token=ae0910d1-2139-4443-b19a-02edde2f9b17
I actually want to access all the 4 images together from folder VDBackgroundFrames & VDFrames respectively.Kindly suggest the possible way to do it.Any suggestion or guidance would be apprecialble.
Thanks in advance.
Just access the root folder instead of the child directly, that way you'll obtain all the nodes/images in that folder something like this:
func firebaseSetUp(){
let store = Storage.storage()
let storeRef = store.reference()
let userProfilesRef = storeRef.child("VDBackgroundFrames/")
userProfilesRef.observe(.childAdded, with: { [weak self] (snapshot) -> Void in
guard let strongSelf = self else { return }
//Logic to extract urls...
}
}
DownloadURL takes single string at a time. In case you want to show all the files inside a folder to a tableview like me, here is the full code:
import UIKit import Firebase
My very First View Controller-
class FolderList: UIViewController {
var folderList: [StorageReference]?
lazy var storage = Storage.storage()
#IBOutlet weak var tableView : UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.storage.reference().child("TestFolder").listAll(completion: {
(result,error) in
print("result is \(result)")
self.folderList = result.items
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
} }
extension FolderList : UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return folderList?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "FolderListCell", for:
indexPath) as? FolderListCell else {return UITableViewCell()}
cell.itemName.text = folderList?[indexPath.row].name
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 64.0
} }
extension FolderList : UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
guard let downloadVC = storyBoard.instantiateViewController(withIdentifier:
"DownloadedItemView") as? DownloadedItemView else {
return
}
downloadVC.storageRef = folderList?[indexPath.row]
self.navigationController?.pushViewController(downloadVC, animated: true)
}
}
And here is you DownloadedItemView, which will open the images you selected from the list in a view:
import UIKit
import WebKit
import Firebase
class DownloadedItemView: UIViewController {
#IBOutlet weak var webView : WKWebView!
var downloadItemURL : String?
var storageRef : StorageReference?
override func viewDidLoad() {
super.viewDidLoad()
storageRef?.downloadURL(completion: {(downloadURL,error) in
print("url is \(downloadURL)")
DispatchQueue.main.async {
guard let url = downloadURL else {return}
let urlrequest = URLRequest(url: url)
self.webView.load(urlrequest)
}
})
}
}
Your each cell:
class FolderListCell: UITableViewCell {
#IBOutlet weak var itemName : UILabel!
}
I've got a couple a problem with this code. Here's my code and I don't understand why there is an error line 61 with cell.userID = self.user[indexPath.row].userID it says : Cannot assign value of type String? to type String?.Type. It's probably because in line 36 : if let uid = value["profilepicture.userID"] as? String. userID is in Firebase a child of profile picture but I don't know how to write that inside of value[]. Thanks for your answers.
// TableViewCell.swift
import UIKit
class FriendsTableViewCell: UITableViewCell {
#IBOutlet weak var userImage: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
var userID = String?.self
}
// ViewController.swift
import UIKit
import Firebase
class FriendsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableview: UITableView!
var user = [User]()
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func retrieveUsers() {
let ref = Database.database().reference()
ref.child("users").queryOrderedByKey().observeSingleEvent(of: .value, with: { DataSnapshot in
let users = DataSnapshot.value as! [String: AnyObject]
self.user.removeAll()
for (_, value) in users{
//let uid = Auth.auth().currentUser!.uid
if let uid = value["profilepicture.userID"] as? String{
if uid != Auth.auth().currentUser!.uid {
let userToShow = User()
if let fullName = value["username"] as? String , let imagePath = value["profilepicture.photoURL"] as? String {
userToShow.username = fullName
userToShow.imagePath = imagePath
userToShow.userID = uid
self.user.append(userToShow)
}
}
}
}
})
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableview.dequeueReusableCell(withIdentifier: "FriendsTableViewCell", for: indexPath) as! FriendsTableViewCell
cell.nameLabel.text = self.user[indexPath.row].username
cell.userID = self.user[indexPath.row].userID
cell.userImage.downloadImage(from: self.user[indexPath.row].imagePath!)
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return user.count ?? 0
}
}
extension UIImageView{
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()
}
}
Cannot assign value of type String? to type String?.Type.
Change
var userID = String?.self
To
var userID : String?
I'm using firebase to make an iOS app. I want to retrieve all the users on my database and display their name and profile picture in a table view. Here is my code for my TableViewCell:
import UIKit
import FirebaseDatabase
import FirebaseAuth
import SDWebImage
class HomeTableViewCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var profileImageView: UIImageView!
#IBOutlet weak var likeImageView: UIImageView!
#IBOutlet weak var messageImageView: UIImageView!
#IBOutlet weak var likeCountButton: UIButton!
var homeVC: HomeViewController?
var postReference: DatabaseReference!
var post: UserFile?{
didSet {
updateView()
}
}
var user: UserFile? {
didSet {
updateUserInfo()
}
}
override func awakeFromNib() {
super.awakeFromNib()
nameLabel.text = ""
let berryTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleLikeTap))
likeImageView.addGestureRecognizer(berryTapGesture)
likeImageView.isUserInteractionEnabled = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func updateView() {
if let photoURL = post?.picURL {
profileImageView.sd_setImage(with: URL(string: photoURL))
}
API.Post.REF_POSTS.child(post!.id!).observeSingleEvent(of: .value, with: { postSnapshot in
if let postDictionary = postSnapshot.value as? [String:Any] {
let post = UserFile.transformPost(postDictionary: postDictionary, key: postSnapshot.key)
self.updateLike(post: post)
}
})
API.Post.REF_POSTS.child(post!.id!).observe(.childChanged, with: { snapshot in
if let value = snapshot.value as? Int {
self.likeCountButton.setTitle("\(value) berries", for: .normal)
}
})
}
func updateLike(post: UserFile) {
let imageName = post.berries == nil || !post.isBerried! ? "berry" : "berrySelected"
likeImageView.image = UIImage(named: imageName)
// display a message for berries
guard let count = post.berryCount else {
return
}
if count != 0 {
likeCountButton.setTitle("\(count) berries", for: .normal)
} else if post.berryCount == 0 {
likeCountButton.setTitle("Be the first to Like this", for: .normal)
}
}
func incrementberries(forReference ref: DatabaseReference) {
ref.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
if var post = currentData.value as? [String : AnyObject], let uid = Auth.auth().currentUser?.uid {
var berries: Dictionary<String, Bool>
berries = post["berries"] as? [String : Bool] ?? [:]
var likeCount = post["berryCount"] as? Int ?? 0
if let _ = berries[uid] {
// Unlike the post and remove self from stars
likeCount -= 1
berries.removeValue(forKey: uid)
} else {
// Like the post and add self to stars
likeCount += 1
berries[uid] = true
}
post["berryCount"] = likeCount as AnyObject?
post["berries"] = berries as AnyObject?
currentData.value = post
return TransactionResult.success(withValue: currentData)
}
return TransactionResult.success(withValue: currentData)
}) { (error, committed, snapshot) in
if let error = error {
print(error.localizedDescription)
}
if let postDictionary = snapshot?.value as? [String:Any] {
let post = UserFile.transformPost(postDictionary: postDictionary, key: snapshot!.key)
self.updateLike(post: post)
}
}
}
func handleLikeTap() {
postReference = API.Post.REF_POSTS.child(post!.id!)
incrementberries(forReference: postReference)
}
override func prepareForReuse() {
super.prepareForReuse()
profileImageView.image = UIImage(named: "industribune-default-no-profile-pic")
}
func updateUserInfo() {
nameLabel.text = user?.username
if let photoURL = user?.profileImageURL {
profileImageView.sd_setImage(with: URL(string: photoURL), placeholderImage: UIImage(named: "industribune-default-no-profile-pic"))
}
}
}
I am displaying this cell on my HomeViewController:
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import Firebase
class HomeViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
var posts = [UserFile]()
var users = [UserFile]()
override func viewDidLoad() {
super.viewDidLoad()
// for performance set an estimated row height
tableView.estimatedRowHeight = 1
// but also request to dynamically adjust to content using AutoLayout
tableView.rowHeight = UITableViewAutomaticDimension
//tableView.delegate = self
tableView.dataSource = self
loadPosts()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadPosts() {
activityIndicatorView.startAnimating()
API.User.observePosts { (newPost) in
guard let userID = newPost.uid else { return }
self.fetchUser(uid: userID, completed: {
// append the new Post and Reload after the user
// has been cached
self.posts.append(newPost)
self.activityIndicatorView.stopAnimating()
self.tableView.reloadData()
})
}
}
func fetchUser(uid: String, completed: #escaping () -> Void) {
API.User.observeUser(withID: uid) { user in
self.users.append(user)
completed()
}
}
}
extension HomeViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
cell.post = posts[indexPath.row]
cell.user = users[indexPath.row]
cell.homeVC = self
return cell
}
}
I have a lot of craziness going on in my project so let me know if you have any questions and what I'm doing wrong. If it's too complicated to understand I'm ready to erase everything and start over too.
And I do honestly think that I followed all the guidelines to ask a question so don't like shut this question down or something.
That's a lot of code. Try this super reduced example. For this, the users node only stores the name as a child node but it could also have an image, email, address, etc.
Example users node
users
uid_0:
name: "Bert"
uid_1:
name: "Ernie"
and some code
var usersArray = [ [String: Any] ]() //an array of dictionaries.
class ViewController: UIViewController {
//set up firebase references here
override func viewDidLoad() {
super.viewDidLoad()
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let userDict = snap.value as! [String: Any]
self.usersArray.append(userDict)
}
self.tableView.reloadData()
})
and the tableView delegate methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.usersArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
let userDict = self.usersArray[indexPath.row]
cell.text = userDict["name"] as! String
//cell.imge = userDict["image"] etc etc
return cell
}
Now... that all being said. This is the perfect use for an array of UserClass objects instead of the dictionaries.
Here's a starting point....
class UserClass {
var name = ""
var image = ""
func init(snap: DataSnapshot) {
//populate the vars from the snapshot
}
}
var userClassArray = [UserClass]()
Don't copy and paste this as there are probably typos but it should point you in the right direction.
I have an app like instagram that display photos in a tableView.
When the network is slow, image are re used in wrong cells and my dowload indicator label is also reused in wrong cells as i scroll fast.
I tried to use async image loading but can't optimize it.
Here is my cellForRowAtIndexPath method :
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:WallTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as WallTableViewCell
if indexPath.section == timeLineData.count - 1 {
println("load more pics")
loadPost()
}
cell.tag = indexPath.row
// timeLineData is my data array
if timeLineData.count != 0 {
let userPost = timeLineData.objectAtIndex(indexPath.section) as PFObject
cell.commentButton.tag = indexPath.section
// check if image is in the cache
if let imagePostedCache: AnyObject = self.imageCache.objectForKey(userPost.objectId){
dispatch_async(dispatch_get_main_queue(), {
if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) as? WallTableViewCell {
cell.imagePosted.image = imagePostedCache as? UIImage
cell.downloadProgressLabel.hidden = true
}
})
}
// if it is not in the cache get the image from Parse
else if let imagesPost:PFFile = userPost["imageFile"] as? PFFile {
cell.imagePosted.image = nil
cell.downloadProgressLabel.hidden = false
imagesPost.getDataInBackgroundWithBlock({ (imageData:NSData!, error :NSError!) -> Void in
if !(error != nil) {
let image:UIImage = UIImage(data: imageData)!
// add image to the cache
self.imageCache.setObject( image , forKey: userPost.objectId)
// display image in the cell
dispatch_async(dispatch_get_main_queue(), {
if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) as? WallTableViewCell {
cell.imagePosted.image = image as UIImage
}
})
}
else {
println("error")
}
}, progressBlock: { (progressStatus :Int32) -> Void in
cell.downloadProgressLabel.text = "\(progressStatus) %"
if progressStatus == 100 {
cell.downloadProgressLabel.text = ""
}
})
}
// Define description
if cell.tag == indexPath.row {
cell.brandLabel.text = userPost["photoText"] as? String
}
}
return cell
}
Here is my custom cell :
import UIKit
import QuartzCore
class WallTableViewCell: UITableViewCell {
#IBOutlet var downloadProgressLabel: UILabel!
#IBOutlet var commentButton: UIButton!
#IBOutlet var imagePosted: UIImageView!
#IBOutlet var brandLabel: UILabel!
}
WallTableViewCell
import UIKit
import QuartzCore
class WallTableViewCell: UITableViewCell
{
var isDownloadingInProgress
#IBOutlet var downloadProgressLabel: UILabel!
#IBOutlet var commentButton: UIButton!
#IBOutlet var imagePosted: UIImageView!
#IBOutlet var brandLabel: UILabel!
func updateCellWithUserPost(userPost: PFObject)
{
if let imagePostedCache: AnyObject = self.imageCache.objectForKey(userPost.objectId)
{
self.imagePosted.image = imagePostedCache as? UIImage
self.downloadProgressLabel.hidden = true
}
else if let imagesPost:PFFile = userPost["imageFile"] as? PFFile
{
self.imagePosted.image = nil
self.downloadProgressLabel.hidden = false
isDownloadingInProgress = true
imagesPost.getDataInBackgroundWithBlock({ (imageData:NSData!, error :NSError!) -> Void in
isDownloadingInProgress = false
if !(error != nil)
{
let image:UIImage = UIImage(data: imageData)!
self.imageCache.setObject( image , forKey: userPost.objectId)
self.imagePosted.image = image as UIImage
}
else
{
println("Error while downloading image")
}
}, progressBlock: { (progressStatus :Int32) -> Void in
self.downloadProgressLabel.text = "\(progressStatus) %"
if progressStatus == 100
{
cell.downloadProgressLabel.text = ""
}
})
}
}
}
cellForRowAtIndexPath method
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell:WallTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as WallTableViewCell
let userPost = timeLineData.objectAtIndex(indexPath.section) as PFObject
if (!cell.isDownloadingInProgress)
cell.updateCellWithUserPost(userPost)
return cell
}
Note: I don't know Swift too much as I am a pure Objective-C programmer. Let me know if you have any questions regarding the answer
Try using SDWebImages . It will handle images for your application properly .