कोशिकाओं द्वारा UICollectionView पेजिंग, स्क्रीन नहीं


113

मेरे पास UICollectionViewक्षैतिज स्क्रॉलिंग है और पूरी स्क्रीन के अनुसार हमेशा 2 सेल होते हैं। मुझे सेल की भीख मांगने से रोकने के लिए स्क्रॉल की आवश्यकता है। पेजिंग सक्षम होने के साथ, संग्रह दृश्य पूरे पृष्ठ को स्क्रॉल करता है, जो एक बार में 2 सेल है, और फिर यह बंद हो जाता है।

मुझे एक सेल द्वारा स्क्रॉल करने में सक्षम होना चाहिए, या सेल के किनारे पर रुकने के साथ कई सेल द्वारा स्क्रॉल करना है।

मैंने उपवर्ग UICollectionViewFlowLayoutऔर विधि को लागू करने की कोशिश की targetContentOffsetForProposedContentOffset, लेकिन अभी तक मैं केवल अपने संग्रह दृश्य को तोड़ने में सक्षम था और इसने स्क्रॉल करना बंद कर दिया। क्या इसे प्राप्त करने का कोई आसान तरीका है और कैसे, या मुझे वास्तव में UICollectionViewFlowLayoutउपवर्ग के सभी तरीकों को लागू करने की आवश्यकता है ? धन्यवाद।


1
आपके संग्रह की चौड़ाई स्क्रेन की चौड़ाई और संग्रह के बराबर होनी चाहिए। पेजिंग सक्षम
Erhan

लेकिन मुझे एक बार में 2 सेल दिखाने की जरूरत है। मैं iPad पर हूं, इसलिए 2 कोशिकाएं प्रत्येक स्क्रीन का आधा हिस्सा साझा करती हैं।
मार्टिन कॉल्स

2
का प्रयोग करें targetContentOffsetForProposedContentOffset:withScrollingVelocity:और पेजिंग बंद कर देते हैं
वेन

यही मैं कोशिश कर रहा हूं। कहीं कोई उदाहरण?
मार्टिन कॉल्स

जवाबों:


47

ठीक है, तो मुझे यहाँ समाधान मिला: targetContentOffsetForProposedContentOffset: USollectionViewFlowLayout को उप-लिंक किए बिना विस्मृति

मुझे targetContentOffsetForProposedContentOffsetभीख में तलाश करना चाहिए था ।


1
स्विफ्ट 5 में इस बारे में पूछने वाले किसी के लिए: collectionView.isPagingEnabled = true यह करता है!
मोहम्मद बशीर सिदानी

23

बस विधि को ओवरराइड करें:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    *targetContentOffset = scrollView.contentOffset; // set acceleration to 0.0
    float pageWidth = (float)self.articlesCollectionView.bounds.size.width;
    int minSpace = 10;

    int cellToSwipe = (scrollView.contentOffset.x)/(pageWidth + minSpace) + 0.5; // cell width + min spacing for lines
    if (cellToSwipe < 0) {
        cellToSwipe = 0;
    } else if (cellToSwipe >= self.articles.count) {
        cellToSwipe = self.articles.count - 1;
    }
    [self.articlesCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:cellToSwipe inSection:0] atScrollPosition:UICollectionViewScrollPositionLeft animated:YES];
}

1
कोड के उस टुकड़े ने मुझे बहुत मदद की, मुझे हालांकि वर्तमान स्क्रॉलिंग दिशा के लिए चेक को जोड़ना पड़ा और तदनुसार +/- 0.5 मान के अनुकूल किया।
हेलकरली

1
आप संग्रह
दृश्य

@ यव्या वाह तुम सही हो। isPagingEnabled मेरे लिए काम करता है।
BigSauce

@ यज्ञ महान सामान !!
अनीश कुमार

कैसे pagingEnabled आप लोगों के लिए काम कर रहा है? मूल पेजिंग ऑफसेट पर समाप्त होने से पहले मेरा सुपर गड़बड़ हो जाता है
एथन झाओ

17

कस्टम पेज चौड़ाई के साथ क्षैतिज पेजिंग (स्विफ्ट 4 और 5)

यहां प्रस्तुत कई समाधान कुछ अजीब व्यवहार करते हैं जो ठीक से लागू किए गए पेजिंग की तरह महसूस नहीं करते हैं।


