我正在尝试重新创建这个东西。我在故事板骨架中创建了。这是我的代码的想法:
- 借助函数getThumbnailFromImage从URL的数组中获取图像
- 在数组中添加UIImage和我的缩略图webImages
- 加载项ViewController可重复使用的单元格MyCollectionView
但我在这里(((不要介意没有自动布局(。我做错了什么?我认为问题出在reloadData((上,但我不知道该把它放在哪里
ViewController:
//
// ViewController.swift
// youtube-clone
//
// Created by мас on 16.08.2022.
//
import Foundation
import UIKit
import YouTubePlayer
import AVFoundation
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var url: [URL?] = [
URL(string: "https://www.youtube.com/watch?v=KhebpuFBD14"),
URL(string: "https://www.youtube.com/watch?v=UfNdNrRHpUw"),
URL(string: "https://www.youtube.com/watch?v=CX-BdDHW0Ho"),
URL(string: "https://www.youtube.com/watch?v=NIOMtSzfpck")
]
var webImages: [UIImage] = []
var currentPage: Int = 0
@IBOutlet var myPage: UIPageControl!
@IBOutlet weak var buttonInfo: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
setupLayout()
myPage.currentPage = 0
myPage.numberOfPages = webImages.count
}
// MARK: - Collection View Setup
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return webImages.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCollectionCell
getThumbnailFromImage(url: url[indexPath.row]!, completion: { image in
self.webImages.append(image!)
})
cell.myWebImage.image = webImages[indexPath.row]
cell.myWebImage.layer.cornerRadius = 20
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
myPage.currentPage = indexPath.row
}
// MARK: - Layout Setup // IGNORE IT
func setupLayout() {
buttonInfo.layer.cornerRadius = 25
buttonInfo.imageView!.transform = CGAffineTransform(rotationAngle: 180 * .pi / 180)
self.navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
}
// MARK: - Videos Thumbnail Fetcher
func getThumbnailFromImage(url: URL, completion: @escaping ((_ image: UIImage?) -> Void)) {
DispatchQueue.global().async {
let asset = AVAsset(url: url)
let avAssetImageGenerator = AVAssetImageGenerator(asset: asset)
avAssetImageGenerator.appliesPreferredTrackTransform = true
let thumbnailTime = CMTimeMake(value: 7, timescale: 1)
do {
let cgThumbImage = try avAssetImageGenerator.copyCGImage(at: thumbnailTime, actualTime: nil)
let thumbImage = UIImage(cgImage: cgThumbImage)
DispatchQueue.main.async {
completion(thumbImage)
}
}
catch {
print(error.localizedDescription)
}
}
}
}
可重复使用细胞AKA MyCollectionCell:
import UIKit
class MyCollectionCell: UICollectionViewCell {
@IBOutlet var myWebImage: UIImageView!
}
附言:YouTubePlayer是GitHub的自定义pod,目前尚未使用。
您不必使用AVAssetImageGenerator
,只需使用Youtube API通过视频id将缩略图作为.jpg
图像获取即可,并且每个YouTube视频具有四个生成的图像。
https://img.youtube.com/vi/{id}/0.jpg
https://img.youtube.com/vi/{id}/1.jpg
https://img.youtube.com/vi/{id}/2.jpg
https://img.youtube.com/vi/{id}/3.jpg
示例https://img.youtube.com/vi/KhebpuFBD14/0.jpg
然后,最好使用第三方将此图像加载为列表中显示的图像,如https://github.com/SDWebImage/SDWebImage或https://github.com/onevcat/Kingfisher并且您将不必担心并发性或缓存。
几个想法:
-
@matt在注释中是正确的-当
cellForItemAt
返回时,getThumbnailFromImage
可能还没有调用完成块。 -
根据您发布的代码中可见的内容,当您的集合视图选中
numberOfItemsInSection
时,webImages.count
仍将为0。如果项目数为0,则可能永远不会调用cellForItemAt
,因此甚至无法调用getThumbnailFromImage
。(我不确定屏幕截图中的白框是一个单元格还是另一个视图元素的一部分。如果正在显示一个单元格,我假设在集合视图布局之前,您正在其他地方填充webImages
(。
解决这些问题的一种方法是给每个单元格一个URL,而不是缩略图。这样,可以在图像仍在加载时显示单元格。细胞可能看起来像这样:
class MyCollectionCell: UICollectionViewCell {
@IBOutlet var myWebImage: UIImageView!
func configure(urlString: String) {
guard let self = self, let url = URL(string: urlString) else {
return
}
getThumbnailFromImage(url: url, completion: { [weak self] image in
self?.myWebImage.image = image
})
}
// Move `getThumbnailForImage` function to here, or give the cell a delegate to call back to the VC with if you don't want any networking in the view itself
}
VC中的cellForItemAt
函数需要更改为以下内容:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCollectionCell
cell.configure(urlString: url[indexPath.row])
cell.myWebImage.layer.cornerRadius = 20 // This should probably live in the cell since the parent doesn't actually need to know about it!
return cell
}
这种方法的另一个好处是,如果代码中有错误,你不会引用一个单独的图像阵列,理论上这些图像阵列可能会以错误的顺序结束。您可以完全摆脱webImages
数组,而在numberOfItemsInSection
中使用urls.count
,或者最终从某个地方的API返回的元素数。
附带说明-请确保在引用self
的任何闭包的开头添加[weak self]
,以避免在释放后试图访问它!目前对getThumbnailFromImage
的调用没有:(
另外,请注意,我更改为guard
语句,用于检查URL是否存在。这比强制展开URL(string:(值要安全得多,尤其是当您最终从动态源获取字符串时。