You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift实现图片浏览页滑动翻页时导航箭头未自动隐藏问题

问题描述
  • 实现了photoview图片浏览组件,点击图片后会在前台全屏打开图片查看器,查看器内置左、右箭头按钮,分别用于切换上一张、下一张图片。
  • 点击箭头按钮时功能运行正常:浏览到最左侧首张图片时,左箭头自动隐藏;浏览到最右侧末张图片时,右箭头自动隐藏。
  • 异常表现:在图片区域通过滑动手势切换图片时,箭头自动隐藏逻辑不生效,箭头会持续显示不会自动隐藏。
  • 排查过程:最初编写了绑定滑动手势的gesSwipe()方法,期望实现滑动时和点击箭头一致的箭头隐藏逻辑,但该方法仅当滑动操作作用在箭头按钮上方时才会触发,在图片区域滑动时不会触发;且滑动操作作用在箭头上时,本身也无法触发图片翻页动作。最终确认图片区域本身支持滑动翻页,但绑定的gesSwipe()方法始终不会被调用。
初始实现代码

PhotoViewcontroller.swift初始代码如下:

import Foundation
import UIKit

struct PhotoParam {
    let urls: [URL]
}

class PhotoViewController: ModalViewController, UIGestureRecognizerDelegate {
    // MARK: 属性
    @IBOutlet private weak var imageCollection: UICollectionView!
    @IBOutlet weak var arrowLeftButton: UIButton!
    @IBOutlet weak var arrowRightButton: UIButton!
    
    var urls: [URL] = [] {
        didSet {
            imageCollection?.reloadData()
        }
    }
    
    // MARK: 初始化
    override func initPresenter() {
        
    }
    
    override func setParam(param: Any) {
        guard let param = param as? PhotoParam else {
            return
        }
        
        self.urls = param.urls
    }
    
    
    @IBAction func gesSwipe(_ sender: UISwipeGestureRecognizer) {
        
        let screenW = UIScreen.main.bounds.width
        let offset: CGPoint = imageCollection.contentOffset
        let page = CGFloat(Int(offset.x / screenW) - 1)

        
            switch sender.direction {

            case .left:
                print("swipe right")
                print("%%%", page)
                print("%%%", CGFloat(urls.count - 2))
                if page >= CGFloat(urls.count - 2) {
                    self.arrowRightButton.isHidden = true
                } else {
                    self.arrowRightButton.isHidden = false
                }
                
            case .right:
                print("swipe left")
                if page < 0 {
                    self.arrowLeftButton.isHidden = true
                } else {
                    self.arrowLeftButton.isHidden = false
                }

            default:
                break
            }
    }
    
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.arrowLeftButton.isHidden = true
        self.imageCollection.delegate = self
        self.imageCollection.dataSource = self
        self.imageCollection.isPagingEnabled = true
        self.imageCollection.reloadData()
        

        let leftSwipeGesture = UISwipeGestureRecognizer(
                target: self,
                action: #selector(PhotoViewController.gesSwipe(_:))
        
        )
        
        leftSwipeGesture.direction = .left
        self.view.addGestureRecognizer(leftSwipeGesture)
        
        
        let rightSwipeGesture = UISwipeGestureRecognizer(
                target: self,
                action: #selector(PhotoViewController.gesSwipe(_:))
        
        )
        
        rightSwipeGesture.direction = .right
        self.view.addGestureRecognizer(rightSwipeGesture)
        
    }
    
    
    // MARK: 方法
    @IBAction func arrowLeftTapped(_ sender: Any) {
        let screenW = UIScreen.main.bounds.width
        let offset: CGPoint = imageCollection.contentOffset
        let page = CGFloat(Int(offset.x / screenW) - 1)
        self.arrowRightButton.isHidden = false
        if page <= 0 {
            self.arrowLeftButton.isHidden = true
        } else {
            self.arrowLeftButton.isHidden = false
        }
        if page >= 0 {
            imageCollection.setContentOffset(CGPoint(x: screenW * page, y: 0), animated: true)
        }
    }
    
    @IBAction func arrowRightTapped(_ sender: Any) {
        let screenW = UIScreen.main.bounds.width
        let offset: CGPoint = imageCollection.contentOffset
        let page = CGFloat(Int(offset.x / screenW) + 1)
        self.arrowLeftButton.isHidden = false
        if page >= CGFloat(urls.count - 1) {
            self.arrowRightButton.isHidden = true
        } else {
            self.arrowRightButton.isHidden = false
        }
        if page < CGFloat(urls.count) {
            imageCollection.setContentOffset(CGPoint(x: screenW * page, y: 0), animated: true)
        }
    }
}

extension PhotoViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    // section数量
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    // 单元格数量
    func collectionView(_ collectionView: UICollectionView,
                        numberOfItemsInSection section: Int) -> Int {
        return self.urls.count
    }
    
    // 单元格生成
    func collectionView(_ collectionView: UICollectionView,
                        cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let photoCell: PhotoCell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as! PhotoCell

        if indexPath.row < self.urls.count {
            photoCell.setImageURL(url: self.urls[indexPath.row])
        }
        return photoCell
    }
    
    // 根据屏幕尺寸返回对应单元格大小
    // 需要遵循UICollectionViewDelegateFlowLayout协议
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
    }
}

extension PhotoViewController: PhotoCellDelegate {
    func isNotFound(url: URL) {
        if let index = urls.firstIndex(of: url) {
            self.urls.remove(at: index)
            self.imageCollection.reloadData()
        }
    }
}
问题原因

UICollectionView继承自UIScrollView,内部已经实现了完整的滑动手势处理逻辑,外部直接添加到根视图的滑动手势会被UICollectionView的内部手势拦截,仅在手势作用于collectionView覆盖范围外的上层视图(比如箭头按钮)时才会触发,因此图片区域滑动时gesSwipe()方法始终不会执行。

修复方案

无需额外添加滑动手势,直接实现UIScrollView的代理方法,在拖拽结束、定位到目标滚动位置时判断当前所在页码,同步更新左右箭头的显隐状态即可。
注:修复后可移除之前额外添加的左右滑动手势及对应的gesSwipe()方法,避免冗余逻辑。

可用实现代码如下:

public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        let pageWidth = self.imageCollection.bounds.width
        let targetXContentOffset = Float(targetContentOffset.pointee.x)
        let contentWidth = Float(self.imageCollection!.contentSize.width)
        
        // 滚动到首张图片时隐藏左箭头
        if targetXContentOffset == 0.0 {
            self.arrowLeftButton.isHidden = true
        } else {
            self.arrowLeftButton.isHidden = false
        }
        
        // 滚动到末张图片时隐藏右箭头
        if contentWidth == targetXContentOffset + Float(pageWidth) {
            self.arrowRightButton.isHidden = true
        } else {
            self.arrowRightButton.isHidden = false
        }
    }

内容的提问来源于stack exchange,提问作者Marcel Kopera

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 22:33:25