无法使用自定义UICollectionViewFlowLayout

时间:2020-08-24 03:23:17

标签: swift uicollectionviewlayout uicollectionviewflowlayout

我目前正在尝试在集合视图的每个单元格之间实现自定义分隔符视图。我在网上找到了这个答案,并在行间距(link)中添加了自定义视图。

private let separatorDecorationView = "separator"

final class CustomFlowLayout: UICollectionViewFlowLayout {
    override init() {
        super.init()
        register(SeparatorView.self,
                 forDecorationViewOfKind: separatorDecorationView)
    }

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

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let layoutAttributes = super.layoutAttributesForElements(in: rect) ?? []
        let lineWidth = self.minimumLineSpacing

        var decorationAttributes: [UICollectionViewLayoutAttributes] = []

        // skip first cell
        for layoutAttribute in layoutAttributes where layoutAttribute.indexPath.item > 0 {
            let separatorAttribute = UICollectionViewLayoutAttributes(forDecorationViewOfKind: separatorDecorationView,
                                                                      with: layoutAttribute.indexPath)
            let cellFrame = layoutAttribute.frame
            separatorAttribute.frame = CGRect(x: cellFrame.origin.x,
                                              y: cellFrame.origin.y - lineWidth,
                                              width: cellFrame.size.width,
                                              height: lineWidth)
            separatorAttribute.zIndex = Int.max
            decorationAttributes.append(separatorAttribute)
        }

        return layoutAttributes + decorationAttributes
    }
}

private final class SeparatorView: UICollectionReusableView {
    private let imageView: UIImageView = {
        let iv = UIImageView(image: UIImage(named: "cell-divider"))
        iv.contentMode = .scaleAspectFit
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        addSubview(imageView)

        imageView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
        imageView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
        imageView.topAnchor.constraint(equalTo: topAnchor).isActive = true
        imageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    }

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

    override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
        self.frame = layoutAttributes.frame
    }
}

此解决方案实际上有效,并且我能够看到分隔线。但是,当用户单击单元格之一时,就会出现问题。我想要的行为是单击该单元格时,该单元格可以展开以显示更多详细信息。我实现此方法的方式是跟踪所选的indexPath,如果在sizeForItemAt中选择了它们,则返回更大的大小。在didSelectItemAt中,我重新加载了集合视图。当我使用普通的UICollectionViewFlowLayout时,这种方法有效,但是当我尝试使用自定义流布局(如上)时,出现以下崩溃:

no UICollectionViewLayoutAttributes instance for -layoutAttributesForDecorationViewOfKind: separator at path <NSIndexPath: 0xf75c5b66b8a0a8ab> {length = 2, path = 0 - 6}

我尝试查找解决方案,发现这两个堆栈溢出herehere,但是我尝试的所有答案似乎都不起作用。

我尝试过:

  1. 重新加载集合视图时使布局无效。
  2. 实现在自定义布局中覆盖layoutAttributesForItem时返回的缓存。

在此刻,任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:0)

似乎我必须在自定义布局中覆盖两个方法:

    override func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let layoutAttributes = UICollectionViewLayoutAttributes(forDecorationViewOfKind: elementKind,
                                                                with: indexPath)
        return layoutAttributes;
    }

    override func initialLayoutAttributesForAppearingDecorationElement(ofKind elementKind: String, at decorationIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attributes = layoutAttributesForDecorationView(ofKind: elementKind,
                                                           at: decorationIndexPath)
        return attributes
    }

重新加载数据时,我还需要保留布局无效调用。

collectionView.reloadData()
let context = collectionViewLayout.invalidationContext(forBoundsChange: bounds)
context.contentOffsetAdjustment = CGPoint.zero
collectionView.collectionViewLayout.invalidateLayout(with: context)

layoutSubviews()
相关问题