UICollectionView 无法正确滚动
UICollectionView not scrolling correctly
我正在填充一个 UICollectionView,其中的单元格是全屏的,并且它们水平滚动。正确的数据显示在屏幕上,但是当我按下按钮打印出当前价格时,它继续打印下一个 indexPath 的价格。我相信在滚动方面我做错了什么。如果 pagingEnabled 打开或关闭也没有区别。
class PostCell: UICollectionViewCell {
let containerView:UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.clipsToBounds = true
return view
}()
let priceLabel:UILabel = {
let label = UILabel()
label.font = UIFont.boldSystemFont(ofSize: 20)
label.text = "price"
label.textColor = .black
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let myButton: UIButton = {
let button = UIButton(type: .system)
button.titleLabel?.font = UIFont.systemFont(ofSize: 18)
button.backgroundColor = .red
return button
}()
func set(name: String, brand: String, price: String){
priceLabel.text = price
}
override init(frame: CGRect) {
super.init(frame: frame)
self.contentView.addSubview(containerView)
containerView.addSubview(priceLabel)
containerView.addSubview(myButton)
setupCellConstraints()
}
private func setupCellConstraints(){
containerView.anchor(top: contentView.topAnchor, left: contentView.leftAnchor, bottom: contentView.bottomAnchor, right: contentView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
nameLabel.anchor(top: containerView.topAnchor, left: nil, bottom: nil, right: nil, paddingTop: 50, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
brandLabel.anchor(top: nameLabel.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 50, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
priceLabel.anchor(top: brandLabel.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 50, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
myButton.anchor(top: priceLabel.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 60, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 70, height: 70)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
var itemsArr = [Item]()
init() {
super.init(collectionViewLayout: UICollectionViewFlowLayout())
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private var collectionViewFlowLayout: UICollectionViewFlowLayout {
return collectionViewLayout as! UICollectionViewFlowLayout
}
override func viewDidLoad() {
parseData()
collectionView.showsHorizontalScrollIndicator = false
collectionView.showsVerticalScrollIndicator = false
collectionViewFlowLayout.minimumLineSpacing = 0
self.collectionView.isPagingEnabled = true
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .horizontal
}
collectionView?.register(PostCell.self, forCellWithReuseIdentifier: cellId)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemsArr.count
}
@objc func buttonPressed(){
print(price)
}
var price: String = ""
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PostCell
let item = itemsArr[indexPath.row]]
price = item.price
cell.set(name: item.name, brand: item.brand, price: item.price)
cell.myButton.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
print(item)
return cell
}
原因:
这是因为每当创建或重新使用新的 cell
时,都会多次调用 cellForItemAt
。因此,CollectionViewController
值中保存的 price
将用于最近调用 cellForItemAt
的 cell
。
解法:
不是在CollectionViewController
中保存price
值然后打印它,而是使用closure
来获取[=14的精确值=] 对于特定的 cell
.
在 PostCell 中,创建一个 handler
并在 buttonPressed()
方法中调用它,设置为 myButton
的 target
,即
class PostCell: UICollectionViewCell {
//rest of the code...
var handler: ((String?)->())? //here....
let myButton: UIButton = {
let button = UIButton(type: .system)
button.titleLabel?.font = UIFont.systemFont(ofSize: 18)
button.backgroundColor = .red
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside) //here....
return button
}()
@objc func buttonPressed(){
handler?(priceLabel.text) //here....
}
}
现在,在 collectionView(_:cellForItemAt:)
中为 cell
设置 handler
,即
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PostCell
let item = itemsArr[indexPath.row]
cell.set(name: item.name, brand: item.brand, price: item.price)
cell.handler = {(price) in //here....
print(price)
}
return cell
}
您只有一个 'price' 变量,它保留最后一个可见单元格的数据。
你的逻辑不正确。
每个单元格必须有一个 'price' 值。
这是给你的另一个建议;
// we capture the tag of the button and tag points the index of the array.
@objc func buttonPressed(_ sender : UIButton){
let item = itemArr[sender.tag]
print(item)
}
// var price: String = "" // that variable is meaningless.
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PostCell
let item = itemsArr[indexPath.row]]
// price = item.price // that line is meaningless.
cell.set(name: item.name, brand: item.brand, price: item.price)
cell.myButton.tag = indexPath.row // added that line.
cell.myButton.addTarget(self, action: #selector(buttonPressed(:)), for: .touchUpInside) // changed that line little bit.
print(item)
return cell
}
我正在填充一个 UICollectionView,其中的单元格是全屏的,并且它们水平滚动。正确的数据显示在屏幕上,但是当我按下按钮打印出当前价格时,它继续打印下一个 indexPath 的价格。我相信在滚动方面我做错了什么。如果 pagingEnabled 打开或关闭也没有区别。
class PostCell: UICollectionViewCell {
let containerView:UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.clipsToBounds = true
return view
}()
let priceLabel:UILabel = {
let label = UILabel()
label.font = UIFont.boldSystemFont(ofSize: 20)
label.text = "price"
label.textColor = .black
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let myButton: UIButton = {
let button = UIButton(type: .system)
button.titleLabel?.font = UIFont.systemFont(ofSize: 18)
button.backgroundColor = .red
return button
}()
func set(name: String, brand: String, price: String){
priceLabel.text = price
}
override init(frame: CGRect) {
super.init(frame: frame)
self.contentView.addSubview(containerView)
containerView.addSubview(priceLabel)
containerView.addSubview(myButton)
setupCellConstraints()
}
private func setupCellConstraints(){
containerView.anchor(top: contentView.topAnchor, left: contentView.leftAnchor, bottom: contentView.bottomAnchor, right: contentView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
nameLabel.anchor(top: containerView.topAnchor, left: nil, bottom: nil, right: nil, paddingTop: 50, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
brandLabel.anchor(top: nameLabel.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 50, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
priceLabel.anchor(top: brandLabel.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 50, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
myButton.anchor(top: priceLabel.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 60, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 70, height: 70)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
var itemsArr = [Item]()
init() {
super.init(collectionViewLayout: UICollectionViewFlowLayout())
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private var collectionViewFlowLayout: UICollectionViewFlowLayout {
return collectionViewLayout as! UICollectionViewFlowLayout
}
override func viewDidLoad() {
parseData()
collectionView.showsHorizontalScrollIndicator = false
collectionView.showsVerticalScrollIndicator = false
collectionViewFlowLayout.minimumLineSpacing = 0
self.collectionView.isPagingEnabled = true
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .horizontal
}
collectionView?.register(PostCell.self, forCellWithReuseIdentifier: cellId)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemsArr.count
}
@objc func buttonPressed(){
print(price)
}
var price: String = ""
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PostCell
let item = itemsArr[indexPath.row]]
price = item.price
cell.set(name: item.name, brand: item.brand, price: item.price)
cell.myButton.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
print(item)
return cell
}
原因:
这是因为每当创建或重新使用新的 cell
时,都会多次调用 cellForItemAt
。因此,CollectionViewController
值中保存的 price
将用于最近调用 cellForItemAt
的 cell
。
解法:
不是在CollectionViewController
中保存price
值然后打印它,而是使用closure
来获取[=14的精确值=] 对于特定的 cell
.
在 PostCell 中,创建一个 handler
并在 buttonPressed()
方法中调用它,设置为 myButton
的 target
,即
class PostCell: UICollectionViewCell {
//rest of the code...
var handler: ((String?)->())? //here....
let myButton: UIButton = {
let button = UIButton(type: .system)
button.titleLabel?.font = UIFont.systemFont(ofSize: 18)
button.backgroundColor = .red
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside) //here....
return button
}()
@objc func buttonPressed(){
handler?(priceLabel.text) //here....
}
}
现在,在 collectionView(_:cellForItemAt:)
中为 cell
设置 handler
,即
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PostCell
let item = itemsArr[indexPath.row]
cell.set(name: item.name, brand: item.brand, price: item.price)
cell.handler = {(price) in //here....
print(price)
}
return cell
}
您只有一个 'price' 变量,它保留最后一个可见单元格的数据。 你的逻辑不正确。 每个单元格必须有一个 'price' 值。
这是给你的另一个建议;
// we capture the tag of the button and tag points the index of the array.
@objc func buttonPressed(_ sender : UIButton){
let item = itemArr[sender.tag]
print(item)
}
// var price: String = "" // that variable is meaningless.
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PostCell
let item = itemsArr[indexPath.row]]
// price = item.price // that line is meaningless.
cell.set(name: item.name, brand: item.brand, price: item.price)
cell.myButton.tag = indexPath.row // added that line.
cell.myButton.addTarget(self, action: #selector(buttonPressed(:)), for: .touchUpInside) // changed that line little bit.
print(item)
return cell
}