更改 UICollectionViewCell 的布局

Posted

技术标签:

【中文标题】更改 UICollectionViewCell 的布局【英文标题】:Change layout of UICollectionViewCell 【发布时间】:2020-10-19 02:40:58 【问题描述】:

我正在使用 UICollectionViewController 来显示 UICollectionViewCell 的集合,用户可以在 ios 13、swift 5 和 UIKit 之间滑动。如果用户改变了方向,我最终想重新绘制单元格。在我的控制器中,我注意到这段代码会在方向更改时执行:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! MyCell
    return cell

在 MyCell 中,我使用此代码进行监控,然后重新初始化布局:

override init(frame: CGRect) 
    super.init(frame: frame)
    setLayout()
    print("### orientation:: \(UIDevice.current.orientation.isLandscape) ")

结果:这个方法被调用了 2 次。当应用程序启动时,以及当我从纵向更改为横向时。任何后续更改,不要调用它。我怀疑这很好,因为可能两个版本都被缓存了(风景和肖像)。如果我根据 UIDevice.current.orientation.isLandscape 标志向 setLayout() 方法添加代码,则在执行时不会发生任何变化。注意:UIDevice.current.orientation.isLandscape 的实际结果是正确的。

这是我的 setLayout() 方法中的一个示例:

let topImageViewContainer = UIView();

topImageViewContainer.translatesAutoresizingMaskIntoConstraints = false
topImageViewContainer.backgroundColor = .yellow
addSubview(topImageViewContainer)
    
NSLayoutConstraint.activate([
    topImageViewContainer.topAnchor.constraint(equalTo: topAnchor),
    topImageViewContainer.leftAnchor.constraint(equalTo: leftAnchor),
    topImageViewContainer.rightAnchor.constraint(equalTo: rightAnchor),
    topImageViewContainer.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.5)
])
if (UIDevice.current.orientation.isLandscape) 
    let imageView = UIImageView(image: UIImage(named: "photo_a"))
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.contentMode = .scaleAspectFit
    topImageViewContainer.addSubview(imageView)
 else 
    let imageView = UIImageView(image: UIImage(named: "photo_b"))
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.contentMode = .scaleAspectFit
    topImageViewContainer.addSubview(imageView)

在上面的示例中,带有“photo_a”的代码正在执行,但 ui 仍然显示 photo_b。我怀疑我做错了什么,我需要以某种方式迁移到此处提到的“自适应布局”:Different layouts in portrait and landscape mode。但是,我不确定如何将一些“viewWillTransition*”函数挂接到具有 UIView 的 UICollectionViewCell。

如何调整实现 UICollectionViewCell 的 MyCell 类,以允许在用户以 Swift 预期的方式从横向更改为纵向时更新单元格?

【问题讨论】:

实现 layoutSubviews 并查看自上次以来我们是否因轮换而被调用。 “以斯威夫特的预期方式?”无关紧要。无论您使用哪种语言,Cocoa 都是 Cocoa。 @matt 我的意思是“Swift 预期的方式”,更多的是“我不是为了让它工作而修改代码,而是遵循最佳实践。&& layoutSubviews 在旋转更改时执行多次。 【参考方案1】:

您好,尝试检查 cellForItem 中是横向还是纵向,我的示例: 设置colloectionView、flowLayaout和cell id:

class ViewController: UIViewController 

private var collectionView: UICollectionView?
let layout = UICollectionViewFlowLayout()
let cellId = "cellId"

现在在 viewDidLoad 中设置集合视图和布局参数以及集合约束:

layout.scrollDirection = .horizontal
    
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView?.delegate = self
collectionView?.dataSource = self
collectionView?.backgroundColor = .clear
collectionView?.register(BottomCellTableViewCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.contentInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
collectionView?.showsHorizontalScrollIndicator = false
collectionView?.translatesAutoresizingMaskIntoConstraints = false
    
guard let collection = collectionView else  return 
    
view.addSubview(collection)
layout.itemSize = CGSize(width: view.bounds.size.width / 2, height: 200)
collection.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20).isActive = true
collection.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
collection.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
collection.heightAnchor.constraint(equalToConstant: 200).isActive = true

之后在单独的扩展中符合委托和数据源(更简洁的代码)并在其中设置 numberOfItems 和 cellForRow:

extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource 

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    return 10


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! BottomCellTableViewCell
// check here device orientation
    if UIDevice.current.orientation.isLandscape 
        let image = UIImage(named: "meB")
        cell.imageV.image = image
        
     else 
        let image = UIImage(named: "me")
        cell.imageV.image = image
    
// reload the view and the collection
    DispatchQueue.main.async 
        self.collectionView?.reloadData()
    
    return cell
 

在 collectionViewCell 中直接向单元格添加子视图,并将图像视图固定到单元格,是错误的。您将它添加到 contentView 并固定到它的相关约束:

class BottomCellTableViewCell: UICollectionViewCell 

let imageV: UIImageView = 
    let iv = UIImageView()
    iv.image = UIImage(named: "me")
    iv.contentMode = .scaleAspectFill
    iv.clipsToBounds = true
    iv.translatesAutoresizingMaskIntoConstraints = false
    
    return iv
()

override init(frame: CGRect) 
    super.init(frame: frame)
    
    contentView.backgroundColor = .red
    
    contentView.addSubview(imageV)
    imageV.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
    imageV.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
    imageV.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
    imageV.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true


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

这是结果:

【讨论】:

您能否评论“在 collectionViewCell 中直接向单元格添加子视图,并将图像视图固定到单元格,这是错误的”?我有点困惑为什么视图逻辑要进入控制器而不是视图单元。 @angryip for the contentView 你可以阅读这个developer.apple.com/documentation/uikit/uitableviewcell/… ... 直接将 pin 添加到单元格是非法的。逻辑进入视图,因为当改变方向时你必须重新加载集合视图数据来改变图像 啊,有道理。对象的名称只是有点误导。该文档还证明了您所提到的。 developer.apple.com/documentation/uikit/uicollectionviewcell 。我很快就会尝试一下,看看它的表现如何。谢谢

以上是关于更改 UICollectionViewCell 的布局的主要内容,如果未能解决你的问题,请参考以下文章

更改 UICollectionViewCell 子视图框架

UICollectionViewCell 在滚动开始后立即更改数据

单击按钮 UICollectionViewCell 宽度和高度更改

UICollectionViewCell 中的 UIButton 子视图不更改控件状态

iOS:UICollectionViewCell 动画部分更改单元格内容

iOS:如何更改 UICollectionViewCell 的方向?