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
相关产品推荐
相关产品推荐