इस ट्यूटोरियल में प्रस्तुत समाधान , हालांकि, कोई समस्या नहीं है। यह सिर्फ एक पूरी तरह से काम पेजिंग एल्गोरिथ्म की तरह लगता है। आप इसे 5 सरल चरणों में कार्यान्वित कर सकते हैं:

  1. निम्न प्रकार को अपने प्रकार में जोड़ें: private var indexOfCellBeforeDragging = 0
  2. collectionView delegateइस तरह सेट करें:collectionView.delegate = self
  3. UICollectionViewDelegateएक्सटेंशन के माध्यम से पुष्टि जोड़ें :extension YourType: UICollectionViewDelegate { }
  4. UICollectionViewDelegateअनुरूपता को लागू करने वाले विस्तार के लिए निम्नलिखित विधि जोड़ें और इसके लिए एक मूल्य निर्धारित करें pageWidth:

    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        let pageWidth = // The width your page should have (plus a possible margin)
        let proportionalOffset = collectionView.contentOffset.x / pageWidth
        indexOfCellBeforeDragging = Int(round(proportionalOffset))
    }
  5. UICollectionViewDelegateअनुरूपता को लागू करने वाले विस्तार के लिए निम्न विधि जोड़ें , के लिए एक ही मान सेट करें pageWidth(आप इस मूल्य को केंद्रीय स्थान पर भी संग्रहीत कर सकते हैं) और इसके लिए एक मूल्य निर्धारित करें collectionViewItemCount:

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        // Stop scrolling
        targetContentOffset.pointee = scrollView.contentOffset
    
        // Calculate conditions
        let pageWidth = // The width your page should have (plus a possible margin)
        let collectionViewItemCount = // The number of items in this section
        let proportionalOffset = collectionView.contentOffset.x / pageWidth
        let indexOfMajorCell = Int(round(proportionalOffset))
        let swipeVelocityThreshold: CGFloat = 0.5
        let hasEnoughVelocityToSlideToTheNextCell = indexOfCellBeforeDragging + 1 < collectionViewItemCount && velocity.x > swipeVelocityThreshold
        let hasEnoughVelocityToSlideToThePreviousCell = indexOfCellBeforeDragging - 1 >= 0 && velocity.x < -swipeVelocityThreshold
        let majorCellIsTheCellBeforeDragging = indexOfMajorCell == indexOfCellBeforeDragging
        let didUseSwipeToSkipCell = majorCellIsTheCellBeforeDragging && (hasEnoughVelocityToSlideToTheNextCell || hasEnoughVelocityToSlideToThePreviousCell)
    
        if didUseSwipeToSkipCell {
            // Animate so that swipe is just continued
            let snapToIndex = indexOfCellBeforeDragging + (hasEnoughVelocityToSlideToTheNextCell ? 1 : -1)
            let toValue = pageWidth * CGFloat(snapToIndex)
            UIView.animate(
                withDuration: 0.3,
                delay: 0,
                usingSpringWithDamping: 1,
                initialSpringVelocity: velocity.x,
                options: .allowUserInteraction,
                animations: {
                    scrollView.contentOffset = CGPoint(x: toValue, y: 0)
                    scrollView.layoutIfNeeded()
                },
                completion: nil
            )
        } else {
            // Pop back (against velocity)
            let indexPath = IndexPath(row: indexOfMajorCell, section: 0)
            collectionView.scrollToItem(at: indexPath, at: .left, animated: true)
        }
    }

इसका उपयोग करने वाले किसी भी व्यक्ति के लिए आपको उस Pop back (against velocity)हिस्से को बदलने की आवश्यकता है collectionViewLayout.collectionView!.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true):। ध्यान दें.centeredHorizontally
मैथ्यू.केम्पसन

@ Matthew.kempson इस बात पर निर्भर करता है कि आप लेआउट को कैसे व्यवहार करना चाहते हैं। जिस लेआउट के साथ मैंने इसका इस्तेमाल किया, .leftवह ठीक था
fredpi

मैंने पाया कि .leftउम्मीद के मुताबिक काम नहीं किया। ऐसा लग रहा था कि सेल बहुत दूर @fredpi
Matthew.kempson

13

ईवा के जवाब का स्विफ्ट 3 संस्करण:

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
  targetContentOffset.pointee = scrollView.contentOffset
    let pageWidth:Float = Float(self.view.bounds.width)
    let minSpace:Float = 10.0
    var cellToSwipe:Double = Double(Float((scrollView.contentOffset.x))/Float((pageWidth+minSpace))) + Double(0.5)
    if cellToSwipe < 0 {
        cellToSwipe = 0
    } else if cellToSwipe >= Double(self.articles.count) {
        cellToSwipe = Double(self.articles.count) - Double(1)
    }
    let indexPath:IndexPath = IndexPath(row: Int(cellToSwipe), section:0)
    self.collectionView.scrollToItem(at:indexPath, at: UICollectionViewScrollPosition.left, animated: true)


}

जब आप सेल की तरफ क्लिक करते हैं, तो एक अजीब ऑफसेट होता है
Maor

