【问题标题】:CollectionView in TableView Cell Swift mix card type reuse issueTableView Cell Swift 中的 CollectionView 混合卡片类型重用问题
【发布时间】:2021-09-16 23:59:26
【问题描述】:

集合视图包含在表格视图单元格中。 为每种卡片类型绘制集合视图单元格。 在 indexPath 0 和 1 中,卡片是单一类型的。 indexPath 2 是一种混合类型。 卡类型共有三种,直播预约点播,点播类型时加playLayer。 绘制混合类型的集合视图单元格时,将playLayer添加到保留类型,并且在上下滚动时将playLayer添加到所有单元格。

class TableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

lazy private var homeManager = HomeManager()
var sections: [Section]?
var liveData: [Item]?
var vodData: [Item]?
var mixData: [Item]?

var table: UITableView = {
   let tableView = UITableView()
    tableView.register(CardCollectionTableViewCell.self, forCellReuseIdentifier: CardCollectionTableViewCell.identifier)
   return tableView
}()

override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = .black
    view.addSubview(table)
    homeManager.getdata{ [weak self] response in
        self?.sections = response.sections ?? []
        self?.liveData = self?.sections?[1].items ?? []
        self?.vodData = self?.sections?[2].items ?? []
        self?.mixData = self?.sections?[3].items ?? []
        
        self?.table.reloadData()
    }
    
    table.delegate = self
    table.dataSource = self
    
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    table.frame = view.bounds
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 3
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
        if indexPath.row == 0 && liveData != nil {
            let cell = table.dequeueReusableCell(withIdentifier: CardCollectionTableViewCell.identifier, for: indexPath) as! CardCollectionTableViewCell
            cell.configure(with: liveData)
            cell.titleLabel.text = sections![1].title!
            return cell
        }  else if indexPath.row == 1 && vodData != nil {
            let cell = table.dequeueReusableCell(withIdentifier: CardCollectionTableViewCell.identifier, for: indexPath) as! CardCollectionTableViewCell
            cell.configure(with: vodData)
            cell.titleLabel.text = sections![2].title!
            return cell
        } else if indexPath.row == 2 && mixData != nil {
            let cell = table.dequeueReusableCell(withIdentifier: CardCollectionTableViewCell.identifier, for: indexPath) as! CardCollectionTableViewCell
            cell.configure(with: mixData)
            cell.titleLabel.text = sections![3].title!
           
            return cell
        }
        
        else {
            return UITableViewCell()
        }

}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 400
}

CardCollection TableViewCell

static let identifier = "CardCollectionTableViewCell"

var titleLabel: UILabel = {
    let label = UILabel()
    label.font = Fonts.text16()
    label.textColor = .white
    return label
}()

 var collectionView: UICollectionView = {
    let layout = UICollectionViewFlowLayout()
    let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
    layout.scrollDirection = .horizontal
    collectionView.register(MyCollectionViewCell.self, forCellWithReuseIdentifier: MyCollectionViewCell.identifier)
    collectionView.backgroundColor = .black
    return collectionView
}()

var models:[Item]?

func configure(with models: [Item]?) {
    
    self.models = models
    
    titleLabel.snp.makeConstraints { make in
        make.top.equalToSuperview()
        make.leading.equalToSuperview().offset(20)
        make.width.equalTo(300)
        make.height.equalTo(24)
    }
    
    collectionView.snp.makeConstraints { make in
        make.top.equalToSuperview().offset(44)
        make.leading.equalToSuperview().offset(20)
        make.trailing.bottom.equalToSuperview()
    }
    
    collectionView.reloadData()
}

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    contentView.addSubview(collectionView)
    contentView.addSubview(titleLabel)
    
    collectionView.delegate = self
    collectionView.dataSource = self
    
    contentView.backgroundColor = .black
    
    //guard models != nil else { return }
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}
// Collectionview

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return models?.count ?? 5
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCollectionViewCell.identifier, for: indexPath) as! MyCollectionViewCell
    cell.setupViews(with: models![indexPath.item])
    cell.setupConstraints(with: models![indexPath.item])
    cell.configure(with: models![indexPath.item])
    return cell
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: 140, height: 350)
}

CollectionViewCell

