对象包含从服务器拍摄的照片应重新加载包含对象的 tableView
Objects contain photo taken from server should reload tableView containing the objects
我在 ViewController 中有一个 table 视图,其中包含一个对象数组 Channel
;该数组由保存在我的 CoreData 数据库
中的 json 字符串填充
解析 json 并创建对象 Channel
我可以拥有典型的变量,如 id、频道名称和对话成员;这是对象 Channel
class Channel: NSObject {
var id: String?
var name: String?
var members = [Member]()
var profileImage: UIImage?
override init() {
super.init()
}
init(id: String, name: String, members: [String]) {
super.init()
self.id = id
self.name = name
for idMember in members {
let newMember = Member(id: idMember)
self.members.append(newMember)
}
}
func loadPhoto(id: Int, completion: @escaping (Bool) -> Void) {
Helper.getImage(id: id) { (imageResult) in
if let image = imageResult {
self.profileImage = image
completion(true)
} else {
completion(false)
}
}
}
}
变量 profileImage 应该取自具有 API 的服务器,问题是当创建对象时解析函数 getDataFromCoreData()
中的 json Channel
数组中的对象具有变量 id、名称和成员,但仍然没有图像,因此我的 tableView 将具有各种通道的行,但没有 profileImage,因为异步调用.
在我的例子中,为了重新加载 table 以可视化图像,一个函数 loadImages()
等待异步调用完成并重新加载 tableView
import UIKit
import Firebase
class ConversationsViewController: UIViewController {
var channelList = [Channel]()
override func viewDidLoad() {
super.viewDidLoad()
//I get the channelList from a JSON saved like a string in my coreData with a custom function
channelList = getDataFromCoreData()
loadImages()
}
func loadImages() {
for channel in self.channelList {
channel.loadPhoto(id: channel.id) { success in
if success {
self.chatTable.reloadData()
}
}
}
}
}
extension ConversationsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MessageChannelTableViewCell
var channel = channelList[indexPath.row]
cell.titleLabel.text = channel.name
if let profileImage = channel.profileImage {
cell.profileImage.image = profileImage
} else {
if channel.members.count > 2 {
cell.profileImage.image = theme.groupChatImage
} else {
cell.profileImage.image = UIImage(named: "profile-icon")!
}
}
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return channelList.count
}
}
我知道这个问题可能很长很无聊,但我想知道是否有更好的方法,如果有,你可以告诉我改进我的代码
也许您可以每次都重新加载单个单元格而不是整个 UITableView:
你应该想办法获取每个需要下载图片的单元格的indexPathRow然后:
let indexPosition = IndexPath(row: indexPathRow, section: 0)
tableView.reloadRows(at: [indexPosition], with: .none)
...查看动画的枚举选项以选择适合您应用的选项。
编辑:您可以在 CellForRowAtIndexPath 中测试图像是否为 nil,然后从您的服务器加载它。在成功闭包中重新加载单元格本身......你已经有了 indexPath。使用 weak self 来避免保留循环。
有很多扩展 UIImageView 的库来解决这个问题:
AlamofireImage
Nuke
SDWebImage
KingFisher
在架构上它们与您的解决方案不同的地方是 1) 有一个单独的图像缓存持久保存到磁盘(有时是 NSCache,有时不是),它可以防止重复下载相同的图像,而且还可以让您不必将所有内容存储在数据库(您只需存储 URL)。 2) 扩展在 UIImageView 上,因此图像视图知道如何加载 URL 并在图像视图在完成之前被销毁时取消请求。它们通常提供额外的功能,例如在下载期间使用的占位符图像、调整图像大小等。编写自己的缓存并不难,尽管我几乎总是只使用 pods 之一。这是我为我不想使用 pod 的演示项目编写的一个非常简单的缓存:
class ImageCache {
enum Result {
case success(UIImage, URL), failure (Error)
}
static let shared = ImageCache()
private let cache = NSCache<NSURL, UIImage>()
private init() { }
func image(for url: URL, completion: @escaping (Result) -> ()) {
guard let image = cache.object(forKey: url as NSURL) else {
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard let `self` = self,
let image = UIImage.gif(url: url.absoluteString) else {
completion(.failure(NSError()))
return
}
self.cache.setObject(image, forKey: url as NSURL)
completion(.success(image, url))
}
return
}
completion(.success(image, url))
}
}
我在 ViewController 中有一个 table 视图,其中包含一个对象数组 Channel
;该数组由保存在我的 CoreData 数据库
解析 json 并创建对象 Channel
我可以拥有典型的变量,如 id、频道名称和对话成员;这是对象 Channel
class Channel: NSObject {
var id: String?
var name: String?
var members = [Member]()
var profileImage: UIImage?
override init() {
super.init()
}
init(id: String, name: String, members: [String]) {
super.init()
self.id = id
self.name = name
for idMember in members {
let newMember = Member(id: idMember)
self.members.append(newMember)
}
}
func loadPhoto(id: Int, completion: @escaping (Bool) -> Void) {
Helper.getImage(id: id) { (imageResult) in
if let image = imageResult {
self.profileImage = image
completion(true)
} else {
completion(false)
}
}
}
}
变量 profileImage 应该取自具有 API 的服务器,问题是当创建对象时解析函数 getDataFromCoreData()
中的 json Channel
数组中的对象具有变量 id、名称和成员,但仍然没有图像,因此我的 tableView 将具有各种通道的行,但没有 profileImage,因为异步调用.
在我的例子中,为了重新加载 table 以可视化图像,一个函数 loadImages()
等待异步调用完成并重新加载 tableView
import UIKit
import Firebase
class ConversationsViewController: UIViewController {
var channelList = [Channel]()
override func viewDidLoad() {
super.viewDidLoad()
//I get the channelList from a JSON saved like a string in my coreData with a custom function
channelList = getDataFromCoreData()
loadImages()
}
func loadImages() {
for channel in self.channelList {
channel.loadPhoto(id: channel.id) { success in
if success {
self.chatTable.reloadData()
}
}
}
}
}
extension ConversationsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MessageChannelTableViewCell
var channel = channelList[indexPath.row]
cell.titleLabel.text = channel.name
if let profileImage = channel.profileImage {
cell.profileImage.image = profileImage
} else {
if channel.members.count > 2 {
cell.profileImage.image = theme.groupChatImage
} else {
cell.profileImage.image = UIImage(named: "profile-icon")!
}
}
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return channelList.count
}
}
我知道这个问题可能很长很无聊,但我想知道是否有更好的方法,如果有,你可以告诉我改进我的代码
也许您可以每次都重新加载单个单元格而不是整个 UITableView:
你应该想办法获取每个需要下载图片的单元格的indexPathRow然后:
let indexPosition = IndexPath(row: indexPathRow, section: 0)
tableView.reloadRows(at: [indexPosition], with: .none)
...查看动画的枚举选项以选择适合您应用的选项。
编辑:您可以在 CellForRowAtIndexPath 中测试图像是否为 nil,然后从您的服务器加载它。在成功闭包中重新加载单元格本身......你已经有了 indexPath。使用 weak self 来避免保留循环。
有很多扩展 UIImageView 的库来解决这个问题:
AlamofireImage Nuke SDWebImage KingFisher
在架构上它们与您的解决方案不同的地方是 1) 有一个单独的图像缓存持久保存到磁盘(有时是 NSCache,有时不是),它可以防止重复下载相同的图像,而且还可以让您不必将所有内容存储在数据库(您只需存储 URL)。 2) 扩展在 UIImageView 上,因此图像视图知道如何加载 URL 并在图像视图在完成之前被销毁时取消请求。它们通常提供额外的功能,例如在下载期间使用的占位符图像、调整图像大小等。编写自己的缓存并不难,尽管我几乎总是只使用 pods 之一。这是我为我不想使用 pod 的演示项目编写的一个非常简单的缓存:
class ImageCache {
enum Result {
case success(UIImage, URL), failure (Error)
}
static let shared = ImageCache()
private let cache = NSCache<NSURL, UIImage>()
private init() { }
func image(for url: URL, completion: @escaping (Result) -> ()) {
guard let image = cache.object(forKey: url as NSURL) else {
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard let `self` = self,
let image = UIImage.gif(url: url.absoluteString) else {
completion(.failure(NSError()))
return
}
self.cache.setObject(image, forKey: url as NSURL)
completion(.success(image, url))
}
return
}
completion(.success(image, url))
}
}