Reputation: 15
A collection view is contained in a table view cell. Collection view cells are drawn for each card type. In indexPath 0 and 1, the card is of a single type. indexPath 2 is a mix type. There are three card types, live reserved vod, and playLayer is added when it is a vod type. When drawing collection view cells in mix type, playLayer is added to reserved type, and playLayer is added to all cells when scrolling up and down.
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"
}
}
}
The way I tried is let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) -> let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewFlowLayout.init()) . The reuse problem did not occur, but it cannot be used because the scroll direction is changed.
Upvotes: 0
Views: 932
Reputation: 976
You could solve your problem in two ways:
1 - reset your cell in cellForItemAt
Currently you call cell.setupViews(with: models![indexPath.item])
for each cell, which adds subviews to the cells. Since collectionView reuses cells, when scrolling you are getting cells which where used as all types (live, reserved, vod), that is why you see that "playLayer is added to all cells when scrolling up and down".
With this code you also add subviews each time the cell is reused, which is also not the best practice.
You could add an additional function to reset the cell and remove all subviews before adding new ones:
cell.subviews.forEach { $0.removeFromSuperview() }
(it is also better to add views to the cell.contentView
)
2 - Create separate classes for different cell types
In cellFroItemAt
you could check for the type of the cell and return the required on. Each type will have a different ReuseIdentifier
and playLayer will be added only to the type you need
Upvotes: 0