UICollectionView flowLayout कोशिकाओं को सही तरीके से नहीं लपेटता है


79

मेरे पास एक UICollectionViewFlowLayout है। यह काम करेगा क्योंकि मैं ज्यादातर समय की उम्मीद करता हूं, लेकिन हर अब और फिर कोशिकाओं में से एक ठीक से लपेटता नहीं है। उदाहरण के लिए, सेल जो कि तीसरी पंक्ति के पहले "कॉलम" में होनी चाहिए यदि वास्तव में दूसरी पंक्ति में पीछे चल रही है और बस एक खाली जगह है जहां यह होना चाहिए (नीचे चित्र देखें)। आप सभी देख सकते हैं कि यह रूज सेल बाएं हाथ की तरफ है (बाकी को काट दिया गया है) और उस जगह को खाली होना चाहिए।

यह लगातार नहीं होता है; यह हमेशा एक ही पंक्ति नहीं है। एक बार ऐसा हो जाने के बाद, मैं स्क्रॉल कर सकता हूं और फिर वापस और सेल खुद तय हो जाएगा। या, जब मैं सेल दबाता हूं (जो मुझे एक धक्का के माध्यम से अगले दृश्य में ले जाता है) और फिर वापस पॉप होता है, तो मैं सेल को गलत स्थिति में देखूंगा और फिर यह सही स्थिति पर कूद जाएगा।

स्क्रॉल की गति समस्या को पुन: उत्पन्न करना आसान बनाती है। जब मैं धीरे-धीरे स्क्रॉल करता हूं, तब भी मैं सेल को हर हाल में गलत स्थिति में देख सकता हूं, लेकिन फिर वह सीधे सही स्थिति में कूद जाएगा।

समस्या तब शुरू हुई जब मैंने अनुभागों को जोड़ दिया। पहले, मेरे पास कलेक्शन बाउंड्स (थोड़ा, या कोई इनसेट) के खिलाफ लगभग फ्लश था और मुझे इस समस्या पर ध्यान नहीं था। लेकिन इसका मतलब था कि संग्रह के दृश्य का दायां और बायां हिस्सा खाली था। यानी, स्क्रॉल नहीं कर सका। इसके अलावा, स्क्रॉल बार दाईं ओर फ्लश नहीं था।

मैं समस्या को सिम्युलेटर और iPad 3 दोनों पर कर सकता हूं।

मुझे लगता है कि समस्या बाएं और दाएं अनुभाग के इनसेट के कारण हो रही है ... लेकिन यदि मूल्य गलत है, तो मैं व्यवहार के अनुरूप होने की उम्मीद करूंगा। मुझे आश्चर्य है कि क्या यह Apple के साथ बग हो सकता है? या शायद यह इनसेट या कुछ समान के निर्माण के कारण है।

समस्या और सेटिंग्स का चित्रण


फॉलो अप करें : मैं 2 साल से अधिक समय से निक द्वारा नीचे दिए गए इस जवाब का उपयोग कर रहा हूं, बिना किसी समस्या के (यदि लोग उस उत्तर में कोई छेद हैं - तो मुझे आश्चर्य है - मुझे अब तक नहीं मिला है)। अच्छा किया निक।

जवाबों:


94

UICollectionViewFlowLayout के लेआउट के कार्यान्वयन में एक बग है ।AttributesForElementsInRect के कारण यह खंड इनसेट से जुड़े कुछ मामलों में एकल सेल के लिए दो विशेषता ऑब्जेक्ट वापस करने का कारण बनता है। लौटी विशेषता ऑब्जेक्ट्स में से एक अमान्य है (संग्रह दृश्य की सीमा के बाहर) और दूसरा वैध है। नीचे UICollectionViewFlowLayout का एक उपवर्ग है जो संग्रह दृश्य के सीमा के बाहर कोशिकाओं को छोड़कर समस्या को ठीक करता है।

// NDCollectionViewFlowLayout.h
@interface NDCollectionViewFlowLayout : UICollectionViewFlowLayout
@end

// NDCollectionViewFlowLayout.m
#import "NDCollectionViewFlowLayout.h"
@implementation NDCollectionViewFlowLayout
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
  NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
  NSMutableArray *newAttributes = [NSMutableArray arrayWithCapacity:attributes.count];
  for (UICollectionViewLayoutAttributes *attribute in attributes) {
    if ((attribute.frame.origin.x + attribute.frame.size.width <= self.collectionViewContentSize.width) &&
        (attribute.frame.origin.y + attribute.frame.size.height <= self.collectionViewContentSize.height)) {
      [newAttributes addObject:attribute];
    }
  }
  return newAttributes;
}
@end