class MyCollectionViewCell: UICollectionViewCell {

static let identifier = "MyCollectionViewCell"

var player: AVPlayer?

private lazy var imageView: UIImageView = {
    let image = UIImageView()
    image.contentMode = .scaleAspectFill
    image.clipsToBounds = true
    image.backgroundColor = .blue
    image.layer.cornerRadius = 8
    return image
}()

private lazy var typeLabelBackgroud: UIImageView = {
    let image = UIImageView()
    image.clipsToBounds = true
    image.layer.cornerRadius = 8
    return image
}()

private lazy var playerView: AVPlayerLayer? = {
   let url = URL(string: "https://1303309272.vod2.myqcloud.com/7e895809vodkr1303309272/8155555e3701925920462082823/f0.mp4")
    player = AVPlayer(url: url!)
    let playerLayer = AVPlayerLayer(player: player)
    playerLayer.frame = CGRect(x: 0, y: 0, width: 140, height: 210)
    playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
        self.player?.play()
    }
   
    return playerLayer
}()

private lazy var typeLabel: UILabel = {
    let label = UILabel()
    label.font = Fonts.text10()
    label.textColor = .white
    return label
}()

private lazy var timeLabel: UILabel? = {
    let label = UILabel()
    label.font = Fonts.text11()
    label.textColor = .white
    label.frame.size.width = 42
    label.frame.size.height = 16
    return label
}()

private lazy var titleLabel: UILabel = {
    let label = UILabel()
    label.numberOfLines = 0
    label.font = Fonts.text14()
    label.textColor = .white
    return label
}()

private lazy var storeName: UILabel = {
    let label = UILabel()
    label.font = Fonts.text11()
    label.textColor = .gray
    return label
}()

private lazy var heartImage: UIImageView = {
    let image = UIImageView()
    image.image = UIImage(named: "Vector")
    image.contentMode = .scaleAspectFit
    return image
}()

private lazy var heartCountLabel: UILabel = {
    let label = UILabel()
    label.font = Fonts.text11()
    label.textColor = .gray
    label.frame.size.width = 27
    label.frame.size.height = 16
    label.textAlignment = .left
    return label
}()

private lazy var eyeImage: UIImageView = {
    let image = UIImageView()
    image.image = UIImage(named: "Eye")
    image.contentMode = .scaleAspectFit
    return image
}()

private lazy var eyeCountLabel: UILabel = {
    let label = UILabel()
    label.font = Fonts.text11()
    label.textColor = .gray
    label.frame.size.width = 27
    label.frame.size.height = 16
    label.textAlignment = .left
    return label
}()

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

public convenience init() {
    self.init(frame:. zero)

}

