V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
tudoutiaoya
V2EX  ›  iOS

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

  •  
  •   tudoutiaoya · 2023-09-07 16:13:32 +08:00 · 1325 次点击
    这是一个创建于 445 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我的思路是用 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)
    
            }
            
            
        }
    }
    
    
    
    
    8 条回复    2023-09-07 17:25:38 +08:00
    tsanie
        1
    tsanie  
       2023-09-07 16:25:35 +08:00
    这种需求用 UIPageViewController 是不是好点
    kera0a
        2
    kera0a  
       2023-09-07 16:26:38 +08:00 via iPhone
    就三个 UIImageView 放到 UIScrollView 上,监听 scrollView 的滚动,到临界点了调整已经移出屏幕的 ImageView 坐标。
    调整 frame 性能开销很小,你只要计算好了可以无限滚动循坏

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

    视频里的代码在这里: https://github.com/gspiers/LoopLayout
    tudoutiaoya
        8
    tudoutiaoya  
    OP
       2023-09-07 17:25:38 +08:00
    @xaoflysho 感谢佬
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1076 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 19:43 · PVG 03:43 · LAX 11:43 · JFK 14:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.