देखें इस

अन्य उत्तर सुझाव देते हैं कि YES को shouldInvalidateLayoutForBoundsChange से वापस करना चाहिए, लेकिन यह अनावश्यक पुनर्संयोजन का कारण बनता है और समस्या का पूरी तरह से समाधान भी नहीं करता है।

मेरा समाधान पूरी तरह से बग को हल करता है और किसी भी समस्या का कारण नहीं होना चाहिए जब Apple मूल कारण को ठीक करता है।


5
FYI करें यह बग भी क्षैतिज स्क्रॉलिंग के साथ तैयार होता है। Y के साथ x और चौड़ाई के साथ x को बदलने से यह पैच काम करता है।
पैट्रिक टेचर

धन्यवाद! मैं सिर्फ संग्रह दृश्य के साथ खेलना शुरू कर रहा था (यदि आप इसके बारे में Apple को स्पैम करना चाहते हैं, तो यहाँ rdar संदर्भ openradar.appspot.com/12433891 )
Vinzzz

1
@richarddas नहीं, आप यह जांचना नहीं चाहते हैं कि क्या जड़ें प्रतिच्छेद करती हैं या नहीं। वास्तव में, सभी सेल (वैध या अमान्य) संग्रह दृश्य की सीमा को पार कर जाएंगे। आप जांचना चाहते हैं कि क्या कोई भी हिस्सा सीमा के बाहर गिरता है, जो कि मेरा कोड करता है।
निक स्नाइडर

2
@Rpranata iOS 7.1 के रूप में, यह बग ठीक नहीं किया गया है। आह।
nonamelive

2
हो सकता है कि मैं इस गलत का उपयोग कर रहा हूं, लेकिन स्विफ्ट में iOS 8.3 पर, यह उस दाईं ओर सबव्यू का कारण बन रहा है जो कि बिल्कुल भी दिखाई नहीं देने के लिए कट जाता था। कोई और?
सूदो

8

संग्रह दृश्य का स्वामी है कि दृश्य केंद्र में यह रखो

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    [self.collectionView.collectionViewLayout invalidateLayout];
}

तुम कहाँ रख दिया?
फतुहोकू


3
मेरा मुद्दा यह था कि कोशिकाएं पूरी तरह से गायब हो गईं। इस समाधान ने मदद की - हालांकि यह अनावश्यक भार का कारण बनता है। अभी भी यह काम कर रहा है .. thx!
पावी

1
अगर मेरे लिए
व्यू

जैसा कि DHennessy13 ने उल्लेख किया है , यह वर्तमान समाधान अच्छा है, लेकिन अपूर्ण हो सकता है क्योंकि यह स्क्रीन को घुमाते समय अमान्य होगा (और अधिकांश मामलों के लिए इसे नहीं करना चाहिए)। सुधार invalidateLayoutकेवल एक बार करने के लिए एक ध्वज स्थापित करने के लिए हो सकता है ।
सेर

7

मैं अपने iPhone आवेदन में इसी तरह की समस्याओं की खोज की। Apple देव मंच की खोज से मुझे यह उपयुक्त समाधान मिला जो मेरे मामले में काम आया और शायद आपके मामले में भी होगा:

उपवर्ग UICollectionViewFlowLayoutऔर shouldInvalidateLayoutForBoundsChangeवापस लौटने के लिए ओवरराइड करें YES

//.h
@interface MainLayout : UICollectionViewFlowLayout
@end

तथा

//.m
#import "MainLayout.h"
@implementation MainLayout
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    return YES;
}
@end

यह केवल आंशिक रूप से इस समस्या को हल करता है जो मुझे इस मुद्दे के साथ है। जब पंक्ति दिखाई देती है तो सेल वास्तव में सही जगह पर जाती है। हालाँकि, इसके प्रकट होने से ठीक पहले मुझे अभी भी एक सेल दिखाई दे रही है।
डेनियल वुड

सावधान - ऐसा करने से आपके द्वारा स्क्रॉल किए जाने पर हर बार लेआउट चलाया जाएगा। यह प्रदर्शन को बुरी तरह प्रभावित कर सकता है।
फतुहोकू

7

निक स्नाइडर के जवाब का एक स्विफ्ट संस्करण:

class NDCollectionViewFlowLayout : UICollectionViewFlowLayout {
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributes = super.layoutAttributesForElements(in: rect)
        let contentSize = collectionViewContentSize
        return attributes?.filter { $0.frame.maxX <= contentSize.width && $0.frame.maxY < contentSize.height }
    }
}

