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

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

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

问题

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

  1. import UIKit
  2. import AVKit
  3. import Firebase
  4. struct CustomData {
  5. var title: String
  6. var image: UIImage
  7. var url: String
  8. }
  9. var videoURL = ""
  10. var array = [String]()
  11. class HomeViewController: UIViewController {
  12. var fileArray: Array<Any>?
  13. fileprivate let data = [
  14. CustomData(title: "Test", image: #imageLiteral(resourceName: "ss-1"), url: "test.com"),
  15. CustomData(title: "Test2", image: #imageLiteral(resourceName: "done-button"), url: "test.com"),
  16. CustomData(title: "Test2", image: #imageLiteral(resourceName: "notificationIcon"), url: "test.com")
  17. ]
  18. fileprivate let collectionView: UICollectionView = {
  19. let layout = UICollectionViewFlowLayout()
  20. layout.scrollDirection = .horizontal
  21. let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
  22. cv.translatesAutoresizingMaskIntoConstraints = false
  23. cv.register(CustomCell.self, forCellWithReuseIdentifier: "cell")
  24. return cv
  25. }()
  26. override func viewDidLoad() {
  27. super.viewDidLoad()
  28. view.addSubview(collectionView)
  29. collectionView.backgroundColor = .white
  30. collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 150).isActive = true
  31. collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true
  32. collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true
  33. collectionView.heightAnchor.constraint(equalTo: collectionView.widthAnchor, multiplier: 0.5).isActive = true
  34. collectionView.delegate = self
  35. collectionView.dataSource = self
  36. array = fileArray as! [String]
  37. }
  38. }
  39. extension HomeViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
  40. public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
  41. return CGSize(width: collectionView.frame.width, height: collectionView.frame.width)
  42. }
  43. public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  44. return data.count
  45. }
  46. public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  47. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CustomCell
  48. cell.data = self.data[indexPath.row]
  49. return cell
  50. }
  51. }
  52. class CustomCell: UICollectionViewCell {
  53. var data: CustomData? {
  54. didSet {
  55. guard let data = data else { return }
  56. bg.image = data.image
  57. }
  58. }
  59. fileprivate let bg: UIImageView = {
  60. let iv = UIImageView()
  61. iv.image = #imageLiteral(resourceName: "splash_icon")
  62. iv.translatesAutoresizingMaskIntoConstraints = false
  63. iv.contentMode = .scaleAspectFill
  64. iv.clipsToBounds = true
  65. iv.layer.cornerRadius = 10
  66. return iv
  67. }()
  68. override init(frame: CGRect) {
  69. super.init(frame: frame)
  70. // 以下代码用于显示视频,您需要根据您的需求来更改
  71. /*
  72. let player = AVPlayer(url: URL(string: videoURL)!)
  73. let playerLayer = AVPlayerLayer(player: player)
  74. playerLayer.frame = contentView.bounds
  75. contentView.layer.addSublayer(playerLayer)
  76. player.play()
  77. */
  78. contentView.addSubview(bg)
  79. bg.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
  80. bg.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
  81. bg.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
  82. bg.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
  83. }
  84. required init?(coder: NSCoder) {
  85. fatalError("init(coder:) has not been implemented")
  86. }
  87. }

请注意,代码中有一部分注释,用于显示视频。您需要根据您的需求来取消注释并更改视频的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:

  1. import UIKit
  2. import AVKit
  3. import Firebase
  4. struct CustomData {
  5. var title: String
  6. var image: UIImage
  7. var url: String
  8. }
  9. var videoURL = &quot;&quot;
  10. var array = [String]()
  11. class HomeViewController: UIViewController {
  12. var fileArray:Array&lt;Any&gt;?
  13. fileprivate let data = [
  14. CustomData(title: &quot;Test&quot;, image: #imageLiteral(resourceName: &quot;ss-1&quot;), url: &quot;test.com&quot;),
  15. CustomData(title: &quot;Test2&quot;, image: #imageLiteral(resourceName: &quot;done-button&quot;), url: &quot;test.com&quot;),
  16. CustomData(title: &quot;Test2&quot;, image: #imageLiteral(resourceName: &quot;notificationIcon&quot;), url: &quot;test.com&quot;)
  17. ]
  18. fileprivate let collectionView: UICollectionView = {
  19. let layout = UICollectionViewFlowLayout()
  20. layout.scrollDirection = .horizontal
  21. let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
  22. cv.translatesAutoresizingMaskIntoConstraints = false
  23. cv.register(CustomCell.self, forCellWithReuseIdentifier: &quot;cell&quot;)
  24. return cv
  25. }()
  26. override func viewDidLoad() {
  27. super.viewDidLoad()
  28. view.addSubview(collectionView)
  29. collectionView.backgroundColor = .white
  30. collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 150).isActive = true
  31. collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true
  32. collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true
  33. collectionView.heightAnchor.constraint(equalTo: collectionView.widthAnchor, multiplier: 0.5).isActive = true
  34. collectionView.delegate = self
  35. collectionView.dataSource = self
  36. array = fileArray as! [String]
  37. }
  38. }
  39. extension HomeViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource{
  40. public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -&gt; CGSize {
  41. return CGSize(width: collectionView.frame.width, height: collectionView.frame.width)
  42. }
  43. public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -&gt; Int {
  44. return data.count
  45. }
  46. public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -&gt; UICollectionViewCell {
  47. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: &quot;cell&quot;, for: indexPath) as! CustomCell
  48. cell.data = self.data[indexPath.row]
  49. return cell
  50. }
  51. }
  52. class CustomCell: UICollectionViewCell{
  53. var data: CustomData?{
  54. didSet{
  55. guard let data = data else { return }
  56. bg.image = data.image
  57. }
  58. }
  59. fileprivate let bg: UIImageView = {
  60. let iv = UIImageView()
  61. iv.image = #imageLiteral(resourceName: &quot;splash_icon&quot;)
  62. iv.translatesAutoresizingMaskIntoConstraints = false
  63. iv.contentMode = .scaleAspectFill
  64. iv.clipsToBounds = true
  65. iv.layer.cornerRadius = 10
  66. return iv
  67. }()
  68. override init(frame: CGRect) {
  69. super.init(frame: frame)
  70. // let player = AVPlayer(url: URL(string: videoURL)!)
  71. // let playerLayer = AVPlayerLayer(player: player)
  72. // playerLayer.frame = contentView.bounds
  73. // contentView.layer.addSublayer(playerLayer)
  74. // player.play()
  75. contentView.addSubview(bg)
  76. bg.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
  77. bg.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
  78. bg.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
  79. bg.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
  80. }
  81. required init?(coder: NSCoder) {
  82. fatalError(&quot;init(coder:) has not been implemented&quot;)
  83. }
  84. }

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:

  1. override init(frame: CGRect) {
  2. super.init(frame: frame)
  3. // let player = AVPlayer(url: URL(string: videoURL)!)
  4. // let playerLayer = AVPlayerLayer(player: player)
  5. // playerLayer.frame = contentView.bounds
  6. // contentView.layer.addSublayer(playerLayer)
  7. // player.play()
  8. contentView.addSubview(bg)
  9. bg.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
  10. bg.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
  11. bg.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
  12. bg.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
  13. }

to this:

  1. override init(frame: CGRect) {
  2. super.init(frame: frame)
  3. let player = AVPlayer(url: URL(string: videoURL)!)
  4. let playerLayer = AVPlayerLayer(player: player)
  5. playerLayer.frame = contentView.bounds
  6. contentView.layer.addSublayer(playerLayer)
  7. player.play()
  8. // contentView.addSubview(bg)
  9. // bg.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
  10. // bg.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
  11. // bg.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
  12. // bg.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
  13. }

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:

  1. var data: CustomData?{
  2. didSet{
  3. guard let data = data else { return }
  4. bg.image = data.image
  5. }
  6. }
  7. fileprivate let bg: UIImageView = {
  8. let iv = UIImageView()
  9. iv.image = #imageLiteral(resourceName: &quot;splash_icon&quot;)
  10. iv.translatesAutoresizingMaskIntoConstraints = false
  11. iv.contentMode = .scaleAspectFill
  12. iv.clipsToBounds = true
  13. iv.layer.cornerRadius = 10
  14. return iv
  15. }()

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播放器。

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

  1. override init(frame: CGRect) {
  2. super.init(frame: frame)
  3. // 仅在data.url存在且是有效的URL而不仅仅是一堆普通文本时才创建播放器
  4. if let unwrappedURLString = data?.url, let url = URL(string: unwrappedURLString) {
  5. let player = AVPlayer(url: url)
  6. let playerLayer = AVPlayerLayer(player: player)
  7. playerLayer.frame = contentView.bounds
  8. contentView.layer.addSublayer(playerLayer)
  9. player.play()
  10. }
  11. }

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

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

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

  1. var data: CustomData?{
  2. didSet{
  3. guard let data = data else { return }
  4. bg.image = data.image
  5. }
  6. }

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:

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

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

  1. fileprivate let player: AVPlayer = {
  2. 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
  3. 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
  4. let player = AVPlayer(url: url)
  5. return player
  6. }()

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:

确定