我是iOS开发初学者,想做一个instagram clone app,在制作instagram clone app的news feed时遇到了问题。
所以我使用 Firebase 来存储图像和数据库。发布图片(将数据上传到 Firebase)后,我想使用从我的 Firebase 上传的数据填充表格 View 。
但是当我运行该应用程序时, Storyboard中的虚拟图像和标签与我放入表格 View 中的下载数据重叠。我下载的数据最终会在我向下滚动后显示。
这是我运行应用程序时的 gif:
http://g.recordit.co/iGIybD9Pur.gif
.gif 中显示了 3 个用户
- 用户名( Storyboard中的虚拟人)
- 佐科维里
- 梅加瓦蒂研究所
从 Firebase 异步下载图像后(加载指示器消失后),我希望 MegawatiRI 会显示在表格顶部,但虚拟图像会先显示,但在我向下滚动并返回顶部后, MegawatiRI 最终会出现。
我相信 MegawatiRI 已成功下载,但我不知道为什么虚拟图像似乎与实际数据重叠。我不希望在我的应用程序运行时显示虚拟对象。
这是原型(prototype)单元格的屏幕截图:
下面是 TableView Controller 的简化代码:
class NewsFeedTableViewController: UITableViewController {
var currentUser : User!
var media = [Media]()
override func viewDidLoad() {
super.viewDidLoad()
tabBarController?.delegate = self
// to set the dynamic height of table view
tableView.estimatedRowHeight = StoryBoard.mediaCellDefaultHeight
tableView.rowHeight = UITableViewAutomaticDimension
// to erase the separator in the table view
tableView.separatorColor = UIColor.clear
}
override func viewWillAppear(_ animated: Bool) {
// check wheter the user has already logged in or not
Auth.auth().addStateDidChangeListener { (auth, user) in
if let user = user {
RealTimeDatabaseReference.users(uid: user.uid).reference().observeSingleEvent(of: .value, with: { (snapshot) in
if let userDict = snapshot.value as? [String:Any] {
self.currentUser = User(dictionary: userDict)
}
})
} else {
// user not logged in
self.performSegue(withIdentifier: StoryBoard.showWelcomeScreen, sender: nil)
}
}
tableView.reloadData()
fetchMedia()
}
func fetchMedia() {
SVProgressHUD.show()
Media.observeNewMedia { (mediaData) in
if !self.media.contains(mediaData) {
self.media.insert(mediaData, at: 0)
self.tableView.reloadData()
SVProgressHUD.dismiss()
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: StoryBoard.mediaCell, for: indexPath) as! MediaTableViewCell
cell.currentUser = currentUser
cell.media = media[indexPath.section]
// to remove table view highlight style
cell.selectionStyle = .none
return cell
}
}
下面是表格 View 单元格的简化代码:
class MediaTableViewCell: UITableViewCell {
var currentUser: User!
var media: Media! {
didSet {
if currentUser != nil {
updateUI()
}
}
}
var cache = SAMCache.shared()
func updateUI () {
// check, if the image has already been downloaded and cached then just used the image, otherwise download from firebase storage
self.mediaImageView.image = nil
let cacheKey = "\(self.media.mediaUID))-postImage"
if let image = cache?.object(forKey: cacheKey) as? UIImage {
mediaImageView.image = image
} else {
media.downloadMediaImage { [weak self] (image, error) in
if error != nil {
print(error!)
}
if let image = image {
self?.mediaImageView.image = image
self?.cache?.setObject(image, forKey: cacheKey)
}
}
}
那么是什么让虚拟图像与我下载的数据重叠?
最佳答案
回答
出现虚拟图像是因为您的 TableView Controller 在您的当前用户在 tableViewController 上正确设置之前开始渲染单元格。
因此,在第一次调用 cellForRowAtIndexPath 时,您的 Controller 中可能有一个 nil currentUser,它被传递给单元格。因此,您的单元格类中的 didSet 属性观察器不会调用 updateUI():
didSet {
if currentUser != nil {
updateUI()
}
}
稍后,您重新加载数据并且当前用户已经设置,所以事情开始按预期工作。
updateUI() 中的这一行应该隐藏您的虚拟图像。但是,updateUI 并不总是像上面解释的那样被调用:
self.mediaImageView.image = nil
我真的不明白 updateUI 需要当前用户不是 nil 的原因。所以你可以在你的 didSet 观察者中消除 nil 测试,并且总是调用 updateUI:
var media: Media! {
didSet {
updateUI()
}
或者,您可以重新安排您的 TableView Controller ,以便在加载数据源之前真正等待当前用户被设置。 viewWillAppear 中与登录相关的代码具有嵌套的完成处理程序来设置当前用户。这些可能是异步执行的..所以你要么必须等待它们完成,要么处理当前用户为零的情况。
Auth.auth etc {
// completes asynchronously, setting currentUser
}
// Unless you do something to wait, the rest starts IMMEDIATELY
// currentUser is not set yet
tableView.reloadData()
fetchMedia()
其他说明
(1) 我认为当图像下载并插入共享缓存时重新加载单元格(使用 reloadRows)是一种很好的形式。可以引用这个question里面的答案查看从单元启动的异步任务如何使用 NotificationCenter 或委托(delegate)联系 tableViewController。
(2) 我怀疑您的图片下载任务目前正在主线程中运行,这可能不是您想要的。当您修复该问题时,您将需要切换回主线程以更新图像(正如您现在所做的那样)或重新加载该行(正如我在上面所建议的那样)。
关于ios - 界面生成器中的图像和标签与 TableView 单元格中的数据重叠,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46930379/