हे @ मौर, मुझे नहीं पता कि आपको अभी भी इसकी आवश्यकता है, लेकिन मेरे मामले में जो संग्रह दृश्य पर पेजिंग को अक्षम करने के लिए तय किया गया था।
फर्नांडो माता

2
मुझे यह बहुत पसंद था, लेकिन छोटे छोटे झटपटों के साथ थोड़ा सुस्त लगा, इसलिए मैंने वेग को ध्यान में रखने के लिए कुछ जोड़ा और इसे बहुत चिकना बना दिया: if(velocity.x > 1) { mod = 0.5; } else if(velocity.x < -1) { mod = -0.5; }फिर + modबाद में जोड़ें+ Double(0.5)
Captnwalker1

12

यहाँ सबसे आसान तरीका है जो मैंने पाया है कि स्विफ्ट 4.2 में हॉरिनज़ॉन्टल स्क्रॉल के लिए:

मैं पहली सेल का उपयोग कर रहा हूं visibleCellsऔर तब तक स्क्रॉल कर रहा हूं , अगर पहली दिखाई देने वाली सेल चौड़ाई के आधे से कम दिखाई दे रही है, मैं अगले एक पर स्क्रॉल कर रहा हूं।

अपने संग्रह स्क्रॉल खड़ी , बस बदलने के xद्वाराy और widthद्वाराheight

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    targetContentOffset.pointee = scrollView.contentOffset
    var indexes = self.collectionView.indexPathsForVisibleItems
    indexes.sort()
    var index = indexes.first!
    let cell = self.collectionView.cellForItem(at: index)!
    let position = self.collectionView.contentOffset.x - cell.frame.origin.x
    if position > cell.frame.size.width/2{
       index.row = index.row+1
    }
    self.collectionView.scrollToItem(at: index, at: .left, animated: true )
}

क्या आप कृपया स्रोत लेख का लिंक जोड़ सकते हैं। महान जवाब BTW।
मो। इब्राहिम हसन

@ Md.IbrahimHassan कोई लेख नहीं है, मैं स्रोत हूं। Thx
रोमुलो बीएम

यह काम करता है, लेकिन दुर्भाग्य से अनुभव सुचारू नहीं है
अला इद्दीन चेरिब

सुगमता से आपका क्या मतलब है? मेरे लिए, परिणाम एनिमेटेड बहुत ही सहज है .. मेरा परिणाम यहां
रोमुलो बीएम

1
यह ठीक काम करता है
अनुज कुमार राय

9

आंशिक रूप से स्टीवनओजो के जवाब पर आधारित है। मैंने इसे क्षैतिज स्क्रॉलिंग और नो बाउंस यूआईसीओलेक्शनियन व्यू का उपयोग करके परीक्षण किया है। सेलसाइज़ CollectionViewCell आकार है। स्क्रॉलिंग संवेदनशीलता को संशोधित करने के लिए आप कारक को ट्विक कर सकते हैं।

override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    targetContentOffset.pointee = scrollView.contentOffset
    var factor: CGFloat = 0.5
    if velocity.x < 0 {
        factor = -factor
    }
    let indexPath = IndexPath(row: (scrollView.contentOffset.x/cellSize.width + factor).int, section: 0)
    collectionView?.scrollToItem(at: indexPath, at: .left, animated: true)
}

9

यहाँ ऊर्ध्वाधर सेल आधारित पेजिंग के लिए स्विफ्ट 5 में मेरा कार्यान्वयन है :

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

    guard let collectionView = self.collectionView else {
        let latestOffset = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
        return latestOffset
    }

    // Page height used for estimating and calculating paging.
    let pageHeight = self.itemSize.height + self.minimumLineSpacing

    // Make an estimation of the current page position.
    let approximatePage = collectionView.contentOffset.y/pageHeight

    // Determine the current page based on velocity.
    let currentPage = velocity.y == 0 ? round(approximatePage) : (velocity.y < 0.0 ? floor(approximatePage) : ceil(approximatePage))

    // Create custom flickVelocity.
    let flickVelocity = velocity.y * 0.3

    // Check how many pages the user flicked, if <= 1 then flickedPages should return 0.
    let flickedPages = (abs(round(flickVelocity)) <= 1) ? 0 : round(flickVelocity)

    let newVerticalOffset = ((currentPage + flickedPages) * pageHeight) - collectionView.contentInset.top

    return CGPoint(x: proposedContentOffset.x, y: newVerticalOffset)
}