required init?(coder: NSCoder) {
  
    
    fatalError("init(coder:) has not been implemented")
}

 func setupViews(with model: Item?) {
guard model != nil else {return}
print("CollectionViewCell의 model! data: \(model)")
    if model!.type == "LIVE" {
       addSubview(imageView)
       addSubview(typeLabelBackgroud)
       addSubview(typeLabel)
       addSubview(titleLabel)
       addSubview(storeName)
       addSubview(heartImage)
       addSubview(heartCountLabel)
       addSubview(eyeImage)
       addSubview(eyeCountLabel)
    } else if model!.type == "RESERVED"{
        addSubview(imageView)
        addSubview(typeLabelBackgroud)
        addSubview(typeLabel)
        addSubview(titleLabel)
        addSubview(storeName)
        addSubview(heartImage)
        addSubview(heartCountLabel)
        addSubview(eyeImage)
        addSubview(eyeCountLabel)
    }  else if model!.type == "VOD" {
       addSubview(imageView)
       addSubview(typeLabelBackgroud)
       addSubview(typeLabel)
       addSubview(titleLabel)
       addSubview(storeName)
       addSubview(heartImage)
       addSubview(heartCountLabel)
       addSubview(eyeImage)
       addSubview(eyeCountLabel)
       addSubview(timeLabel!)
        imageView.layer.addSublayer(playerView!)
    }

}

 func setupConstraints(with model: Item?) {
 guard model != nil else {return}
    if model!.type == "LIVE" {
        imageView.snp.makeConstraints { make in
            make.width.equalTo(140)
            make.height.equalTo(210)
        }
        
        typeLabelBackgroud.snp.makeConstraints { make in
            make.leading.equalTo(imageView).inset(8)
            make.top.equalTo(imageView).inset(10)
            make.width.equalTo(33)
            make.height.equalTo(20)
        }
        
        typeLabel.snp.makeConstraints { make in
            make.leading.equalTo(typeLabelBackgroud).inset(6)
            make.top.equalTo(typeLabelBackgroud).inset(2)
            make.width.equalTo(21)
            make.height.equalTo(16)
        }
        
        titleLabel.snp.makeConstraints { make in
            make.top.equalTo(imageView.snp.bottom).offset(8)
            make.width.equalTo(140)
            make.height.equalTo(42)
        }
        
        storeName.snp.makeConstraints { make in
            make.top.equalTo(titleLabel.snp.bottom).offset(4)
            make.width.equalTo(140)
            make.height.equalTo(16)
        }
        
        heartImage.snp.makeConstraints { make in
            make.top.equalTo(storeName.snp.bottom).offset(3)
            make.width.height.equalTo(16)
        }
        
        heartCountLabel.snp.makeConstraints { make in
            make.top.equalTo(storeName.snp.bottom).offset(3)
            make.leading.equalTo(heartImage.snp.trailing).offset(5)
        }
        
        eyeImage.snp.makeConstraints { make in
            make.leading.equalTo(heartCountLabel.snp.trailing).offset(13)
            make.top.equalTo(storeName.snp.bottom).offset(3)
            make.width.height.equalTo(16)
        }
        
        eyeCountLabel.snp.makeConstraints { make in
            make.top.equalTo(storeName.snp.bottom).offset(3)
            make.leading.equalTo(eyeImage.snp.trailing).offset(5)
        }
        
    } else if model!.type == "RESERVED" {
        imageView.snp.makeConstraints { make in
            make.width.equalTo(140)
            make.height.equalTo(210)
        }
        
        typeLabelBackgroud.snp.makeConstraints { make in
            make.leading.equalTo(imageView).inset(8)
            make.top.equalTo(imageView).inset(10)
            make.width.equalTo(33)
            make.height.equalTo(20)
        }
        
        typeLabel.snp.makeConstraints { make in
            make.leading.equalTo(typeLabelBackgroud).inset(6)
            make.top.equalTo(typeLabelBackgroud).inset(2)
            make.width.equalTo(21)
            make.height.equalTo(16)
        }
        
        titleLabel.snp.makeConstraints { make in
            make.top.equalTo(imageView.snp.bottom).offset(8)
            make.width.equalTo(140)
            make.height.equalTo(42)
        }
        
        storeName.snp.makeConstraints { make in
            make.top.equalTo(titleLabel.snp.bottom).offset(4)
            make.width.equalTo(140)
            make.height.equalTo(16)
        }
        
        heartImage.snp.makeConstraints { make in
            make.top.equalTo(storeName.snp.bottom).offset(3)
            make.width.height.equalTo(16)
        }
        
        heartCountLabel.snp.makeConstraints { make in
            make.top.equalTo(storeName.snp.bottom).offset(3)
            make.leading.equalTo(heartImage.snp.trailing).offset(5)
        }
        
        eyeImage.snp.makeConstraints { make in
            make.leading.equalTo(heartCountLabel.snp.trailing).offset(13)
            make.top.equalTo(storeName.snp.bottom).offset(3)
            make.width.height.equalTo(16)
        }
        
        eyeCountLabel.snp.makeConstraints { make in
            make.top.equalTo(storeName.snp.bottom).offset(3)
            make.leading.equalTo(eyeImage.snp.trailing).offset(5)
        }
    } else if model!.type == "VOD" {
        imageView.snp.makeConstraints { make in
            make.width.equalTo(140)
            make.height.equalTo(210)
        }
        
        typeLabelBackgroud.snp.makeConstraints { make in
            make.leading.equalTo(imageView).inset(8)
            make.top.equalTo(imageView).inset(10)
            make.width.equalTo(33)
            make.height.equalTo(20)
        }
        
        typeLabel.snp.makeConstraints { make in
            make.leading.equalTo(typeLabelBackgroud).inset(6)
            make.top.equalTo(typeLabelBackgroud).inset(2)
            make.width.equalTo(21)
            make.height.equalTo(16)
        }
        
        timeLabel!.snp.makeConstraints { make in
            make.top.equalTo(imageView).inset(8)
            make.trailing.equalTo(imageView).inset(10)
           
        }
        
        titleLabel.snp.makeConstraints { make in
            make.top.equalTo(imageView.snp.bottom).offset(8)
            make.width.equalTo(140)
            make.height.equalTo(42)
        }
        
        storeName.snp.makeConstraints { make in
            make.top.equalTo(titleLabel.snp.bottom).offset(4)
            make.width.equalTo(140)
            make.height.equalTo(16)
        }
        
        heartImage.snp.makeConstraints { make in
            make.top.equalTo(storeName.snp.bottom).offset(3)
            make.width.height.equalTo(16)
        }
        
        heartCountLabel.snp.makeConstraints { make in
            make.top.equalTo(storeName.snp.bottom).offset(3)
            make.leading.equalTo(heartImage.snp.trailing).offset(5)
        }
        
        eyeImage.snp.makeConstraints { make in
            make.leading.equalTo(heartCountLabel.snp.trailing).offset(13)
            make.top.equalTo(storeName.snp.bottom).offset(3)
            make.width.height.equalTo(16)
        }
        
        eyeCountLabel.snp.makeConstraints { make in
            make.top.equalTo(storeName.snp.bottom).offset(3)
            make.leading.equalTo(eyeImage.snp.trailing).offset(5)
        }
    }
}

    public func configure(with model: Item?) {
    //self.model! = model!
    
     guard model != nil else {return}
    if model!.type == "LIVE" {
        imageView.kf.setImage(with: URL(string: model!.image!))
        typeLabel.text = "LIVE"
        typeLabelBackgroud.backgroundColor = .orange
        titleLabel.text = String(model!.title!)
        storeName.text = String(model!.store!)
        heartCountLabel.text = String(model!.likeCount!)
        eyeCountLabel.text = String(model!.playedCount!)
    } else if model!.type == "RESERVED" {
        imageView.kf.setImage(with: URL(string: model!.image!))
        typeLabel.text = "예정"
        typeLabelBackgroud.backgroundColor = Colors.primary()
        titleLabel.text = String(model!.title!)
        storeName.text = String(model!.store!)
        heartCountLabel.text = String(model!.likeCount!)
        eyeCountLabel.text = String(model!.playedCount!)
    } else if model!.type == "VOD" {
        imageView.kf.setImage(with: URL(string: model!.image!))
        typeLabel.text = "VOD"
        titleLabel.text = String(model!.title!)
        typeLabelBackgroud.backgroundColor = Colors.vodBackgroud()
        storeName.text = String(model!.store!)
        heartCountLabel.text = String(model!.likeCount!)
        eyeCountLabel.text = String(model!.playedCount!)
        timeLabel?.text = "01:35:40"
    }
}

}