1
यह पूरी तरह से CollectionViewCell गायब हो गया ... किसी भी अन्य संभव समाधान?
गिग्स

5

मैं इस समस्या के साथ ही मार्जिन के लिए एक बुनियादी ग्रिडव्यू लेआउट के लिए था। अब तक मैंने जो सीमित डिबगिंग की है, वह - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rectमेरे UICollectionViewFlowLayout उपवर्ग में लागू हो रही है और लॉगिंग करके सुपर क्लास कार्यान्वयन रिटर्न देता है, जो स्पष्ट रूप से समस्या को दर्शाता है।

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray *attrsList = [super layoutAttributesForElementsInRect:rect];

    for (UICollectionViewLayoutAttributes *attrs in attrsList) {
        NSLog(@"%f %f", attrs.frame.origin.x, attrs.frame.origin.y);
    }

    return attrsList;
}

लागू करने से - (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPathमैं यह भी देख सकता हूं कि यह आइटम के लिए गलत मान लौटाता है।

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {
    UICollectionViewLayoutAttributes *attrs = [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath];

    NSLog(@"initialAttrs: %f %f atIndexPath: %d", attrs.frame.origin.x, attrs.frame.origin.y, itemIndexPath.item);

    return attrs;
}

अधिक डिबगिंग के लिए समय की कमी के साथ, मैंने अभी के लिए जो वर्कअराउंड किया है वह मेरे संग्रह की चौड़ाई को बाएं और दाएं मार्जिन के बराबर राशि के साथ कम कर दिया है। मेरे पास एक हेडर है जो अभी भी पूरी चौड़ाई की आवश्यकता है इसलिए मैंने क्लिपट्यूबाउंड सेट किया है = मेरे संग्रह पर NO और फिर उस पर बाएँ और दाएँ इनसेट को भी हटा दिया, काम करने लगता है। शीर्ष लेख दृश्य के लिए तब तक बने रहने के लिए आपको फ़्रेम विधियों को लागू करने और लेआउट विधियों में आकार देने की आवश्यकता होती है जो कि लेआउट को वापस लाने के साथ असाइन किए जाते हैं शीर्ष लेख दृश्य के लिए।


अतिरिक्त जानकारी के लिए धन्यवाद @monowerker। मुझे लगता है कि मेरी समस्या तब शुरू हुई जब मैंने इनसेट्स जोड़े (मैंने इसे प्रश्न में जोड़ा है)। मैं आपके डिबगिंग के तरीकों की कोशिश करूंगा और देखूंगा कि क्या यह मुझे कुछ बताता है मैं आपके काम के आसपास भी कोशिश कर सकता हूं।
लिंडन फॉक्स

यह सबसे अधिक संभावना है कि यूआईसीएफएल / यूआईसीएल में एक बग है, मैं कोशिश कर रहा हूं और जब मेरे पास समय हो तो एक रडार दर्ज किया जा सकता है, यहां कुछ रदर-नंबरों के साथ एक चर्चा है जिसे आप संदर्भित कर सकते हैं। twitter.com/steipete/status/25832391327941010177
मोनोरकर

4

मैंने Apple में बग रिपोर्ट जोड़ी है। मेरे लिए क्या काम करता है नीचे अनुभाग सेट करना है। शीर्ष इनसेट की तुलना में कम मूल्य पर शुरू करें।


3

मैं iPhone का उपयोग कर एक ही सेल-डेप्लेसिंग-समस्या का सामना कर UICollectionViewFlowLayoutरहा था और इसलिए मुझे खुशी हुई कि मैं आपकी पोस्ट पा रहा हूं। मुझे पता है कि आपको एक iPad पर समस्या हो रही है, लेकिन मैं यह पोस्ट कर रहा हूं क्योंकि मुझे लगता है कि यह एक सामान्य मुद्दा है UICollectionView। तो यहाँ मैं क्या पाया है।

मैं पुष्टि कर सकता हूं कि sectionInsetउस समस्या के लिए प्रासंगिक है। इसके अलावा यह headerReferenceSizeभी प्रभाव पड़ता है कि एक सेल विस्थापित है या नहीं। (यह मूल को शांत करने के लिए आवश्यक होने के बाद से समझ में आता है।)

दुर्भाग्य से, यहां तक ​​कि विभिन्न स्क्रीन आकारों को भी ध्यान में रखा जाना चाहिए। जब इन दो गुणों के लिए मूल्यों के साथ खेल रहे हों, तो मैंने अनुभव किया कि एक निश्चित विन्यास ने दोनों (3.5 "और 4"), दोनों में से किसी पर, या केवल एक स्क्रीन आकार पर काम किया। आमतौर पर उनमें से कोई नहीं। (यह भी समझ में आता है, चूंकि UICollectionViewपरिवर्तनों की सीमा है , इसलिए मुझे रेटिना और गैर-रेटिना के बीच किसी भी असमानता का अनुभव नहीं हुआ।)

मैंने स्क्रीन साइज़ के आधार पर सेटिंग की sectionInsetऔर समाप्त कर दिया headerReferenceSize। मैंने लगभग 50 संयोजनों की कोशिश की जब तक कि मुझे ऐसे मूल्य नहीं मिले जिनके तहत समस्या अब नहीं हुई और लेआउट नेत्रहीन स्वीकार्य था। उन मूल्यों को खोजना बहुत मुश्किल है जो स्क्रीन आकार दोनों पर काम करते हैं।

इसलिए संक्षेप में, मैं आपको मूल्यों के साथ खेलने की सलाह दे सकता हूं, विभिन्न स्क्रीन आकारों पर इनकी जांच कर सकता हूं और आशा करता हूं कि Apple इस मुद्दे को ठीक कर देगा।


3

मैं सिर्फ iOS 10 पर UICollectionView स्क्रॉल करने के बाद गायब होने वाली कोशिकाओं के साथ एक समान समस्या का सामना कर रहा हूं (IOS 6-9 पर कोई समस्या नहीं मिली)।

UICollectionViewFlowLayout और ओवरराइडिंग विधि लेआउट की उप-क्लासिंग ।AttributesForElementsInRect: मेरे मामले में काम नहीं करता है।

समाधान काफी सरल था। वर्तमान में मैं UICollectionViewFlowLayout का एक उदाहरण का उपयोग करता हूं और दोनों आइटम्स को सेट करता हूं और अनुमान लगाता हूं (मैंने पहले अनुमान नहीं लगाया है) और इसे कुछ गैर-शून्य आकार में सेट करें। वास्तविक आकार संग्रह दृश्य में गणना कर रहा है: लेआउट: sizeForItemAtIndexPath: विधि।

साथ ही, मैंने अनावश्यक री-लोड से बचने के लिए लेआउटसब्यूव्यू से अमान्य कॉल विधि को हटा दिया है।


आइटम कहां सेट किया गया और अनुमान लगाया गया यह uicollectionviewflowlayout में है?
गैरेट कॉक्स

UICollectionViewFlowLayout * flowLayout = [[UICollectionViewFlowLayout आवंटन] init]; [flowLayout setItemSize: CGSizeMake (200, 200)]; [flowLayout setEstimatedItemSize: CGSizeMake (200, 200)]; self.collectionView = [[UICollectionView आवंटन] initWithFrame: CGRectZero collectionViewLayout: flowLayout];
एंड्री सेर्डकिन

एस्टिमेट सेट
करनाइसमाइज़

2

मैंने सिर्फ एक समान मुद्दे का अनुभव किया लेकिन एक बहुत अलग समाधान पाया।

मैं क्षैतिज स्क्रॉल के साथ UICollectionViewFlowLayout के एक कस्टम कार्यान्वयन का उपयोग कर रहा हूं। मैं प्रत्येक सेल के लिए कस्टम फ्रेम लोकेशन भी बना रहा हूं।

जो समस्या मुझे हो रही थी वह यह थी कि [सुपर लेआउटएट्यूएंटेसफॉरसेल्स इनरिक्ट: रेक्ट] वास्तव में UICollectionViewLayoutAttributes के सभी वापस नहीं कर रहा था जिसे स्क्रीन पर प्रदर्शित किया जाना चाहिए। कॉल करने के लिए [self.collectionView reloadData] में से कुछ सेल को अचानक छिपा दिया जाएगा।

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

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {

    NSArray * originAttrs = [super layoutAttributesForElementsInRect:rect];
    NSMutableArray * attrs = [NSMutableArray array];
    CGSize calculatedSize = [self calculatedItemSize];

    [originAttrs enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * attr, NSUInteger idx, BOOL *stop) {
        NSIndexPath * idxPath = attr.indexPath;
        CGRect itemFrame = [self frameForItemAtIndexPath:idxPath];
        if (CGRectIntersectsRect(itemFrame, rect))
        {
            attr = [self layoutAttributesForItemAtIndexPath:idxPath];
            [self.savedAttributesDict addAttribute:attr];
        }
    }];

    // We have to do this because there is a bug in the collection view where it won't correctly return all of the on screen cells.
    [self.savedAttributesDict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSArray * cachedAttributes, BOOL *stop) {

        CGFloat columnX = [key floatValue];
        CGFloat leftExtreme = columnX; // This is the left edge of the element (I'm using horizontal scrolling)
        CGFloat rightExtreme = columnX + calculatedSize.width; // This is the right edge of the element (I'm using horizontal scrolling)

        if (leftExtreme <= (rect.origin.x + rect.size.width) || rightExtreme >= rect.origin.x) {
            for (UICollectionViewLayoutAttributes * attr in cachedAttributes) {
                [attrs addObject:attr];
            }
        }
    }];

    return attrs;
}