कुछ नोट:

  • गड़बड़ नहीं है
  • सेट करने के लिए मिल रहा है ! (अन्यथा यह काम नहीं करेगा)
  • आपको आसानी से अपने स्वयं के फ़्लिपेलोसिटी सेट करने की अनुमति देता है ।
  • यदि यह कोशिश करने के बाद भी कुछ काम नहीं कर रहा है, तो जांच लें कि क्या आपका itemSizeवास्तव में आइटम के आकार से मेल खाता है क्योंकि यह अक्सर एक समस्या है, खासकर जब उपयोग करते हैं collectionView(_:layout:sizeForItemAt:), तो आइटम के साथ कस्टम चर का उपयोग करें।
  • जब आप सेट करते हैं तो यह सबसे अच्छा काम करता है self.collectionView.decelerationRate = UIScrollView.DecelerationRate.fast

यहां एक क्षैतिज संस्करण है (इसे पूरी तरह से परीक्षण नहीं किया गया है, इसलिए कृपया किसी भी गलतियों को माफ करें):

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

    guard let collectionView = self.collectionView else {
        let latestOffset = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
        return latestOffset
    }

    // Page width used for estimating and calculating paging.
    let pageWidth = self.itemSize.width + self.minimumInteritemSpacing

    // Make an estimation of the current page position.
    let approximatePage = collectionView.contentOffset.x/pageWidth

    // Determine the current page based on velocity.
    let currentPage = velocity.x == 0 ? round(approximatePage) : (velocity.x < 0.0 ? floor(approximatePage) : ceil(approximatePage))

    // Create custom flickVelocity.
    let flickVelocity = velocity.x * 0.3

    // Check how many pages the user flicked, if <= 1 then flickedPages should return 0.
    let flickedPages = (abs(round(flickVelocity)) <= 1) ? 0 : round(flickVelocity)

    // Calculate newHorizontalOffset.
    let newHorizontalOffset = ((currentPage + flickedPages) * pageWidth) - collectionView.contentInset.left

    return CGPoint(x: newHorizontalOffset, y: proposedContentOffset.y)
}

यह कोड मेरे व्यक्तिगत प्रोजेक्ट में उपयोग किए गए कोड पर आधारित है, आप इसे डाउनलोड करके और उदाहरण लक्ष्य को चलाकर यहां देख सकते हैं ।


1
स्विफ्ट 5 के लिए: .fastUIScollViewDecelerationRateFast
जोस

यह बात बताने के लिए धन्यवाद! इस उत्तर को अद्यतन करने के लिए भूल गए और बस किया!
जॉनीवीआर

हाय, @JVVR बहुत अच्छा स्पष्टीकरण उदाहरण दिखाता है कि स्वाइप कैसे लंबवत काम करेगा। यह इस तरह का सुझाव देना होगा कि क्षैतिज दिशा में इस कार्य को त्रुटिपूर्ण बनाने के लिए समग्र कोड परिवर्तनों की क्या आवश्यकता है। ऊपर दिए गए कोड के अलावा आपने लक्ष्य सामग्री ऑफसेट फ़ंक्शन में क्षैतिज स्वाइपिंग के लिए सुझाव दिया। मुझे लगता है कि सटीक परिदृश्य को क्षैतिज रूप से दोहराने के लिए बहुत सारे बदलाव किए गए हैं। अगर मैं ग़लत हूं तो मेरी गलती सुझाएं।
शिव प्रकाश

7

दृष्टिकोण 1: संग्रह दृश्य

flowLayoutहै UICollectionViewFlowLayoutसंपत्ति

override func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    if let collectionView = collectionView {

        targetContentOffset.memory = scrollView.contentOffset
        let pageWidth = CGRectGetWidth(scrollView.frame) + flowLayout.minimumInteritemSpacing

        var assistanceOffset : CGFloat = pageWidth / 3.0

        if velocity.x < 0 {
            assistanceOffset = -assistanceOffset
        }

        let assistedScrollPosition = (scrollView.contentOffset.x + assistanceOffset) / pageWidth

        var targetIndex = Int(round(assistedScrollPosition))


        if targetIndex < 0 {
            targetIndex = 0
        }
        else if targetIndex >= collectionView.numberOfItemsInSection(0) {
            targetIndex = collectionView.numberOfItemsInSection(0) - 1
        }

        print("targetIndex = \(targetIndex)")

        let indexPath = NSIndexPath(forItem: targetIndex, inSection: 0)

        collectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: .Left, animated: true)
    }
}

दृष्टिकोण 2: पृष्ठ नियंत्रक देखें

यदि आप UIPageViewControllerअपनी आवश्यकताओं को पूरा करते हैं तो आप इसका उपयोग कर सकते हैं , प्रत्येक पृष्ठ पर एक अलग दृश्य नियंत्रक होगा।


इसके लिए मुझे पेजिंग को अक्षम करना होगा और केवल संग्रह में स्क्रॉल करने में सक्षम करना होगा?
एनआर 5

