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

249 天前
 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)

        }
        
        
    }
}



1075 次点击
所在节点    iOS
8 条回复
tsanie
249 天前
这种需求用 UIPageViewController 是不是好点
kera0a
249 天前
就三个 UIImageView 放到 UIScrollView 上,监听 scrollView 的滚动,到临界点了调整已经移出屏幕的 ImageView 坐标。
调整 frame 性能开销很小,你只要计算好了可以无限滚动循坏

另外这种效果 github 搜一下能搜到 1000 个,不用自己写
tizzy1
249 天前
看看有没有分页效果,有分页效果就 UIScrollView+三张图片的思路,没有分页效果还要很丝滑,就 UICollectionView ,不过不用定义 100 组,itemCount 那个代理方法直接返回 10000 ,在 cellforRowAtIndexPath 方法里处理逻辑,反正 cell 都是复用的,绝对丝滑!!
chiaf
249 天前
可以去看下官方的 swiftUI 教程,里面有使用 UIPageViewController 实现轮播图的 demo 😂。
但是 UIPageViewController 这个类据说有 bug
tudoutiaoya
249 天前
tudoutiaoya
249 天前
@kera0a UIScrollView 能实现,一个屏幕中显示三张图片吗,中间图片完整的显示在中间,左右两边分别有上一张和下一张的图片,🤔,我试试
xaoflysho
249 天前
翻了好久,终于找到了!当时按照这个视频写了一个可以无限滚动的 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
249 天前
@xaoflysho 感谢佬

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

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

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

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

© 2021 V2EX