यहाँ NSMutableDictionary के लिए श्रेणी है कि UICollectionViewLayoutAttributes को सही ढंग से सहेजा जा रहा है।

#import "NSMutableDictionary+CDBCollectionViewAttributesCache.h"

@implementation NSMutableDictionary (CDBCollectionViewAttributesCache)

- (void)addAttribute:(UICollectionViewLayoutAttributes*)attribute {

    NSString *key = [self keyForAttribute:attribute];

    if (key) {

        if (![self objectForKey:key]) {
            NSMutableArray *array = [NSMutableArray new];
            [array addObject:attribute];
            [self setObject:array forKey:key];
        } else {
            __block BOOL alreadyExists = NO;
            NSMutableArray *array = [self objectForKey:key];

            [array enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *existingAttr, NSUInteger idx, BOOL *stop) {
                if ([existingAttr.indexPath compare:attribute.indexPath] == NSOrderedSame) {
                    alreadyExists = YES;
                    *stop = YES;
                }
            }];

            if (!alreadyExists) {
                [array addObject:attribute];
            }
        }
    } else {
        DDLogError(@"%@", [CDKError errorWithMessage:[NSString stringWithFormat:@"Invalid UICollectionVeiwLayoutAttributes passed to category extension"] code:CDKErrorInvalidParams]);
    }
}