यह नवीनतम स्विफ्ट 4 / Xcode9.3 के लिए काम नहीं कर रहा है, targetContentOffset में मेमोरी फ़ील्ड नहीं है। मैंने स्क्रॉलिंग को लागू किया, लेकिन जब आप "फ़्लिक" करते हैं, तो यह सेल स्थान को समायोजित नहीं कर रहा है।
स्टीवन बी।

कुछ कोशिकाओं के साथ काम करता है, लेकिन जब मैं सेल 13 में पहुंचता हूं तो यह पिछली सेल में वापस कूदना शुरू कर देता है और आप इसे जारी नहीं रख सकते।
क्रिस्टोफर स्मिट

4

यह ऐसा करने का एक सीधा तरीका है।

मामला सरल है, लेकिन अंत में काफी सामान्य है (निश्चित सेल आकार और कोशिकाओं के बीच निश्चित अंतराल के साथ विशिष्ट थंबनेल स्कोरर)

var itemCellSize: CGSize = <your cell size>
var itemCellsGap: CGFloat = <gap in between>

override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    let pageWidth = (itemCellSize.width + itemCellsGap)
    let itemIndex = (targetContentOffset.pointee.x) / pageWidth
    targetContentOffset.pointee.x = round(itemIndex) * pageWidth - (itemCellsGap / 2)
}

// CollectionViewFlowLayoutDelegate

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return itemCellSize
}

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

ध्यान दें कि स्क्रॉलआउटऑफ़सेट को कॉल करने या लेआउट में गोता लगाने का कोई कारण नहीं है। देशी स्क्रॉलिंग व्यवहार पहले से ही सब कुछ करता है।

चीयर्स ऑल :)


2
आप वैकल्पिक collectionView.decelerationRate = .fastरूप से मिमिक डिफॉल्ट पेजिंग को करीब से सेट कर सकते हैं ।
एल्फानेक

1
यह वास्तव में अच्छा है। @elfanek मैंने पाया है कि सेटिंग तब तक ठीक है जब तक कि आप एक छोटा और कोमल स्वाइप नहीं करते हैं, तो यह जल्दी से झिलमिल करने लगता है।
मायलोगन

3

ईवा के जवाब की तरह की तरह, लेकिन यह एक छोटे से चिकनी क्योंकि यह लक्ष्य निर्धारित नहीं करता है ओफ़्सेट को शून्य पर सेट करें।

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    if ([scrollView isKindOfClass:[UICollectionView class]]) {
        UICollectionView* collectionView = (UICollectionView*)scrollView;
        if ([collectionView.collectionViewLayout isKindOfClass:[UICollectionViewFlowLayout class]]) {
            UICollectionViewFlowLayout* layout = (UICollectionViewFlowLayout*)collectionView.collectionViewLayout;

            CGFloat pageWidth = layout.itemSize.width + layout.minimumInteritemSpacing;
            CGFloat usualSideOverhang = (scrollView.bounds.size.width - pageWidth)/2.0;
            // k*pageWidth - usualSideOverhang = contentOffset for page at index k if k >= 1, 0 if k = 0
            // -> (contentOffset + usualSideOverhang)/pageWidth = k at page stops

            NSInteger targetPage = 0;
            CGFloat currentOffsetInPages = (scrollView.contentOffset.x + usualSideOverhang)/pageWidth;
            targetPage = velocity.x < 0 ? floor(currentOffsetInPages) : ceil(currentOffsetInPages);
            targetPage = MAX(0,MIN(self.projects.count - 1,targetPage));

            *targetContentOffset = CGPointMake(MAX(targetPage*pageWidth - usualSideOverhang,0), 0);
        }
    }
}

3

वेग सुनने के लिए रोमुलो बीएम उत्तर को संशोधित करें

func scrollViewWillEndDragging(
    _ scrollView: UIScrollView,
    withVelocity velocity: CGPoint,
    targetContentOffset: UnsafeMutablePointer<CGPoint>
) {
    targetContentOffset.pointee = scrollView.contentOffset
    var indexes = collection.indexPathsForVisibleItems
    indexes.sort()
    var index = indexes.first!
    if velocity.x > 0 {
       index.row += 1
    } else if velocity.x == 0 {
        let cell = self.collection.cellForItem(at: index)!
        let position = self.collection.contentOffset.x - cell.frame.origin.x
        if position > cell.frame.size.width / 2 {
           index.row += 1
        }
    }

    self.collection.scrollToItem(at: index, at: .centeredHorizontally, animated: true )
}

2

स्विफ्ट 5

मुझे UICollectionView को उप-लिंक किए बिना ऐसा करने का एक तरीका मिल गया है, बस सामग्री की गणना करना क्षैतिज में। जाहिर है बिनाPPEEabled सक्षम सच है। यहाँ कोड है:

var offsetScroll1 : CGFloat = 0
var offsetScroll2 : CGFloat = 0
let flowLayout = UICollectionViewFlowLayout()
let screenSize : CGSize = UIScreen.main.bounds.size
var items = ["1", "2", "3", "4", "5"]

override func viewDidLoad() {
    super.viewDidLoad()
    flowLayout.scrollDirection = .horizontal
    flowLayout.minimumLineSpacing = 7
    let collectionView = UICollectionView(frame: CGRect(x: 0, y: 590, width: screenSize.width, height: 200), collectionViewLayout: flowLayout)
    collectionView.register(collectionViewCell1.self, forCellWithReuseIdentifier: cellReuseIdentifier)
    collectionView.delegate = self
    collectionView.dataSource = self
    collectionView.backgroundColor = UIColor.clear
    collectionView.showsHorizontalScrollIndicator = false
    self.view.addSubview(collectionView)
}

func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    offsetScroll1 = offsetScroll2
}

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    offsetScroll1 = offsetScroll2
}

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>){
    let indexOfMajorCell = self.desiredIndex()
    let indexPath = IndexPath(row: indexOfMajorCell, section: 0)
    flowLayout.collectionView!.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
    targetContentOffset.pointee = scrollView.contentOffset
}

private func desiredIndex() -> Int {
    var integerIndex = 0
    print(flowLayout.collectionView!.contentOffset.x)
    offsetScroll2 = flowLayout.collectionView!.contentOffset.x
    if offsetScroll2 > offsetScroll1 {
        integerIndex += 1
        let offset = flowLayout.collectionView!.contentOffset.x / screenSize.width
        integerIndex = Int(round(offset))
        if integerIndex < (items.count - 1) {
            integerIndex += 1
        }
    }
    if offsetScroll2 < offsetScroll1 {
        let offset = flowLayout.collectionView!.contentOffset.x / screenSize.width
        integerIndex = Int(offset.rounded(.towardZero))
    }
    let targetIndex = integerIndex
    return targetIndex
}

1

यहां स्विफ्ट 3 में मेरा संस्करण है। स्क्रॉल समाप्त होने के बाद ऑफसेट की गणना करें और ऑफसेट को एनीमेशन के साथ समायोजित करें।

collectionLayout एक है UICollectionViewFlowLayout()

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    let index = scrollView.contentOffset.x / collectionLayout.itemSize.width
    let fracPart = index.truncatingRemainder(dividingBy: 1)
    let item= Int(fracPart >= 0.5 ? ceil(index) : floor(index))

    let indexPath = IndexPath(item: item, section: 0)
    collectionView.scrollToItem(at: indexPath, at: .left, animated: true)
}

1

इसके अलावा, आप स्क्रॉलिंग को संभालने के लिए नकली स्क्रॉल दृश्य बना सकते हैं।

क्षैतिज या लंबवत

// === Defaults ===
let bannerSize = CGSize(width: 280, height: 170)
let pageWidth: CGFloat = 290 // ^ + paging
let insetLeft: CGFloat = 20
let insetRight: CGFloat = 20
// ================

var pageScrollView: UIScrollView!

override func viewDidLoad() {
    super.viewDidLoad()

    // Create fake scrollview to properly handle paging
    pageScrollView = UIScrollView(frame: CGRect(origin: .zero, size: CGSize(width: pageWidth, height: 100)))
    pageScrollView.isPagingEnabled = true
    pageScrollView.alwaysBounceHorizontal = true
    pageScrollView.showsVerticalScrollIndicator = false
    pageScrollView.showsHorizontalScrollIndicator = false
    pageScrollView.delegate = self
    pageScrollView.isHidden = true
    view.insertSubview(pageScrollView, belowSubview: collectionView)

    // Set desired gesture recognizers to the collection view
    for gr in pageScrollView.gestureRecognizers! {
        collectionView.addGestureRecognizer(gr)
    }
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if scrollView == pageScrollView {
        // Return scrolling back to the collection view
        collectionView.contentOffset.x = pageScrollView.contentOffset.x
    }
}

func refreshData() {
    ...

    refreshScroll()
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    refreshScroll()
}

/// Refresh fake scrolling view content size if content changes
func refreshScroll() {
    let w = collectionView.width - bannerSize.width - insetLeft - insetRight
    pageScrollView.contentSize = CGSize(width: pageWidth * CGFloat(banners.count) - w, height: 100)
}

0

ठीक है, इसलिए प्रस्तावित उत्तरों ने मेरे लिए काम नहीं किया क्योंकि मैं इसके बजाय वर्गों द्वारा स्क्रॉल करना चाहता था, और इस प्रकार, चर चौड़ाई पृष्ठ आकार है

