不滚动时模仿照片应用程序集合视图间距

问题描述 投票:0回答:1

请使用此 gif 作为预期结果的参考

工作正在进行中并在此 github 中公开

我使用默认的

UICollectionViewFlowLayout
并在滚动不运动时为所选单元格设置自定义大小。我可以做什么来模仿所选单元格与其相邻单元格之间的额外间距?

如果我理解正确,为了在单元格之间具有不同的间距,我必须编写 UICollectionViewLayout 的自定义子类。然而,这个间距与滚动行为有关,并且将它们混合在一起,我对如何编写实现一无所知。我正在处理的公共 github 上的一些代码:

private func setupCollectionView() {
    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .horizontal
    layout.minimumInteritemSpacing = itemSpacing

    let sideInset = (view.bounds.width - 44) / 2 // Ensure first and last items center-align
    layout.sectionInset = UIEdgeInsets(top: 0, left: sideInset, bottom: 0, right: sideInset)
    collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
// ...
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    if indexPath == collectionView.indexPathsForSelectedItems?.first {
        if !isScrollingInMotion {
            return .init(width: 44, height: 44)
        }
    }

    return .init(width: 32, height: 44)
}

这里我使用

isScrollingInMotion
来检查 collectionView 是否正在滚动。当调用
scrollViewDidScroll
时,它会变为 true,而当调用
scrollViewDidEndDecelerating
scrollViewDidEndDragging
时,它会变为 false。它还跟踪滚动是来自用户 panGesture 还是其他原因(例如,选择该项目直接导致滚动视图将单元格居中)

ios swift uicollectionview uiscrollview spacing
1个回答
0
投票

感谢 @matt 的建议,我成功地按预期增加了间距:

private var isScrollingInMotion = false { didSet {
    guard isScrollingInMotion != oldValue else { return }

    if isScrollingInMotion {
        collectionView.collectionViewLayout = photoViewerScrollingLayout
    } else {
        collectionView.collectionViewLayout = photoViewerStaticLayout
    }
}}

private let photoViewerScrollingLayout = UICollectionViewFlowLayout()
private lazy var photoViewerStaticLayout = PhotoViewerCollectionViewLayout(scrollingLayout: photoViewerScrollingLayout)

private func setupCollectionView() {
    photoViewerScrollingLayout.scrollDirection = .horizontal
    photoViewerScrollingLayout.minimumInteritemSpacing = itemSpacing

    let sideInset = (view.bounds.width - 44) / 2 // Ensure first and last items center-align
    photoViewerScrollingLayout.sectionInset = UIEdgeInsets(top: 0, left: sideInset, bottom: 0, right: sideInset)

    collectionView = UICollectionView(frame: .zero, collectionViewLayout: photoViewerStaticLayout)

我的布局属性:

/// use when scrolling is not in motion
final class PhotoViewerCollectionViewLayout: UICollectionViewFlowLayout {
    init(scrollingLayout: UICollectionViewFlowLayout) {
        super.init()
        scrollDirection = scrollingLayout.scrollDirection
        minimumInteritemSpacing = scrollingLayout.minimumInteritemSpacing
        sectionInset = scrollingLayout.sectionInset
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil }
        guard let collectionView = collectionView else { return attributes }
        guard let selectedIndexPath = collectionView.indexPathsForSelectedItems?.first else { return attributes }

        let updatedAttributes = attributes.map { $0.copy() as! UICollectionViewLayoutAttributes }
        let itemSpacing = minimumInteritemSpacing + 5

        for attribute in updatedAttributes {
            if attribute.indexPath.item > selectedIndexPath.item {
                // Custom item on the right
                attribute.frame.origin.x += itemSpacing

            } else if attribute.indexPath.item == selectedIndexPath.item {
                attribute.size.width = attribute.size.height
            } else {
                // Custom item on the left
                attribute.frame.origin.x -= itemSpacing
            }
        }

        return updatedAttributes
    }
}

动画看起来不错(有点抓不住,但可以接受)

private func snapSelectedCenter() {
    guard !isUserScrolling else { return }
    guard let indexPath = collectionView.indexPathsForSelectedItems?.first else { return }
    UIView.animate(withDuration: 0.2) {
        if !self.isScrollingInMotion {
            self.collectionView.collectionViewLayout.invalidateLayout()
        }

        self.isScrollingInMotion = false
        self.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
    }
}

func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    isUserScrolling = true
    UIView.animate(withDuration: 0.2) {
        self.isScrollingInMotion = true
        self.collectionView.collectionViewLayout.invalidateLayout()
    }
}

现在,只有默认值

minimumInteritemSpacing
存在问题:如果
itemSpacing: CGFloat
不是8(例如0),当我滚动(userBeginDragging)时,间距突然被设置为默认值(8)而不是我的预设号码(例如 0)。也许我应该进一步子类化?我不确定...

© www.soinside.com 2019 - 2024. All rights reserved.