gpt4 book ai didi

ios - `setCollectionViewLayout` 在更改 Collection View 框架时动画损坏

转载 作者:行者123 更新时间:2023-12-04 11:26:02 25 4
gpt4 key购买 nike

我正在尝试在 之间转换我的收藏 View 水平胶片布局和 垂直堆栈布局。


水平膜条
垂直/扩展堆栈


Cells horizontally aligned in a strip
Cells vertically stacked


这就是我目前正在做的事情:

  • 使用 setCollectionViewLayout(_:animated:completion:) 在水平和垂直滚动方向之间转换
  • 更改集合 View 的高度约束,以及 UIView.animateself.view.layoutIfNeeded()

  • 从胶片到垂直堆栈的动画很好。但是,从垂直堆栈到胶片的动画被破坏了。这是它的样子:
    Transitioning between film-strip and expanded stack, but glitched when going back
    如果我做 collectionViewHeightConstraint默认为 300,并删除这些行:
    collectionViewHeightConstraint.constant = 300
    collectionViewHeightConstraint.constant = 50
    ...过渡动画两种方式都很好。但是,有多余的间距,我希望胶片布局仅在 1 行中。
    Transitioning between film-strip and expanded stack, not glitched, but the film-strip layout has 5 rows and 2 columns instead of 1 row
    如何使动画双向流畅?这是我的代码( link to the demo project ):
    class ViewController: UIViewController {

    var isExpanded = false
    var verticalFlowLayout = UICollectionViewFlowLayout()
    var horizontalFlowLayout = UICollectionViewFlowLayout()

    @IBOutlet weak var collectionView: UICollectionView!
    @IBOutlet weak var collectionViewHeightConstraint: NSLayoutConstraint!
    @IBAction func toggleExpandPressed(_ sender: Any) {

    isExpanded.toggle()
    if isExpanded {
    collectionView.setCollectionViewLayout(verticalFlowLayout, animated: true) /// set vertical scroll
    collectionViewHeightConstraint.constant = 300 /// make collection view height taller
    } else {
    collectionView.setCollectionViewLayout(horizontalFlowLayout, animated: true) /// set horizontal scroll
    collectionViewHeightConstraint.constant = 50 /// make collection view height shorter
    }

    /// animate the collection view's height
    UIView.animate(withDuration: 1) {
    self.view.layoutIfNeeded()
    }

    /// Bonus points:
    /// This makes the animation way more worse, but I would like to be able to scroll to a specific IndexPath during the transition.
    // collectionView.scrollToItem(at: IndexPath(item: 9, section: 0), at: .bottom, animated: true)
    }

    override func viewDidLoad() {
    super.viewDidLoad()
    verticalFlowLayout.scrollDirection = .vertical
    horizontalFlowLayout.scrollDirection = .horizontal

    collectionView.collectionViewLayout = horizontalFlowLayout
    collectionView.dataSource = self
    collectionView.delegate = self
    }
    }

    extension ViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    /// if expanded, cells should be full-width
    /// if not expanded, cells should have a width of 50
    return isExpanded ? CGSize(width: collectionView.frame.width, height: 50) : CGSize(width: 100, height: 50)
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 0 /// no spacing needed for now
    }
    }

    /// sample data source
    extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 10
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ID", for: indexPath)
    cell.contentView.layer.borderWidth = 5
    cell.contentView.layer.borderColor = UIColor.red.cgColor
    return cell
    }
    }

    最佳答案

    我通过使用自定义 UICollectionViewFlowLayout 让它工作了类( link to demo repo )。中心单元格甚至在两种布局中都保持居中(这实际上是我问题中“奖励积分”部分的确切目的)!
    Cells animate and transition between film-strip and vertical stack smoothly
    这是我的 View Controller 。而不是遵守 UICollectionViewDelegateFlowLayout ,我现在使用自定义闭包 sizeForListItemAtsizeForStripItemAt .

    class ViewController: UIViewController {

    var isExpanded = false
    lazy var listLayout = FlowLayout(layoutType: .list)
    lazy var stripLayout = FlowLayout(layoutType: .strip)

    @IBOutlet weak var collectionView: UICollectionView!
    @IBOutlet weak var collectionViewHeightConstraint: NSLayoutConstraint!
    @IBAction func toggleExpandPressed(_ sender: Any) {

    isExpanded.toggle()
    if isExpanded {
    collectionView.setCollectionViewLayout(listLayout, animated: true)
    collectionViewHeightConstraint.constant = 300
    } else {
    collectionView.setCollectionViewLayout(stripLayout, animated: true)
    collectionViewHeightConstraint.constant = 60
    }
    UIView.animate(withDuration: 0.6) {
    self.view.layoutIfNeeded()
    }
    }

    override func viewDidLoad() {
    super.viewDidLoad()
    collectionView.collectionViewLayout = stripLayout
    collectionView.dataSource = self

    /// use these instead of `UICollectionViewDelegateFlowLayout`
    listLayout.sizeForListItemAt = { [weak self] indexPath in
    return CGSize(width: self?.collectionView.frame.width ?? 100, height: 50)
    }
    stripLayout.sizeForStripItemAt = { indexPath in
    return CGSize(width: 100, height: 50)
    }
    }
    }

    /// sample data source
    extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 10
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ID", for: indexPath)
    cell.contentView.layer.borderWidth = 5
    cell.contentView.layer.borderColor = UIColor.red.cgColor
    return cell
    }
    }
    然后,这是我的自定义 UICollectionViewFlowLayout类(class)。内 prepare ,我手动计算并设置每个单元格的帧。我不确定为什么会这样,但是系统现在能够确定哪些单元格等于哪些单元格,甚至跨越多个 FlowLayout s( listLayoutstripLayout .)。
    enum LayoutType {
    case list
    case strip
    }

    class FlowLayout: UICollectionViewFlowLayout {

    var layoutType: LayoutType
    var sizeForListItemAt: ((IndexPath) -> CGSize)? /// get size for list item
    var sizeForStripItemAt: ((IndexPath) -> CGSize)? /// get size for strip item

    var layoutAttributes = [UICollectionViewLayoutAttributes]() /// store the frame of each item
    var contentSize = CGSize.zero /// the scrollable content size of the collection view
    override var collectionViewContentSize: CGSize { return contentSize } /// pass scrollable content size back to the collection view

    override func prepare() { /// configure the cells' frames
    super.prepare()

    guard let collectionView = collectionView else { return }
    let itemCount = collectionView.numberOfItems(inSection: 0) /// I only have 1 section

    if layoutType == .list {
    var y: CGFloat = 0 /// y position of each cell, start at 0
    for itemIndex in 0..<itemCount {
    let indexPath = IndexPath(item: itemIndex, section: 0)
    let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
    attributes.frame = CGRect(
    x: 0,
    y: y,
    width: sizeForListItemAt?(indexPath).width ?? 0,
    height: sizeForListItemAt?(indexPath).height ?? 0
    )
    layoutAttributes.append(attributes)
    y += attributes.frame.height /// add height to y position, so next cell becomes offset
    } /// use first item's width
    contentSize = CGSize(width: sizeForStripItemAt?(IndexPath(item: 0, section: 0)).width ?? 0, height: y)
    } else {
    var x: CGFloat = 0 /// z position of each cell, start at 0
    for itemIndex in 0..<itemCount {
    let indexPath = IndexPath(item: itemIndex, section: 0)
    let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
    attributes.frame = CGRect(
    x: x,
    y: 0,
    width: sizeForStripItemAt?(indexPath).width ?? 0,
    height: sizeForStripItemAt?(indexPath).height ?? 0
    )
    layoutAttributes.append(attributes)
    x += attributes.frame.width /// add width to z position, so next cell becomes offset
    } /// use first item's height
    contentSize = CGSize(width: x, height: sizeForStripItemAt?(IndexPath(item: 0, section: 0)).height ?? 0)
    }
    }

    /// pass attributes to the collection view flow layout
    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    return layoutAttributes[indexPath.item]
    }
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    return layoutAttributes.filter { rect.intersects($0.frame) }
    }

    /// initialize with a layout
    init(layoutType: LayoutType) {
    self.layoutType = layoutType
    super.init()
    }

    /// boilerplate code
    required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { return true }
    override func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext {
    let context = super.invalidationContext(forBoundsChange: newBounds) as! UICollectionViewFlowLayoutInvalidationContext
    context.invalidateFlowLayoutDelegateMetrics = newBounds.size != collectionView?.bounds.size
    return context
    }
    }
    感谢 this amazing article巨大的帮助。

    关于ios - `setCollectionViewLayout` 在更改 Collection View 框架时动画损坏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68062344/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com