为啥在 Swift 中 CollectionView 函数被调用了两次?

Posted

技术标签:

【中文标题】为啥在 Swift 中 CollectionView 函数被调用了两次?【英文标题】:Why is the CollectionView function called twice in Swift?为什么在 Swift 中 CollectionView 函数被调用了两次? 【发布时间】:2020-02-07 05:57:52 【问题描述】:

我在函数被调用 2 次时遇到问题。因此,PageView 中的值设置不正确,因为索引值不正确。如何正确控制?

ViewController.swift

class ViewController: UIViewController 

    @IBOutlet weak var collectionView: UICollectionView!
    @IBOutlet weak var pageView: UIPageControl!
    @IBOutlet var guideView: UIView!
    var step : Int! = 0
    var beforeStep : Int! = 0

    var imageArray = [UIImage(named: "swipe1"),
                      UIImage(named: "swipe2"),
                      UIImage(named: "swipe3"),
                      UIImage(named: "swipe4"),
                      UIImage(named: "swipe5")]


    override func viewDidLoad() 
        super.viewDidLoad()
        pageView.numberOfPages = imageArray.count
        pageView.currentPage = 0
        pageView.currentPageIndicatorTintColor = UIColor.color(.dacColor)
        pageView.pageIndicatorTintColor = UIColor.gray
        view.backgroundColor = UIColor(red: CGFloat(252.0/255.0), green: CGFloat(251.0/255.0), blue: CGFloat(245.0/255.0), alpha: CGFloat(1.0))
        collectionView.backgroundColor = UIColor(red: CGFloat(252.0/255.0), green: CGFloat(251.0/255.0), blue: CGFloat(245.0/255.0), alpha: CGFloat(1.0))
    


extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource 
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
        return imageArray.count
    

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        if let vc = cell.viewWithTag(111) as? UIImageView 
            vc.image = imageArray[indexPath.row]
        
        if let ab = guideView.viewWithTag(222) as? UIPageControl 
            ab.currentPage = indexPath.row - 1
        
        return cell
    


extension ViewController: UICollectionViewDelegateFlowLayout 
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
        let size = collectionView.frame.size
        return CGSize(width: size.width, height: size.height)
    

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat 
        return 0
    

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat 
        return 0
    

有问题的功能部分

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        if let vc = cell.viewWithTag(111) as? UIImageView 
            vc.image = imageArray[indexPath.row]
        
        if let ab = guideView.viewWithTag(222) as? UIPageControl 
            ab.currentPage = indexPath.row - 1
        
        return cell
    

StoryBoard 中的 CollectionView

在 Storyboard 中查看列表

我希望将 PageController 标记为与图片的位置相匹配。

有没有人和我一样的问题解决过?

【问题讨论】:

你需要将 UIPageControl 放在集合视图之外(在主视图中),当你滚动时,你需要使用滚动代理找到索引 @SiddhantNigam 我将图片更新为问题。如您所见,PageController 不在 CollectionView 中。 @SiddhantNigam 我应该如何使用滚动委托?告诉我答案。 试试 FSPagerView 很简单:github.com/WenchaoD/FSPagerView 如果你不喜欢这个试试这个,然后我会分享你收集视图的代码 【参考方案1】:

collectionView(_:cellForItemAt:) 是一个集合视图委托方法,它往往会被多次调用,触发调用的因素很多,例如:

    可见单元格的数量,取决于集合的高度

    滚动单元格以重复使用它们时

或触发更改/刷新集合,如:

    reloadData()reloadItems(at:)reloadSections(_:)

    可能是来自包含此集合视图的视图的layoutIfNeeded() 调用。

还有更多...

关键是,函数被多次调用是很正常的。您的答案可能在于“什么触发了通话?”

编辑

根据集合的索引更新你的pageControl:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) 
   let visibleRect = CGRect(origin: yourCollectionView.contentOffset, size: yourCollectionView.bounds.size)
   let midPointOfVisibleRect = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
   if let visibleIndexPath = yourCollectionView.indexPathForItem(at: midPointOfVisibleRect) 
            yourPageControl.currentPage = visibleIndexPath.row
   

如果您仅将UIPageViewController 用于页面指示,则应使用UIPageControl。您可能还需要设置yourCollectionView.isPagingEnabled = true,因为这更适合页面控件。

【讨论】:

不能正常控制PageController吗? 好的,刚刚看到您编辑的问题。这是为了解释函数调用。那么,你需要根据你的集合视图单元格的索引来更新页面控件吗?? 是的,PageController的值必须设置成与图片的索引值相匹配。 我应该在哪里使用你添加的功能? @hongdeveloper 我已经设置了 isPagingEnabled。谢谢你,我解决了这个问题。【参考方案2】:

在集合视图中转到布局并选择自定义:

class CustomCollectionViewFlowLayout: UICollectionViewFlowLayout

override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint

    if let collectionViewBounds = self.collectionView?.bounds
    
        let halfWidthOfVC = collectionViewBounds.size.width * 0.5
        let proposedContentOffsetCenterX = proposedContentOffset.x + halfWidthOfVC
        if let attributesForVisibleCells = self.layoutAttributesForElements(in: collectionViewBounds)
        
            var candidateAttribute : UICollectionViewLayoutAttributes?
            for attributes in attributesForVisibleCells
            
                let candAttr : UICollectionViewLayoutAttributes? = candidateAttribute
                if candAttr != nil
                
                    let a = attributes.center.x - proposedContentOffsetCenterX
                    let b = candAttr!.center.x - proposedContentOffsetCenterX
                    if fabs(a) < fabs(b)
                    
                        candidateAttribute = attributes
                    
                
                else
                
                    candidateAttribute = attributes
                    continue
                
            

            if candidateAttribute != nil
            
                return CGPoint(x: candidateAttribute!.center.x - halfWidthOfVC, y: proposedContentOffset.y);
            
        
    
    return CGPoint.zero


然后在委托中:

 func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) 
    var visibleRect = CGRect()

    visibleRect.origin = Collections.contentOffset
    visibleRect.size = Collections.bounds.size

    let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)

    guard let indexPath = Collections.indexPathForItem(at: visiblePoint) else  return 


    print(indexPath.item)


【讨论】:

我不知道如何使用你在我的代码中扩展的类。 你复制粘贴我给你的课程吗? 是的,我把你给我的课程做了一个文件并复制了。 现在点击storyBoard中的collectionView,你会看到默认布局是流动的,然后你点击并选择自定义,然后文本字段将在那里写下类名并按回车 该类将帮助您滚动中心,scrollViewDidEndDecelerating 帮助您在滚动完成后获取索引路径

以上是关于为啥在 Swift 中 CollectionView 函数被调用了两次?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 CFStringEncodings 在 Swift 中没有 UTF8?

为啥某些 Swift 标准库函数在 Playground 中无法识别?

为啥我的图像无法在 Swift 中正确加载

Swift - 为啥在 AFHTTPSessionManager 中需要 init(coder)?

为啥在 Swift 中甚至需要方便关键字?

为啥在 Swift 中使用“let”而不是 var?