मैंने यह किया (केवल लंबवत):

   var pagesSizes = [CGSize]()
   func scrollViewDidScroll(_ scrollView: UIScrollView) {
        defer {
            lastOffsetY = scrollView.contentOffset.y
        }
        if collectionView.isDecelerating {
            var currentPage = 0
            var currentPageBottom = CGFloat(0)
            for pagesSize in pagesSizes {
                currentPageBottom += pagesSize.height
                if currentPageBottom > collectionView!.contentOffset.y {
                    break
                }
                currentPage += 1
            }
            if collectionView.contentOffset.y > currentPageBottom - pagesSizes[currentPage].height, collectionView.contentOffset.y + collectionView.frame.height < currentPageBottom {
                return // 100% of view within bounds
            }
            if lastOffsetY < collectionView.contentOffset.y {
                if currentPage + 1 != pagesSizes.count {
                    collectionView.setContentOffset(CGPoint(x: 0, y: currentPageBottom), animated: true)
                }
            } else {
                collectionView.setContentOffset(CGPoint(x: 0, y: currentPageBottom - pagesSizes[currentPage].height), animated: true)
            }
        }
    }

इस स्थिति में, मैं सेक्शन की ऊंचाई + हेडर + फुटर का उपयोग करके प्रत्येक पेज साइज़ की पहले से गणना करता हूं, और इसे ऐरे में स्टोर करता हूं। वह pagesSizesसदस्य है


0

यह मेरा समाधान है, स्विफ्ट 4.2 में, मैं चाहता हूं कि यह आपकी मदद कर सके।

class SomeViewController: UIViewController {

  private lazy var flowLayout: UICollectionViewFlowLayout = {
    let layout = UICollectionViewFlowLayout()
    layout.itemSize = CGSize(width: /* width */, height: /* height */)
    layout.minimumLineSpacing = // margin
    layout.minimumInteritemSpacing = 0.0
    layout.sectionInset = UIEdgeInsets(top: 0.0, left: /* margin */, bottom: 0.0, right: /* margin */)
    layout.scrollDirection = .horizontal
    return layout
  }()

  private lazy var collectionView: UICollectionView = {
    let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
    collectionView.showsHorizontalScrollIndicator = false
    collectionView.dataSource = self
    collectionView.delegate = self
    // collectionView.register(SomeCell.self)
    return collectionView
  }()

  private var currentIndex: Int = 0
}

// MARK: - UIScrollViewDelegate

extension SomeViewController {
  func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    guard scrollView == collectionView else { return }

    let pageWidth = flowLayout.itemSize.width + flowLayout.minimumLineSpacing
    currentIndex = Int(scrollView.contentOffset.x / pageWidth)
  }

  func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    guard scrollView == collectionView else { return }

    let pageWidth = flowLayout.itemSize.width + flowLayout.minimumLineSpacing
    var targetIndex = Int(roundf(Float(targetContentOffset.pointee.x / pageWidth)))
    if targetIndex > currentIndex {
      targetIndex = currentIndex + 1
    } else if targetIndex < currentIndex {
      targetIndex = currentIndex - 1
    }
    let count = collectionView.numberOfItems(inSection: 0)
    targetIndex = max(min(targetIndex, count - 1), 0)
    print("targetIndex: \(targetIndex)")

    targetContentOffset.pointee = scrollView.contentOffset
    var offsetX: CGFloat = 0.0
    if targetIndex < count - 1 {
      offsetX = pageWidth * CGFloat(targetIndex)
    } else {
      offsetX = scrollView.contentSize.width - scrollView.width
    }
    collectionView.setContentOffset(CGPoint(x: offsetX, y: 0.0), animated: true)
  }
}

0
final class PagingFlowLayout: UICollectionViewFlowLayout {
    private var currentIndex = 0

    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
        let count = collectionView!.numberOfItems(inSection: 0)
        let currentAttribute = layoutAttributesForItem(
            at: IndexPath(item: currentIndex, section: 0)
            ) ?? UICollectionViewLayoutAttributes()

        let direction = proposedContentOffset.x > currentAttribute.frame.minX
        if collectionView!.contentOffset.x + collectionView!.bounds.width < collectionView!.contentSize.width || currentIndex < count - 1 {
            currentIndex += direction ? 1 : -1
            currentIndex = max(min(currentIndex, count - 1), 0)
        }

        let indexPath = IndexPath(item: currentIndex, section: 0)
        let closestAttribute = layoutAttributesForItem(at: indexPath) ?? UICollectionViewLayoutAttributes()

        let centerOffset = collectionView!.bounds.size.width / 2
        return CGPoint(x: closestAttribute.center.x - centerOffset, y: 0)
    }
}

आपको उत्तरों को कॉपी / पेस्ट नहीं करना चाहिए। यदि उपयुक्त हो तो इसे डुप्लिकेट के रूप में चिह्नित करें।
21