我尝试的方式是 let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) -> 让 collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewFlowLayout.init()) 。 没有出现重用问题,但是因为滚动方向改变了,所以不能使用。

【问题讨论】:

    标签: ios swift tableview collectionview programmatically


    【解决方案1】:

    您可以通过两种方式解决您的问题:

    1 - 重置cellForItemAt中的单元格

    目前您为每个单元格调用cell.setupViews(with: models![indexPath.item]),这会将子视图添加到单元格中。由于 collectionView 重用单元格,因此在滚动时,您将获得用作所有类型(直播、保留、vod)的单元格,这就是为什么您会看到“上下滚动时将 playLayer 添加到所有单元格”。

    使用此代码,您还可以在每次重用单元格时添加子视图,这也不是最佳做法。

    您可以添加一个额外的功能来重置单元格并在添加新子视图之前删除所有子视图:

    cell.subviews.forEach { $0.removeFromSuperview() }
    

    (最好在cell.contentView添加视图)

    2 - 为不同的细胞类型创建单独的类

    cellFroItemAt你可以检查单元格的类型并返回所需的。每种类型都有不同的ReuseIdentifier,playLayer 只会添加到您需要的类型中

    【讨论】:

    • 我将视图添加到 cell.contentView。
    • 在 tableViewCell - 是的,但不在 collectionViewCell 中(即 addSubview(imageView) 应替换为 contentView.addSubview(imageView))
    • 我更正了。正如你所指出的,我在 contentview 中添加了子视图。谢谢。但我很好奇为什么。和 cell.subviews.forEach { $0.removeFromSuperview() } 我不知道在哪里添加这段代码。
    • 你可以把它放在cell.setupViews(with: models![indexPath.item])之前。由于更新,不应该是cell.contentView.subviews.forEach { $0.removeFromSuperview() }
    • 但是这样你就失去了细胞重用的所有性能优势,所以我建议使用选项 2
    猜你喜欢
    • 2020-06-03
    • 1970-01-01
    • 1970-01-01
    • 2018-07-03
    • 2018-03-30
    • 2020-07-20
    • 2020-12-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多