iOS 实现三张图片左右循环滚动丝滑的效果,大家有没有什么思路?

2023-09-07 16:13:32 +08:00
 tudoutiaoya

我的思路是用 UICollectionView ,定义 100 组(每组就是那三张图片),然后当向左滑动到头的时候或者向右滑动到头的时候,定位到中间 50 组数据的部分,但是这样会有一个切换效果:就是当你向左滑动,本来动画是向左滑动的动画,但是当向左滑动到头的时候,他会向右切换到中间的位置,同样的向右滑动也是这样的道理,导致有明显的切换效果,大家有什么解决方案吗

或者采用其他什么思路实现左右循环滚动丝滑的切换

下面是我的代码


import UIKit

class HorizontalRollViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
    
    let collectionView: UICollectionView
    let images = ["image1", "image2", "image3"]
    let layout: UICollectionViewFlowLayout
    
    var myOffsetX = 0.0 // 记录上次的 offsetx 便于判断是左滑还是右滑
    let groupNum = 100 // 定义多少个组
    
    let lineSpacing = 30.0
    let itemWidth = UIScreen.main.bounds.width/2 // 卡片宽度
    let itemHeigh = UIScreen.main.bounds.height/2
    
    init() {
        layout = UICollectionViewFlowLayout()
        layout.minimumLineSpacing = self.lineSpacing
        layout.itemSize = CGSize(width: itemWidth, height: itemHeigh)
        layout.scrollDirection = .horizontal
        
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.decelerationRate = .fast
        
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        view.backgroundColor = .white
        setupSubView()
        // 初始定位到中间
        collectionView.scrollToItem(at: IndexPath.init(item: groupNum/2 * images.count , section: 0), at: .centeredHorizontally, animated: false)
    }
    
    func setupSubView() {
        collectionView.frame = view.bounds
        collectionView.dataSource = self
        collectionView.delegate = self
        collectionView.isPagingEnabled = false
        // 注册单元格
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "UICollectionViewCell")
        view.addSubview(collectionView)
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // 图片的数量
        return groupNum * images.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "UICollectionViewCell", for:indexPath)
        // 移除之前的子视图
        cell.contentView.subviews.forEach { $0.removeFromSuperview() }
        // 取余 计算出应该在 images 数组哪个位置
        let imageIndex = indexPath.item % images.count
        let imageView = UIImageView(image: UIImage(named: images[imageIndex]))
        imageView.frame = cell.bounds
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        cell.contentView.addSubview(imageView)
        return cell
    }
    
    
    func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
        // 停止滑动时,当前的偏移量(即最近停止的位置)
        self.myOffsetX = scrollView.contentOffset.x
    }
    
    // collectionView.pagingEnabled = NO;
    // 禁止分页滑动时,根据偏移量判断滑动到第几个 item
    // 滑动 “减速滚动时” 是触发的代理,当用户用力滑动或者清扫时触发
    func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
        self.scrollToNextPageOrLastPage(scrollView)
    }
    
    // 用户拖拽时 调用
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        self.scrollToNextPageOrLastPage(scrollView)
    }

    func scrollToNextPageOrLastPage(_ scrollView: UIScrollView) {
        
        // 到达左右边界,定位到中间
        let contentWidth = (itemWidth+lineSpacing) * Double(groupNum*images.count) // 内容的总宽度
        let adjustedContentWidth = contentWidth - lineSpacing // 调整后的内容宽度,减去最后一个间距
        let rightOffset = adjustedContentWidth - scrollView.bounds.width // 右侧边界的偏移量
        if (scrollView.contentOffset.x >= rightOffset || scrollView.contentOffset.x <= 0) {
            collectionView.scrollToItem(at: IndexPath.init(item: groupNum/2 * images.count , section: 0), at: .centeredHorizontally, animated: false)
            print("切换了")
            return
        }
        
        // 之前停止的位置,判断左滑、右滑
        if (scrollView.contentOffset.x > self.myOffsetX) { // 左滑,下一个( i 最大为 cell 个数)
            
            // 计算移动的 item 的个数( item.width + 间距)
            let i = Int(scrollView.contentOffset.x / (itemWidth + lineSpacing) + 1)

            let indexPath = IndexPath(row: i, section: 0)
            // item 居中显示
            collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
            
        } else { // 右滑,上一个( i 最小为 0 )
            
            let i = Int(scrollView.contentOffset.x / (itemWidth + lineSpacing) + 1)
            
            let indexPath = IndexPath(row: i, section: 0)
            
            collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)

        }
        
        
    }
}



1351 次点击
所在节点    iOS
8 条回复
tsanie
2023-09-07 16:25:35 +08:00
这种需求用 UIPageViewController 是不是好点
kera0a
2023-09-07 16:26:38 +08:00
就三个 UIImageView 放到 UIScrollView 上,监听 scrollView 的滚动,到临界点了调整已经移出屏幕的 ImageView 坐标。
调整 frame 性能开销很小,你只要计算好了可以无限滚动循坏

另外这种效果 github 搜一下能搜到 1000 个,不用自己写
tizzy1
2023-09-07 16:29:25 +08:00
看看有没有分页效果,有分页效果就 UIScrollView+三张图片的思路,没有分页效果还要很丝滑,就 UICollectionView ,不过不用定义 100 组,itemCount 那个代理方法直接返回 10000 ,在 cellforRowAtIndexPath 方法里处理逻辑,反正 cell 都是复用的,绝对丝滑!!
chiaf
2023-09-07 16:31:18 +08:00
可以去看下官方的 swiftUI 教程,里面有使用 UIPageViewController 实现轮播图的 demo 😂。
但是 UIPageViewController 这个类据说有 bug
tudoutiaoya
2023-09-07 17:01:16 +08:00
tudoutiaoya
2023-09-07 17:03:54 +08:00
@kera0a UIScrollView 能实现,一个屏幕中显示三张图片吗,中间图片完整的显示在中间,左右两边分别有上一张和下一张的图片,🤔,我试试
xaoflysho
2023-09-07 17:14:25 +08:00
翻了好久,终于找到了!当时按照这个视频写了一个可以无限滚动的 Dashboard
<amp-youtube data-videoid="Q2NLgfYN7Pg" layout="responsive" width="480" height="270"></amp-youtube>&list=PLmEZjI7vcqES-hEaDBXVFtyJ_5mUa0umr&index=1

视频里的代码在这里: https://github.com/gspiers/LoopLayout
tudoutiaoya
2023-09-07 17:25:38 +08:00
@xaoflysho 感谢佬

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/971771

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX