从 firebase 存储中检索图像以显示在 tableView 上 (Swift)
Retrieve image from firebase storage to show on tableView (Swift)
扩展问题来自
通过将 Firebase 代码移动到 viewDidLoad,Post 的所有文本部分都可以当前显示。但是我还是不能把图片放到tableView中。我检查了图像检索并且成功了,我想我从 Firebase 检索的图像不在 post 数组中。
这是我的修复代码:
import UIKit
import Firebase
import FirebaseStorage
class timelineTableViewController: UITableViewController {
var posts = [Post]()
var databaseRef: FIRDatabaseReference!
var storageRef: FIRStorageReference!
override func viewDidLoad() {
super.viewDidLoad()
databaseRef = FIRDatabase.database().reference()
storageRef = FIRStorage.storage().reference()
let userPostRef = self.databaseRef.child("posts")
userPostRef.queryOrderedByChild("time").observeEventType(.ChildAdded, withBlock: {(snapshot) in
if let postAdd = snapshot.value as? NSDictionary{
let url = snapshot.value?["postPhoto"] as! String
let userPhotoUrl = snapshot.value?["userPhoto"] as! String
let myPost = Post(data: postAdd)
FIRStorage.storage().referenceForURL(url).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
let postPhoto = UIImage(data: data!)
})
FIRStorage.storage().referenceForURL(userPhotoUrl).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
let userPhoto = UIImage(data: data!)
})
self.posts.insert(myPost, atIndex: 0)
self.tableView.reloadData()
}
})
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "postCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)as! timelineTableViewCell
//Dispatch the main thread here
dispatch_async(dispatch_get_main_queue()) {
cell.usernameLabel.text = self.posts[indexPath.row].username
cell.postText.text = self.posts[indexPath.row].postText
cell.timeLabel.text = self.posts[indexPath.row].time
cell.postPhoto.image = self.posts[indexPath.row].postPhoto
cell.userPhoto.image = self.posts[indexPath.row].userPhoto
}
return cell
}
}
还有我的 Post 结构希望对您有所帮助!
struct Post {
var username: String?
var postText: String?
var time: String?
var userPhoto: UIImage?
var postPhoto: UIImage?
var url: String?
var userPhotoUrl:String?
init(data: NSDictionary) {
username = data["username"] as? String
postText = data["postText"] as? String
time = data["time"]as? String
userPhoto = data["userPhoto"] as? UIImage
postPhoto = data["postPhoto"] as? UIImage
url = data["postPhoto"] as? String
userPhotoUrl = data["userPhoto"] as? String
}
}
希望得到指点!
这里的问题似乎是您的 post 从未获取照片信息,因为这些照片是异步下载的。
使用伪代码,你正在做的是:
Get information from database
Create post object
Begin downloading post photo
Populate post object with post photo
Begin downloading user photo
Populate post object with user photo
Return post object
发生的事情是 post 对象在照片下载之前返回(因为 dataWithMaxSize:completion:
调用是异步的)。您最终需要做的事情看起来更像:
Get information from database
Create post object
Begin downloading post photo, etc.
Begin downloading user photo, etc.
Return post object, now that it's fully populated
这是一种通常称为 "callback hell" 的范例,因为您嵌套了异步代码非常深,而且它变得更难阅读和调试。 JavaScript 发明了 Promises 来处理这个问题,但不幸的是 Swift 没有首先 class 支持类似的东西(尽管 guard
是朝着那个方向迈出的一个很好的一步)。有像 Bolts 这样的库使用看起来更同步的异步框架,但同样,代码变得有点复杂。
您可以做一些事情(添加 semaphores/mutexes 并在创建对象之前等待下载完成),但这些都是非常高级的概念,在尝试之前我会掌握 synchronous/asynchronous 编程那些。
删除这两行:
userPhoto = data["userPhoto"] as? UIImage
postPhoto = data["postPhoto"] as? UIImage
更新此代码:
FIRStorage.storage().referenceForURL(url).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
dispatch_async(dispatch_get_main_queue()) {
myPost.postPhoto = UIImage(data: data!)
self.tableView.reloadData()
}
})
FIRStorage.storage().referenceForURL(userPhotoUrl).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
dispatch_async(dispatch_get_main_queue()) {
myPost.userPhoto = UIImage(data: data!)
self.tableView.reloadData()
}
})
只需更改以下方法
func downloadPost(){
let userPostRef = self.databaseRef.child("posts")
userPostRef.queryOrderedByChild("time").observeEventType(.ChildAdded, withBlock: {(snapshot) in
if let postAdd = snapshot.value as? NSDictionary {
print("\(snapshot.value)")
let url = snapshot.value?["postPhoto"] as! String
let userPhotoUrl = snapshot.value?["userPhoto"] as! String
var myPost = Post(data: postAdd) //variable change to "var" because going to modify
let url2 = NSURL(string: url) //postPhoto URL
let data = NSData(contentsOfURL: url2!) // this URL convert into Data
if data != nil { //Some time Data value will be nil so we need to validate such things
myPost.postPhoto = UIImage(data: data!)
}
let url3 = NSURL(string: userPhotoUrl) //userPhoto URL
let data2 = NSData(contentsOfURL: url3!) //Convert into data
if data2 != nil { //check the data
myPost.userPhoto = UIImage(data: data2!) //store in image
}
self.posts.insert(myPost, atIndex: 0) // then add the "myPost" Variable
print(self.posts.count)
}
self.tableView.reloadData() // finally going to load the collection of data in tableview
})
}
它需要很多加载时间。因为转换图像并将其存储在主队列中。
另一种方法是存储 URL 并在 tableview 显示上访问 URL。可以在图像视图中应用异步加载。
扩展问题来自
通过将 Firebase 代码移动到 viewDidLoad,Post 的所有文本部分都可以当前显示。但是我还是不能把图片放到tableView中。我检查了图像检索并且成功了,我想我从 Firebase 检索的图像不在 post 数组中。
这是我的修复代码:
import UIKit
import Firebase
import FirebaseStorage
class timelineTableViewController: UITableViewController {
var posts = [Post]()
var databaseRef: FIRDatabaseReference!
var storageRef: FIRStorageReference!
override func viewDidLoad() {
super.viewDidLoad()
databaseRef = FIRDatabase.database().reference()
storageRef = FIRStorage.storage().reference()
let userPostRef = self.databaseRef.child("posts")
userPostRef.queryOrderedByChild("time").observeEventType(.ChildAdded, withBlock: {(snapshot) in
if let postAdd = snapshot.value as? NSDictionary{
let url = snapshot.value?["postPhoto"] as! String
let userPhotoUrl = snapshot.value?["userPhoto"] as! String
let myPost = Post(data: postAdd)
FIRStorage.storage().referenceForURL(url).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
let postPhoto = UIImage(data: data!)
})
FIRStorage.storage().referenceForURL(userPhotoUrl).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
let userPhoto = UIImage(data: data!)
})
self.posts.insert(myPost, atIndex: 0)
self.tableView.reloadData()
}
})
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "postCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)as! timelineTableViewCell
//Dispatch the main thread here
dispatch_async(dispatch_get_main_queue()) {
cell.usernameLabel.text = self.posts[indexPath.row].username
cell.postText.text = self.posts[indexPath.row].postText
cell.timeLabel.text = self.posts[indexPath.row].time
cell.postPhoto.image = self.posts[indexPath.row].postPhoto
cell.userPhoto.image = self.posts[indexPath.row].userPhoto
}
return cell
}
}
还有我的 Post 结构希望对您有所帮助!
struct Post {
var username: String?
var postText: String?
var time: String?
var userPhoto: UIImage?
var postPhoto: UIImage?
var url: String?
var userPhotoUrl:String?
init(data: NSDictionary) {
username = data["username"] as? String
postText = data["postText"] as? String
time = data["time"]as? String
userPhoto = data["userPhoto"] as? UIImage
postPhoto = data["postPhoto"] as? UIImage
url = data["postPhoto"] as? String
userPhotoUrl = data["userPhoto"] as? String
}
}
希望得到指点!
这里的问题似乎是您的 post 从未获取照片信息,因为这些照片是异步下载的。
使用伪代码,你正在做的是:
Get information from database
Create post object
Begin downloading post photo
Populate post object with post photo
Begin downloading user photo
Populate post object with user photo
Return post object
发生的事情是 post 对象在照片下载之前返回(因为 dataWithMaxSize:completion:
调用是异步的)。您最终需要做的事情看起来更像:
Get information from database
Create post object
Begin downloading post photo, etc.
Begin downloading user photo, etc.
Return post object, now that it's fully populated
这是一种通常称为 "callback hell" 的范例,因为您嵌套了异步代码非常深,而且它变得更难阅读和调试。 JavaScript 发明了 Promises 来处理这个问题,但不幸的是 Swift 没有首先 class 支持类似的东西(尽管 guard
是朝着那个方向迈出的一个很好的一步)。有像 Bolts 这样的库使用看起来更同步的异步框架,但同样,代码变得有点复杂。
您可以做一些事情(添加 semaphores/mutexes 并在创建对象之前等待下载完成),但这些都是非常高级的概念,在尝试之前我会掌握 synchronous/asynchronous 编程那些。
删除这两行:
userPhoto = data["userPhoto"] as? UIImage
postPhoto = data["postPhoto"] as? UIImage
更新此代码:
FIRStorage.storage().referenceForURL(url).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
dispatch_async(dispatch_get_main_queue()) {
myPost.postPhoto = UIImage(data: data!)
self.tableView.reloadData()
}
})
FIRStorage.storage().referenceForURL(userPhotoUrl).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
dispatch_async(dispatch_get_main_queue()) {
myPost.userPhoto = UIImage(data: data!)
self.tableView.reloadData()
}
})
只需更改以下方法
func downloadPost(){
let userPostRef = self.databaseRef.child("posts")
userPostRef.queryOrderedByChild("time").observeEventType(.ChildAdded, withBlock: {(snapshot) in
if let postAdd = snapshot.value as? NSDictionary {
print("\(snapshot.value)")
let url = snapshot.value?["postPhoto"] as! String
let userPhotoUrl = snapshot.value?["userPhoto"] as! String
var myPost = Post(data: postAdd) //variable change to "var" because going to modify
let url2 = NSURL(string: url) //postPhoto URL
let data = NSData(contentsOfURL: url2!) // this URL convert into Data
if data != nil { //Some time Data value will be nil so we need to validate such things
myPost.postPhoto = UIImage(data: data!)
}
let url3 = NSURL(string: userPhotoUrl) //userPhoto URL
let data2 = NSData(contentsOfURL: url3!) //Convert into data
if data2 != nil { //check the data
myPost.userPhoto = UIImage(data: data2!) //store in image
}
self.posts.insert(myPost, atIndex: 0) // then add the "myPost" Variable
print(self.posts.count)
}
self.tableView.reloadData() // finally going to load the collection of data in tableview
})
}
它需要很多加载时间。因为转换图像并将其存储在主队列中。
另一种方法是存储 URL 并在 tableview 显示上访问 URL。可以在图像视图中应用异步加载。