如何动态添加正确数量的单元格并在其中显示视频。

huangapple go评论78阅读模式
英文:

How would I dynamically add the right amount of cells and display videos in them

问题

下面是您要翻译的代码部分:

import UIKit
import AVKit
import Firebase

struct CustomData {
    var title: String
    var image: UIImage
    var url: String
}

var videoURL = ""
var array = [String]()

class HomeViewController: UIViewController {
    
    var fileArray: Array<Any>?
    
    fileprivate let data = [
        CustomData(title: "Test", image: #imageLiteral(resourceName: "ss-1"), url: "test.com"),
        CustomData(title: "Test2", image: #imageLiteral(resourceName: "done-button"), url: "test.com"),
        CustomData(title: "Test2", image: #imageLiteral(resourceName: "notificationIcon"), url: "test.com")
    ]
    
    fileprivate let collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
        cv.translatesAutoresizingMaskIntoConstraints = false
        cv.register(CustomCell.self, forCellWithReuseIdentifier: "cell")
        return cv
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(collectionView)
        collectionView.backgroundColor = .white
        collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 150).isActive = true
        collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true
        collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true
        collectionView.heightAnchor.constraint(equalTo: collectionView.widthAnchor, multiplier: 0.5).isActive = true
        
        collectionView.delegate = self
        collectionView.dataSource = self
        
        array = fileArray as! [String]
    }
}

extension HomeViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: collectionView.frame.width, height: collectionView.frame.width)
    }
    
    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return data.count
    }
    
    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CustomCell
        cell.data = self.data[indexPath.row]
        return cell
    }
}

class CustomCell: UICollectionViewCell {
    
    var data: CustomData? {
        didSet {
            guard let data = data else { return }
            bg.image = data.image
        }
    }
    
