UICollectionView 没有正确滚动

Posted

技术标签:

【中文标题】UICollectionView 没有正确滚动【英文标题】:UICollectionView not scrolling correctly 【发布时间】:2020-02-04 05:23:04 【问题描述】:

我正在填充一个 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
  

【问题讨论】:

【参考方案1】:

您只有一个“价格”变量,它保留最后一个可见单元格的数据。 你的逻辑不正确。 您必须为每个单元格设置一个“价格”值。

这是给你的另一个建议;

// 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

【讨论】:

【参考方案2】:

原因:

这是因为每当创建或重用新的cell 时,都会多次调用cellForItemAt。因此,CollectionViewController 值中保存的price 将用于最近调用了cellForItemAtcell

解决方案:

不要将price 值保存在CollectionViewController 中然后打印它,而是使用closure 来获取特定price 的确切值cell

在 PostCell 中,创建一个handler 并在设置为targetmyButtonbuttonPressed() 方法中调用它,即

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:) 中将handler 设置为cell,即

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

【讨论】:

我可以把 c​​ell.handler = (price) in item 吗?还有如何初始化处理程序? cellForItemAt 中,您将初始化handler。我已经在上面添加了代码。 在 super.init(frame: frame) 行中的 super.init 调用中,我不断收到 Property 'self.handler' not initialized handler 中的PostCell 设为可选。我已经用相同的方式更新了代码。 我已经进行了更改,但是现在按钮什么也不做。它不会打印任何东西。

以上是关于UICollectionView 没有正确滚动的主要内容,如果未能解决你的问题,请参考以下文章

将 UIRefreshControl 置于滚动 UICollectionView 上方的正确方法是啥?

滚动 UITableView 和 UICollectionView 的数据不正确

嵌入垂直 UIScrollView 时,UICollectionView 单元格无法正确滚动

UICollectionView 单元格高度/可滚动内容行为奇怪/不正确

UICollectionView Scroll 无法正确分页

UICollectionView - 水平滚动,水平布局?