无限滚动构图布局迅速

发布于 2025-01-19 12:38:00 字数 900 浏览 4 评论 0原文

我正在制作一个使用构图布局的应用程序,用于CollectionView,我的Carrousel布局水平显示元素。

let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(100), heightDimension: .absolute(100))
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    item.contentInsets = NSDirectionalEdgeInsets(top: 2, leading: 2, bottom: 2, trailing: 2)
    let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(100), heightDimension: .fractionalWidth(1 / 3))
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
    group.contentInsets = .init(top: 4, leading: 0, bottom: 4, trailing: 0)
    let section = NSCollectionLayoutSection(group: group)
    section.orthogonalScrollingBehavior = .continuous

我如何才能实现无限的滚动,这意味着每次我们获得最后一个元素时,它都会回到第一个元素并继续滚动? (目前它可以正常运行,我可以滚动元素,但是每次显示最后一个元素时,我都必须以另一种方式滚动)

我已经看到了许多通过旧的CollectionView系统实现此目的的方法,但是我想继续使用新的方法

I am making an app that is using compositional layout for the collectionview I'm having a carrousel layout that display elements horizontally.

let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(100), heightDimension: .absolute(100))
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    item.contentInsets = NSDirectionalEdgeInsets(top: 2, leading: 2, bottom: 2, trailing: 2)
    let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(100), heightDimension: .fractionalWidth(1 / 3))
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
    group.contentInsets = .init(top: 4, leading: 0, bottom: 4, trailing: 0)
    let section = NSCollectionLayoutSection(group: group)
    section.orthogonalScrollingBehavior = .continuous

How can I achieve infinite scrolling meaning that every time we get the last element it goes back to the first element and continue to scroll? (for now it works, I can scroll element but every time the last element is displayed I have to scroll back the other way)

I have seen many way to achieve this with the old collectionview system, but I wanna keep using the new way please

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

戏剧牡丹亭 2025-01-26 12:38:00

维护您的数据源并返回 Int16.max 或任何大数,并执行简单的模运算,以将您的项目从数据源获取到模型中的可重用单元出列

import UIKit

class ViewController: UIViewController {

@IBOutlet weak var collectionView: UICollectionView!

var sections: [Int] = Array(0...7)
var models: [[Int]] = Array(repeating: [1,2,3,4,5,6,7,8], count: 8)

var sectionCellColors: [Int: UIColor] = [
    0 : .blue,
    1 : .red,
    2 : .green,
    3 : .yellow,
    4 : .magenta,
    5 : .brown,
    6 : .gray,
    7 : .black
]

override func viewDidLoad() {
    super.viewDidLoad()
    collectionView.backgroundColor = .cyan
    let layout = createLayout()
    collectionView.setCollectionViewLayout(layout, animated: false)
}

func createLayout() -> UICollectionViewCompositionalLayout {
    let layout = UICollectionViewCompositionalLayout { sectionNum, environment in
        let item = NSCollectionLayoutItem(
            layoutSize: NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(0.2),
                heightDimension: .fractionalHeight(1)))
        item.contentInsets = .init(top: 8, leading: 8, bottom: 8, trailing: 8)
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(300)), subitems: [item])
        let section = NSCollectionLayoutSection(group: group)
        section.orthogonalScrollingBehavior = .continuous
        return section
    }
    return layout
}

override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
    if context.nextFocusedView?.isKind(of: UICollectionViewCell.self) == true {
        context.nextFocusedView?.layer.borderColor = UIColor.purple.cgColor
        context.nextFocusedView?.layer.borderWidth = 2
    }
    if context.previouslyFocusedView?.isKind(of: UICollectionViewCell.self) == true {
        context.nextFocusedView?.layer.borderColor = UIColor.clear.cgColor
        context.nextFocusedView?.layer.borderWidth = 0
    }
}
}

extension ViewController: UICollectionViewDataSource {

func numberOfSections(in collectionView: UICollectionView) -> Int {
    return Int(Int16.max)
//        return sections.count
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return models[section % sections.count].count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID", for: indexPath)
    cell.backgroundColor = sectionCellColors[indexPath.section % sections.count]
    return cell
}
}

Maintain your datasource and return Int16.max or any large number and do simple modulo to get your item from datasource to dequeue reusable cell with your model

import UIKit

