У меня странное поведение в табличном представлении внутри моего приложения tvOS. Мое приложение просто отображает список изображений для некоторых телешоу / фильмов вместе с названием серии и названием эпизода. У меня есть пользовательский класс ячейки представления таблицы, созданный под названием BMContentCell. Вот весь файл:
import UIKit
class BMContentCell: UITableViewCell
{
private var imageCache: NSCache<NSString, UIImage>? // key: string url pointing to the image on the internet, value: UIImage object
private let kCellContentFormat = "VOD: %@ | DRM: %@ | Auth: %@"
private let kPosterContent = "poster"
private var contentImageView: UIImageView =
{
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
imageView.layer.cornerRadius = 15
imageView.clipsToBounds = true
imageView.layer.borderColor = UIColor.clear.cgColor
imageView.layer.borderWidth = 1
return imageView
}()
private let contentTitle: UILabel =
{
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 32)
label.textColor = UIColor.black
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let contentSubtitle: UILabel =
{
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 26)
label.textColor = UIColor.black
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let contentAttributes: UILabel =
{
let label = UILabel()
label.backgroundColor = .white
label.font = UIFont.systemFont(ofSize: 20)
label.textColor = UIColor.black
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func awakeFromNib()
{
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?)
{
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = .white
addSubview(contentImageView)
addSubview(contentTitle)
addSubview(contentSubtitle)
addSubview(contentAttributes)
contentImageView.anchor(
top: self.topAnchor,
leading: self.leadingAnchor,
bottom: self.bottomAnchor,
trailing: contentTitle.leadingAnchor,
padding: .init(top: 0, left: 0, bottom: 0, right: 0),
size: .init(width: 180, height: 180)
)
contentTitle.anchor(
top: self.topAnchor,
leading: contentImageView.trailingAnchor,
bottom: contentSubtitle.topAnchor,
trailing: self.trailingAnchor,
size: CGSize.init(width: 0, height: 60)
)
contentSubtitle.anchor(
top: contentTitle.bottomAnchor,
leading: contentImageView.trailingAnchor,
bottom: contentAttributes.topAnchor,
trailing: self.trailingAnchor,
padding: .zero,
size: CGSize.init(width: 0, height: 60)
)
contentAttributes.anchor(
top: contentSubtitle.bottomAnchor,
leading: contentImageView.trailingAnchor,
bottom: self.bottomAnchor,
trailing: self.trailingAnchor,
padding: .zero,
size: CGSize.init(width: 0, height: 60)
)
}
required init?(coder aDecoder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}
internal func configureCell(content: BMContent)
{
self.contentTitle.text = content.media.name
self.contentSubtitle.text = content.name
self.contentAttributes.text = self.contentCellAttributes(
String(!content.isLiveStream()),
String(content.isDrm),
String(content.authentication.required)
)
var posterImageContent = content.media.images.filter { $0.type == kPosterContent }
DispatchQueue.global(qos: .userInitiated).async
{
if posterImageContent.count > 1
{
posterImageContent.sort { $0.url < $1.url }
self.loadImageURLFromCache(imageURLStr: posterImageContent[0].url)
}
else if posterImageContent.count == 1
{
self.loadImageURLFromCache(imageURLStr: posterImageContent[0].url)
}
}
}
private func contentCellAttributes(_ isLiveStream: String, _ isDRM: String, _ isAuthRequired: String) -> String
{
return String(format: kCellContentFormat, isLiveStream, isDRM, isAuthRequired)
}
private func loadImageURLFromCache(imageURLStr: String)
{
// check if the url (key) exists in the cache
if let imageFromCache = imageCache?.object(forKey: imageURLStr as NSString)
{
// the image exists in cache, load the image and return
DispatchQueue.main.async {self.contentImageView.image = imageFromCache as UIImage}
return
}
guard let url = URL(string: imageURLStr) else
{
// if the url is not valid, load a blank UIImage
self.imageCache?.setObject(UIImage(), forKey: imageURLStr as NSString)
DispatchQueue.main.async {self.contentImageView.image = UIImage()}
return
}
let session = URLSession.shared
let task = session.dataTask(with: url)
{
data, response, error in
if let data = data
{
guard let imageFromURL = UIImage(data: data) else
{
// if we cannot create an image from the data for some reason, load a blank UIImage
let placeholderImage = UIImage()
self.imageCache?.setObject(placeholderImage, forKey: imageURLStr as NSString)
DispatchQueue.main.async {self.contentImageView.image = placeholderImage}
return
}
// load the image and set the key and value in the cache
DispatchQueue.main.async {self.contentImageView.image = imageFromURL}
self.imageCache?.setObject(imageFromURL, forKey: imageURLStr as NSString)
}
}
task.resume()
}
}
Если вы посмотрите на метод configureCell, он ищет URL-адрес изображения (который является ключом для объекта NSCache в этом классе) и проверяет кэш, если этот URL-адрес уже существует. Если это так, он просто загружает UIImage, в противном случае он идет и извлекает его из inte rnet.
Контроллер представления, в котором находится табличное представление, называется BMContentViewController. Метод cellForRowAt внутри этого класса выглядит следующим образом:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: kReuseIdentifier, for: indexPath) as! BMContentCell
if self.viewModel.currentContent.count > 0
{
cell.configureCell(content: self.viewModel.currentContent[indexPath.row])
}
return cell
}
Каждый раз, когда я прокручиваю вверх или вниз в своем табличном представлении в приложении tvOS, это очень изменчиво. И также кажется, что изображения изначально кажутся неправильными для любого данного телешоу / движения ie, и после нескольких миллисекунд обновляется правильное изображение.
По сути, мой вопрос заключается в том, что является лучшим способом Подходите к вопросу кэширования и получения изображений в виде таблицы внутри моего приложения?
Спасибо!