I am able to get it working if the ImageView is in the same View Controller. But I created a custom cell .xib and a model class. All data (text) seems to be transferring, even the URL of the Firebase database image, but the image doesn't change, the default placeholder image is showing. If I don't set the default placeholder image, I will get an error saying that it is nil.. Again, all cells are populating text data from Firebase, the only thing that isn't is the image from the given URL. Debugging shows that the URL does get passed into the "profileImageURL" variable. Here is some of the code:
class Profile{
var name: String = ""
var age: String = ""
var profileImg : UIImageView! = UIImageView.init(image: "main"))
var description : String = ""
}
Here is the table view controller code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCellTableViewCell", for: indexPath) as! CustomCellTableViewCell
let profile = profileArray[indexPath.row]
// Configure the cell...
profileTableView.rowHeight = 300
cell.nameLbl.text = profile.name
cell.descriptionLbl.text = profile.description
cell.profileImage.image = UIImage(named: "main")
return cell
}
func retrievePosts(){
let dataRef = Database.database().reference()
let postsDB = dataRef.child("Messages")
postsDB.observe(.childAdded) { (snapshot) in
let value = snapshot.value as! Dictionary<String,String>
let text = value["postText"]!
let profileImgURL = value["postImage"]!
let userID = value["sender"]!
let url = NSURL(string:"\(profileImgURL)")
let profile = Profile()
profile.description = text
profile.name = name as! String
profile.profileImg.sd_setImage(with: URL(string:profileImgURL), placeholderImage: nil)
self.profileTableView.reloadData()
self.profileArray.insert(profile, at: 0)
})
}
here is the firebase data structure:
- Messages
-L4IkuSxWDnsiJvTKoo0
-postImage: "https://firebasestorage.googleapis.co......"
-postText: "Lorem ipsum dolor sit er elit lamet, consecteta..."
-sender: "C19ghii6OVPNnzJNYPvVJeKuoi73"
bro, the image for any cell is meant to be set in cellForRowAt method. You are getting the raw data because you are fetching it from profile instead of hardcoding, whereas for the image you are setting the image as "main" each time. Hope this helps. – Ashish Sharma
Yup! Thank you! New to App dev. That was it. Had to create a new variable in Profile to hold the url and then in cellForRowAt set the Imageview with that variable URL. There is most likely an easier way to do it but worked for me! Thank you so much! – Guillermo Greco
It's not really the solution, but an alternative:
Use kingfisher pod to set images from URLs.
let url = URL(string: "https://example.com/image.jpg")!
imageView.kf.setImage(with: url)
https://github.com/onevcat/Kingfisher
Related
I have a problem since yesterday morning but I can't figure it out how can I resolve this issue.
I'm having a table view which is using prototype cells, 2 labels and 1 photo. For the labels I used Firestore and for the picture firebase storage.
The problems is that the only way I know how to retrieve photos from my firebase storage is this code
let storage = Storage.storage()
let storageRef = storage.reference()
let ref = storageRef.child("Mancare/Mancare3.jpg")
testImage.sd_setImage(with: ref)
I want to retrieve the photos into my table view, but I do not know how can I can accomplish that.
This is what im using for retrieving the labels with Firestore . I'll paste only the necessary parts of the code :
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return labels.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewTest.dequeueReusableCell(withIdentifier: "CountryTableViewCell", for: indexPath) as! CountryTableViewCell
let storage = Storage.storage()
let storageRef = storage.reference()
let ref = storageRef.child("Mancare/Mancare3.jpg")
let label = labels[indexPath.row]
cell.labelTest.text = label.firstLabel
cell.labelLaba.text = label.secondLabel
return cell
}
func getDatabaseRecords() {
let db = Firestore.firestore()
labels = [] // Empty the array
db.collection("labels").getDocuments { (snapshot, error) in
if let error = error {
print(error)
return
} else {
for document in snapshot!.documents {
let data = document.data()
let newEntry = Labels(
firstLabel: data["firstLabel"] as! String,
secondLabel: data["secondLabel"] as! String)
self.labels
.append(newEntry)
}
}
DispatchQueue.main.async {
self.tableViewTest.reloadData()
}
}
}
This is how I declared the labels:
struct Labels {
let firstLabel: String
let secondLabel: String
}
var labels: [Labels] = []
If someone can help me , ill be forever grateful . Thanks
First, you need to fix your model so it can help you. Add the bucket name to the model like this:
Struct Labels {
let firstLabel: String
let secondLabel: String
let photoKey: String // This will store the bucket name for this `Labels`
}
Now in your getDatabaseRecords change:
let newEntry = Labels(firstLabel: data["firstLabel"] as! String,
secondLabel: data["secondLabel"] as! String),
photoKey: data["photoKey"] as! String) // Added Line
Then in cellForRow:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewTest.dequeueReusableCell(withIdentifier: "CountryTableViewCell", for: indexPath) as! CountryTableViewCell
let label = labels[indexPath.row]
let storageRef = Storage.storage().reference()
let photoRef = storageRef.child(label.photoKey)
cell.labelTest.text = label.firstLabel
cell.labelLaba.text = label.secondLabel
cell.imageView.sd_setImage(with: photoRef) // Assuming the image view in your cell is named this
return cell
}
Last, make sure your document structure matches the new Labels Model in the firebase console, and you have images as well in the root of your storage that match with all the photoKeys. Btw, Labels is not a very good model name, I just went with it for consistency
I create a struct in a ViewController file which has the properties url, img, and name. Here is that code:
struct Article {
var url: URL
var img: URL
var title: String
init(url: URL, img: URL, title: String) {
self.url = url
self.img = img
self.title = title
}
}
When the user presses a button, I create an instance of the struct, assign data to the properties, and put it in an array called bookmarks. Then in a TableView file, I want to loop through bookmarks and grab each struct's url, img, and name so I can assign them to labels on each cell.
The trouble I am having is I do not know how to access each struct's properties inside bookmarks.
If anyone could help me accomplish this, that would be amazing. Any help will be appreciated! Thanks so much in advance. Cheers, Theo
You can access the struct's properties with a dot . followed by the property name.
Here is an example:
let article = Article(url: URL(string: ""), img: URL(string: ""), title: "hello")
let theTitle = article.title // equal to "hello"
In your case, you want to grab the information for a table view. Assuming you have an array of Article named bookmarks, simply index into this array in the table view's cellForRowAt indexPath data source method:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "yourIdentifierHere")
let article = bookmarks[indexPath.row]
let title = article.title
cell.titleLabel.text = title
return cell
}
This data source method is called on your behalf, so it "loops" through the data array automatically.
I've had a great help in creating a functional image cache for a UITableViewCell in cellForRowAtIndex. Unfortunately, with the code below, only one image is displayed over and over. I was under the impression that cellForRowAtIndexPath was like a for loop, running again for each row. Therefore, I'm wondering why only one image is displayed.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "restaurantcell") as? RestaurantTableCell
var oneRestaurant: Restaurant = tablerestaurantarray[indexPath.row]
if let cachedVersion = cache.object(forKey: "image") {
oneRestaurant = cachedVersion
} else {
cache.setObject(oneRestaurant, forKey: "image")
}
cell?.picture?.image = oneRestaurant.value(forKey: "image") as! UIImage?
let restaurant = restaurantArray[indexPath.row]
cell?.name?.text = restaurant.value(forKey: "Name") as? String
return cell!
}
Update 2:
Results from the added breakpoint
You use the same NSCache key ("image") for different objects. That's why only the first Restaurant object is saved into the cache. For all other cells you look for the object cached for key "image" and get the same previously saved Restaurant object back.
You have to use different keys for caching different Restaurant objects. Try to append the index path to the cache key:
let key = "restaurant \(indexPath)"
if let cachedVersion = cache.object(forKey: key) {
oneRestaurant = cachedVersion
} else {
cache.setObject(oneRestaurant, forKey: key)
}
I don't quite understand why you want to cache restaurant objects though. You already have them in the tablerestaurantarray, so there won't be any benefit caching them. Maybe your intention was caching the images?
I have created a fully functioning tableview that populates its data from a text array (String) and an image array (PFFile). I have also implemented a search bar that displays the filtered results based on the created text array.
var fruitArray = [String]()
var imageFile = [PFFile]()
the problem is that fruitArray[indexPath.row] is filtered according to the input in the search bar but, the search bar cannot filter anything from imageFile and imageFile[indexPath.row] is displayed as if nothing has been searched.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print(fruitArray[indexPath.row])
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! TableViewCell
if searchController.active && searchController.searchBar.text != "" {
cell.labelFruitName?.text = searchResults[indexPath.row]
} else {
cell.labelFruitName?.text = fruitArray[indexPath.row]
}
let placeHolder = UIImage(named: "plchlder.png")
cell.fruitImages?.image = placeHolder
imageFile[indexPath.row].getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
cell.fruitImages?.image = downloadedImage
}
}
return cell
}
as a result, the images won't change inside the cell. in other words, before searching anything, if the first cell shows a text and picture of an apple, after the search, the first cell will always display an apple but the text and number of cells change.
Any solutions would be appreciated. Thanks in advance
I would suggest you create an array of type [fruit] , where fruit is just a struct with an image and a string. Then you can filter out the bad apples.
Here is the code for the struct:
struct Fruit {
let name: String
let image: PFFile
}
Then in your searchResultsUpdating function just filter out your search term using a simple filter:
results = fruitArray.filter{$0.name.containsString(searchController.searchBar.text!)}
I have an optional image in the cloudkit DB(checked DB and the image is there in cases where I added it in my testing). I have created a class that initializes the record fields into variables I use in my tableview. I have a custom cell as well. But the image won't display in my custom tableview cell. I don't know if having an optional image in a tableview image is causing a problem or if there's an issue with my code/settings in cloudkit.
Any help is greatly appreciated, as I've been stuck for over a week and there's little to nothing online about this.
Here's my class code:
var feedResults = [UserPosts]()
class UserPosts {
var record: CKRecord
var description: String
var username: String
//var image: CKAsset? // tried also initializing the CKAsset separately
var postPic: UIImage?
init(record: CKRecord) {
self.record = record
self.description = record.objectForKey("description") as String
self.username = record.objectForKey("username") as String
// self.image = record.objectForKey("postPic") as? CKAsset
if var pic = record.objectForKey("postPic") as CKAsset! {
self.postPic = UIImage(contentsOfFile: pic.fileURL.path!)
// below is the other way I've seen it done.
// var url = pic.fileURL!
// var imageData = NSData(contentsOfFile: url.path!)
// self.postPic = UIImage(data: imageData!)
}
}
}
Here's my tableview code:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as TableViewCell
let record = feedResults[indexPath.row]
cell.postDescription.text = record.description
cell.userName.text = record.username
if record.postPic != nil {
cell.postPic.image = record.postPic!
}
return cell
}
Also I've seen a couple ways of pulling a CKAsset into a UIImage. The first way, is how I saw Apple do it in their CloudKitAtlas project. Although I'm not well versed in Obj-C, I think I followed it correctly - CloudKitAtlas Example
I've also seen it done using NSData(contentOFFile:) and UIImage(data:), as shown in this post here: SO CKAsset Example
Try doing it without the .paths and using contentsOfURL. This is how I do this (within your if for the CKAsset):
if var data = NSData(contentsOfURL: pic!.fileURL) {
self.postPic = UIImage(data: data!)!
}
And besides that... You do a dequeueReusableCellWithIdentifier but don't check if it returns nil. You should create a new cell instance If it was nil
You can display the image from the CKAsset like this:
var img = record.valueForKey("Picture") as? CKAsset
cell.imageView?.image = UIImage(contentsOfFile: img!.fileURL.path!)
Hope this helps!