- (NSArray*)attributesForColumn:(NSUInteger)column {
    return [self objectForKey:[NSString stringWithFormat:@"%ld", column]];
}

- (void)removeAttributesForColumn:(NSUInteger)column {
    [self removeObjectForKey:[NSString stringWithFormat:@"%ld", column]];
}

- (NSString*)keyForAttribute:(UICollectionViewLayoutAttributes*)attribute {
    if (attribute) {
        NSInteger column = (NSInteger)attribute.frame.origin.x;
        return [NSString stringWithFormat:@"%ld", column];
    }

    return nil;
}

@end

मैं क्षैतिज स्क्रॉलिंग का भी उपयोग कर रहा हूं, मैं आपके समाधान का उपयोग करके समस्या को ठीक करने का प्रबंधन करता हूं, लेकिन अन्य दृश्य से बहस करने और वापस आने के बाद, सामग्री का आकार गलत लग रहा था जब अतिरिक्त आइटम हैं जो समान रूप से स्तंभों में विभाजित नहीं होते हैं।
मॉर्फ --५

मुझे इस मुद्दे को हल करने के लिए एक समाधान मिला, जहां सेल प्रदर्शन और संग्रह दृश्य पर वापस जाने के बाद छिपा रहा। अनुमान लगाने की कोशिश न करें। संग्रह में देखें। सीधे आइटम सेट करें।
मोर्फ 85

2

उपरोक्त उत्तर मेरे लिए काम नहीं करते हैं, लेकिन छवियों को डाउनलोड करने के बाद, मैंने प्रतिस्थापित कर दिया

[self.yourCollectionView reloadData]

साथ में

[self.yourCollectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];

ताज़ा करने के लिए और यह सभी कोशिकाओं को सही ढंग से दिखा सकता है, आप इसे आज़मा सकते हैं।


0

यह थोड़ी देर हो सकती है लेकिन सुनिश्चित करें कि आप अपनी विशेषताओं को prepare()संभव हो तो सेट कर रहे हैं ।

मेरा मुद्दा यह था कि कोशिकाओं को बाहर रखा गया था, फिर अपडेट हो रहा था layoutAttributesForElements। इसके परिणामस्वरूप एक झिलमिल प्रभाव हुआ जब नई कोशिकाएं देखने में आईं।

सभी विशेषता तर्क को आगे बढ़ाकर prepare, फिर उन्हें UICollectionViewCell.apply()इसमें स्थापित करने से झिलमिलाहट को समाप्त कर दिया और मक्खन चिकना सेल प्रदर्शित किया logic

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