0

Олень Безрогий के मूल उत्तर में एक मुद्दा था, इसलिए अंतिम सेल संग्रह दृश्य पर शुरुआत में स्क्रॉल किया गया था

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    targetContentOffset.pointee = scrollView.contentOffset
    var indexes = yourCollectionView.indexPathsForVisibleItems
    indexes.sort()
    var index = indexes.first!
    // if velocity.x > 0 && (Get the number of items from your data) > index.row + 1 {
    if velocity.x > 0 && yourCollectionView.numberOfItems(inSection: 0) > index.row + 1 {
       index.row += 1
    } else if velocity.x == 0 {
        let cell = yourCollectionView.cellForItem(at: index)!
        let position = yourCollectionView.contentOffset.x - cell.frame.origin.x
        if position > cell.frame.size.width / 2 {
           index.row += 1
        }
    }
    
    yourCollectionView.scrollToItem(at: index, at: .centeredHorizontally, animated: true )
}

-1

यहाँ एक तरीका यह है कि इसे UICollectionViewFlowLayoutओवरराइड का उपयोग करके किया जा सकता है targetContentOffset:

(हालांकि अंत में, मैं इसका उपयोग नहीं करता हूं और इसके बजाय UIPageViewController का उपयोग करता हूं।)

/**
 A UICollectionViewFlowLayout with...
 - paged horizontal scrolling
 - itemSize is the same as the collectionView bounds.size
 */
class PagedFlowLayout: UICollectionViewFlowLayout {

  override init() {
    super.init()
    self.scrollDirection = .horizontal
    self.minimumLineSpacing = 8 // line spacing is the horizontal spacing in horizontal scrollDirection
    self.minimumInteritemSpacing = 0
    if #available(iOS 11.0, *) {
      self.sectionInsetReference = .fromSafeArea // for iPhone X
    }
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError("not implemented")
  }

  // Note: Setting `minimumInteritemSpacing` here will be too late. Don't do it here.
  override func prepare() {
    super.prepare()
    guard let collectionView = collectionView else { return }
    collectionView.decelerationRate = UIScrollViewDecelerationRateFast // mostly you want it fast!

    let insetedBounds = UIEdgeInsetsInsetRect(collectionView.bounds, self.sectionInset)
    self.itemSize = insetedBounds.size
  }

  // Table: Possible cases of targetContentOffset calculation
  // -------------------------
  // start |          |
  // near  | velocity | end
  // page  |          | page
  // -------------------------
  //   0   | forward  |  1
  //   0   | still    |  0
  //   0   | backward |  0
  //   1   | forward  |  1
  //   1   | still    |  1
  //   1   | backward |  0
  // -------------------------
  override func targetContentOffset( //swiftlint:disable:this cyclomatic_complexity
    forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {

    guard let collectionView = collectionView else { return proposedContentOffset }

    let pageWidth = itemSize.width + minimumLineSpacing
    let currentPage: CGFloat = collectionView.contentOffset.x / pageWidth
    let nearestPage: CGFloat = round(currentPage)
    let isNearPreviousPage = nearestPage < currentPage

    var pageDiff: CGFloat = 0
    let velocityThreshold: CGFloat = 0.5 // can customize this threshold
    if isNearPreviousPage {
      if velocity.x > velocityThreshold {
        pageDiff = 1
      }
    } else {
      if velocity.x < -velocityThreshold {
        pageDiff = -1
      }
    }

    let x = (nearestPage + pageDiff) * pageWidth
    let cappedX = max(0, x) // cap to avoid targeting beyond content
    //print("x:", x, "velocity:", velocity)
    return CGPoint(x: cappedX, y: proposedContentOffset.y)
  }

}

-1

आप निम्नलिखित पुस्तकालय का उपयोग कर सकते हैं: https://github.com/ink-spot/UPCarouselFlowLayout

यह बहुत आसान है और अन्य विवरणों जैसे विवरणों के बारे में आपको सोचने की जरूरत नहीं है।


-1

मैंने यहां एक कस्टम संग्रह दृश्य लेआउट बनाया है जो समर्थन करता है:

  • एक समय में एक सेल पेजिंग
  • स्वाइप वेग के आधार पर एक बार में 2+ सेलिंग करें
  • क्षैतिज या ऊर्ध्वाधर दिशाएं

यह उतना आसान है:

let layout = PagingCollectionViewLayout()

layout.itemSize = 
layout.minimumLineSpacing = 
layout.scrollDirection = 

आप बस अपने प्रोजेक्ट में PagingCollectionViewLayout.swift जोड़ सकते हैं

या

pod 'PagingCollectionViewLayout'अपने पॉडफाइल में जोड़ें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.