    fileprivate let bg: UIImageView = {
        let iv = UIImageView()
        iv.image = #imageLiteral(resourceName: "splash_icon")
        iv.translatesAutoresizingMaskIntoConstraints = false
        iv.contentMode = .scaleAspectFill
        iv.clipsToBounds = true
        iv.layer.cornerRadius = 10
        return iv
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        // 以下代码用于显示视频,您需要根据您的需求来更改
        /*
        let player = AVPlayer(url: URL(string: videoURL)!)
        let playerLayer = AVPlayerLayer(player: player)
        playerLayer.frame = contentView.bounds
        contentView.layer.addSublayer(playerLayer)
        player.play()
        */
        
        contentView.addSubview(bg)
        bg.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        bg.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
        bg.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
        bg.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

请注意,代码中有一部分注释,用于显示视频。您需要根据您的需求来取消注释并更改视频的URL。

英文:

At the moment I have a swift file where I have three hardcoded values using a datamodel I also created that takes a title, image and url (I'm only showing the image at the moment). I understand how to change each cell to show its respective image that it should show in the scrollview:

import UIKit
import AVKit
import Firebase
struct CustomData {
var title: String
var image: UIImage
var url: String
}
var videoURL = &quot;&quot;
var array = [String]()
class HomeViewController: UIViewController {
var fileArray:Array&lt;Any&gt;?
fileprivate let data = [
CustomData(title: &quot;Test&quot;, image: #imageLiteral(resourceName: &quot;ss-1&quot;), url: &quot;test.com&quot;),
CustomData(title: &quot;Test2&quot;, image: #imageLiteral(resourceName: &quot;done-button&quot;), url: &quot;test.com&quot;),
CustomData(title: &quot;Test2&quot;, image: #imageLiteral(resourceName: &quot;notificationIcon&quot;), url: &quot;test.com&quot;)
]
fileprivate let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.register(CustomCell.self, forCellWithReuseIdentifier: &quot;cell&quot;)
return cv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
collectionView.backgroundColor = .white
collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 150).isActive = true
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true
collectionView.heightAnchor.constraint(equalTo: collectionView.widthAnchor, multiplier: 0.5).isActive = true 
collectionView.delegate = self
collectionView.dataSource = self
array = fileArray as! [String]
}
}
extension HomeViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource{
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -&gt; CGSize {
return CGSize(width: collectionView.frame.width, height: collectionView.frame.width)
}
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -&gt; Int {
return data.count
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -&gt; UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: &quot;cell&quot;, for: indexPath) as! CustomCell
cell.data = self.data[indexPath.row]
return cell
}
}
class CustomCell: UICollectionViewCell{
var data: CustomData?{
didSet{
guard let data = data else { return }
bg.image = data.image
}
}
fileprivate let bg: UIImageView = {
let iv = UIImageView()
iv.image = #imageLiteral(resourceName: &quot;splash_icon&quot;)
iv.translatesAutoresizingMaskIntoConstraints = false
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.layer.cornerRadius = 10
return iv
}()
override init(frame: CGRect) {
super.init(frame: frame)
//        let player = AVPlayer(url: URL(string: videoURL)!)
//        let playerLayer = AVPlayerLayer(player: player)
//        playerLayer.frame = contentView.bounds
//        contentView.layer.addSublayer(playerLayer)
//        player.play()
contentView.addSubview(bg)
bg.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
bg.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
bg.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
bg.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError(&quot;init(coder:) has not been implemented&quot;)
}
}

The code above successfully shows all the three images I specified in the data array in the scroll view in their respective cell.
I have an array that holds 10 strings which are all unique urls locally on the device to different videos (all of the same size). I have managed to display a video by changing this part:

override init(frame: CGRect) {
super.init(frame: frame)
//        let player = AVPlayer(url: URL(string: videoURL)!)
//        let playerLayer = AVPlayerLayer(player: player)
//        playerLayer.frame = contentView.bounds
//        contentView.layer.addSublayer(playerLayer)
//        player.play()
contentView.addSubview(bg)
bg.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
bg.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
bg.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
bg.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
}

to this:

override init(frame: CGRect) {
super.init(frame: frame)
let player = AVPlayer(url: URL(string: videoURL)!)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = contentView.bounds
contentView.layer.addSublayer(playerLayer)
player.play()
//        contentView.addSubview(bg)
//        bg.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
//        bg.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
//        bg.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
//        bg.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
}

but that obviously hardcodes in one video and that same video will be shown on all of the cells in the scroll view. So how would I do the same as I did with the UIImageView:

var data: CustomData?{
didSet{
guard let data = data else { return }
bg.image = data.image
}
}
fileprivate let bg: UIImageView = {
let iv = UIImageView()
iv.image = #imageLiteral(resourceName: &quot;splash_icon&quot;)
iv.translatesAutoresizingMaskIntoConstraints = false
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.layer.cornerRadius = 10
return iv
}()

but with AVPlayer or some other video player to make all the cells dynamically show the different videos, I know I'll have to change the datamodel and get values from the array etc, I just can't seem to find a way to do the same as I did with the image view.

Thanks,

Nathan

答案1

得分: 1

因为你在设置data.image到你的UIImageView的代码块中,所以在查看你的代码时,你成功地让图像显示在你想要的位置。

如果你想在每个单元格中播放列出的视频,你需要使用data.url来初始化AVP播放器。

为了不要太乱你的代码,你可以尝试这样做:

 override init(frame: CGRect) {
        super.init(frame: frame)

      // 仅在data.url存在且是有效的URL而不仅仅是一堆普通文本时才创建播放器
      if let unwrappedURLString = data?.url, let url = URL(string: unwrappedURLString) {
        let player = AVPlayer(url: url)
        let playerLayer = AVPlayerLayer(player: player)
        playerLayer.frame = contentView.bounds
        contentView.layer.addSublayer(playerLayer)
        player.play()
      }
    } 

即使你想要像初始化UIImageView一样初始化它,仍然有许多问题需要解决和错误需要处理,当编译时,以下代码将不会编译:

fileprivate let player: AVPlayer = {
    let dataURL = data?.url ?? "random URL" // 这将无法编译,因为data是一个实例变量,在初始化内存加载时不可用
    let url = URL(string: initialURL) // 这一行仍然会产生一个可选的URL,所以你需要在这里构建URL时处理解包问题
    let player = AVPlayer(url: url)
    return player
  }()
英文:

Taking a look at your codes, you managed to get the images showing as you wanted because of this codeblock where you are setting data.image into your UIImageView

var data: CustomData?{
didSet{
guard let data = data else { return }
bg.image = data.image
}
}

If you want to play the videos listed in your data for each cell, you will need to initialize the AVP player with data.url.

Not wanting to mess up too much of your code, what you can try is this:

 override init(frame: CGRect) {
super.init(frame: frame)
// only create the player if the data.url is present AND is a valid URL instead of just a bunch of normal text
if let unwrappedURLString = data?.url, let url = URL(string: unwrappedURLString) {
let player = AVPlayer(url: url)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = contentView.bounds
contentView.layer.addSublayer(playerLayer)
player.play()
}
} 

even if you wanted to initialize this like your UIImageView like this, there are still a lot of issues to address and errors to handle when compiling

fileprivate let player: AVPlayer = {
let dataURL = data?.url ?? &quot;random URL&quot; // this will not compile because data is an instance variable which will not be available when this AVP player is initialized on memory load
let url = URL(string: initialURL) // this line will still produce an optional URL so you need to also handle the unwrapping issue when you construct the URL here
let player = AVPlayer(url: url)
return player
}()

huangapple
  • 本文由 发表于 2020年1月4日 01:16:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/59582671.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定