class ViewController: UIViewController {

@IBOutlet weak var collectionView: UICollectionView!

var sections: [Int] = Array(0...7)
var models: [[Int]] = Array(repeating: [1,2,3,4,5,6,7,8], count: 8)

var sectionCellColors: [Int: UIColor] = [
    0 : .blue,
    1 : .red,
    2 : .green,
    3 : .yellow,
    4 : .magenta,
    5 : .brown,
    6 : .gray,
    7 : .black
]

override func viewDidLoad() {
    super.viewDidLoad()
    collectionView.backgroundColor = .cyan
    let layout = createLayout()
    collectionView.setCollectionViewLayout(layout, animated: false)
}

func createLayout() -> UICollectionViewCompositionalLayout {
    let layout = UICollectionViewCompositionalLayout { sectionNum, environment in
        let item = NSCollectionLayoutItem(
            layoutSize: NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(0.2),
                heightDimension: .fractionalHeight(1)))
        item.contentInsets = .init(top: 8, leading: 8, bottom: 8, trailing: 8)
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(300)), subitems: [item])
        let section = NSCollectionLayoutSection(group: group)
        section.orthogonalScrollingBehavior = .continuous
        return section
    }
    return layout
}

override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
    if context.nextFocusedView?.isKind(of: UICollectionViewCell.self) == true {
        context.nextFocusedView?.layer.borderColor = UIColor.purple.cgColor
        context.nextFocusedView?.layer.borderWidth = 2
    }
    if context.previouslyFocusedView?.isKind(of: UICollectionViewCell.self) == true {
        context.nextFocusedView?.layer.borderColor = UIColor.clear.cgColor
        context.nextFocusedView?.layer.borderWidth = 0
    }
}
}

extension ViewController: UICollectionViewDataSource {

func numberOfSections(in collectionView: UICollectionView) -> Int {
    return Int(Int16.max)
//        return sections.count
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return models[section % sections.count].count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID", for: indexPath)
    cell.backgroundColor = sectionCellColors[indexPath.section % sections.count]
    return cell
}
}
世态炎凉 2025-01-26 12:38:00

首先,您必须向您的 imagesArray 添加 2 个附加图像,如下所示:

guard let firstImage = images.first, let lastImage = images.last else { return }
images.append(firstImage)
images.insert(lastImage, at: 0)

这样您就可以获得适当的图像数据源。
我们使用 UICollectionView 的 scrollViewDidScroll 方法来实现无限滚动。但是具有 orthogonalScrollingBehavior 的部分不会响应此方法,因此您必须使用 visibleItemsInvalidationHandler

您可以通过以下代码来实现此目的:

section.visibleItemsInvalidationHandler = { [weak self] _, point, _ in
    guard let self = self else { return }
    guard self.isHorizontalScrollingEnabled else { return }

    if point.x <= .zero {
        self.collectionView.scrollToItem(
            at: .init(row: images.count - 2, section: <your section index>),
            at: .centeredHorizontally,
            animated: false
        )
    }

    if point.x >= (groupWidth - section.interGroupSpacing) * CGFloat(images.count - 1) {
        self.collectionView.scrollToItem(
            at: .init(row: 1, section: <your section index>),
            at: .centeredHorizontally,
            animated: false
        )
    }
}

请注意,当您准备好显示时数据(即获得图像响应),在重新加载数据后,您必须调用:

collectionView.scrollToItem(at: .init(row: 1, section: <yout section index>), at: .centeredHorizontally, animated: false)
isHorizontalScrollingEnabled = true

isHorizo​​ntalScrollingEnabled(它的初始值明显等于 false)用于防止在显示数据之前进行的任何计算,并且您实际上需要它,因为在实际启动滚动之前会调用 visibleItemsInvalidationHandler 一些时间。

First, you have to add 2 more additional images to your imagesArray like this:

guard let firstImage = images.first, let lastImage = images.last else { return }
images.append(firstImage)
images.insert(lastImage, at: 0)

So you get appropriate images dataSource.
We use scrollViewDidScroll method with UICollectionView to achieve infinite scrolling. But sections with orthogonalScrollingBehavior doesn't respond to this method, so you have to use visibleItemsInvalidationHandler

You can achieve this by the following code:

section.visibleItemsInvalidationHandler = { [weak self] _, point, _ in
    guard let self = self else { return }
    guard self.isHorizontalScrollingEnabled else { return }

    if point.x <= .zero {
        self.collectionView.scrollToItem(
            at: .init(row: images.count - 2, section: <your section index>),
            at: .centeredHorizontally,
            animated: false
        )
    }

    if point.x >= (groupWidth - section.interGroupSpacing) * CGFloat(images.count - 1) {
        self.collectionView.scrollToItem(
            at: .init(row: 1, section: <your section index>),
            at: .centeredHorizontally,
            animated: false
        )
    }
}

Notice that when you're ready to display data(i.e. get response with images), right after reloading data you have to call:

collectionView.scrollToItem(at: .init(row: 1, section: <yout section index>), at: .centeredHorizontally, animated: false)
isHorizontalScrollingEnabled = true

isHorizontalScrollingEnabled(it's initial value obvious equal to false) serves to prevent any calculations before your data is displayed and you actually need it because visibleItemsInvalidationHandler is called some times before you actually initiate scrolling.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文