A UICollectionView bug with a dynamic height - ios

Since I have been struggling for 3 days with this problem and have asked about it twice already, but maybe was not clear, I had decided to investigate the issue & found a buggy behavior with this view.
I will show the entire simple code, so anyone can reproduce the bug (iPad Air).
I am setting a collectionView flowlayout that subclasses the layout to get a constant spacing between cells, and here is the start:
TopAlignedCollectionViewFlowLayout *layout = [[TopAlignedCollectionViewFlowLayout alloc] init];
CGRect size = CGRectMake(0, 0, 900, 1200);
self.GridView = [[UICollectionView alloc] initWithFrame:size
collectionViewLayout:layout];
[self.GridView registerClass:[GridCell class] forCellWithReuseIdentifier:#"Cell"];
[self.GridView setDelegate:self];
[self.GridView setDataSource:self];
[self.view addSubview:self.GridView];
Then setting my delegates is as simple as that : (height is dynamic )
#pragma grid- main functions
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 80;
}
//cell size
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
{
//a random dynamic height of a cell
int a = arc4random()%300;
CGSize size = CGSizeMake( 340, 240+a );
return size;
}
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier=#"Cell";
GridCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier
forIndexPath:indexPath];
cell.textL.text=[NSString stringWithFormat:#"%d",indexPath.row];
NSLog(#"%d",indexPath.row);
return cell;
}
Now the subclass, to get a constant spacing : (TopAlignedCollectionViewFlowLayout)
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray* attributesToReturn = [super layoutAttributesForElementsInRect:rect];
for (UICollectionViewLayoutAttributes* attributes in attributesToReturn) {
if (nil == attributes.representedElementKind) {
NSIndexPath* indexPath = attributes.indexPath;
attributes.frame = [self layoutAttributesForItemAtIndexPath:indexPath].frame;
}
}
return attributesToReturn;
}
#define numColumns 2
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes* currentItemAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];
if (indexPath.item < numColumns) {
CGRect f = currentItemAttributes.frame;
f.origin.y = 0;
currentItemAttributes.frame = f;
return currentItemAttributes;
}
NSIndexPath* ipPrev = [NSIndexPath indexPathForItem:indexPath.item-numColumns
inSection:indexPath.section];
CGRect fPrev = [self layoutAttributesForItemAtIndexPath:ipPrev].frame;
CGFloat YPointNew = fPrev.origin.y + fPrev.size.height + 10;
CGRect f = currentItemAttributes.frame;
f.origin.y = YPointNew;
currentItemAttributes.frame = f;
return currentItemAttributes;
}
Anyone can check and see that after you scroll for a while, you get a strange effect of blank spaces that are filled lately by their cells,something like :
1 2
3 4
6
8
NOTE: 5-7 are loaded in later.
EDIT1:
Removing the random height from the cell size delegate method, set it to be constant height, solves this issue.
Problem is: Cell's height must be dynamic.
EDIT2:
Setting the random height (int a) to be smaller, makes also the problem to disappear,(<100), means that the smaller the distance height between cells, more likely the problem will not occur .
EDIT3 !
I have managed to set a constant distance between cells, not with subclass of the layout, but with my own memory by saving the previous cell origin and height, so i have got the constant spacing but the problem is back again ! seems that if the cells are in some certain structure, it makes the callback method that create cells, to not being called in time ! wow , i am really wondering how no one had seen this before ..
here is my implementation to create spacing with no subclassing,that also cause the problem:
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier=#"Cell";
GridCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.textL.text=[NSString stringWithFormat:#"%ld",(long)indexPath.row];
NSLog(#"%d",indexPath.row);
if(indexPath.row>1)
{
NSIndexPath* ipPrev = [NSIndexPath indexPathForItem:indexPath.item-2 inSection:indexPath.section];
float prey=[[[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:#"y:%ld",(long)ipPrev.row]] floatValue];
float preh=[[[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:#"h:%ld",(long)ipPrev.row]] floatValue];
cell.frame=CGRectMake(cell.frame.origin.x, preh+prey+10, cell.frame.size.width, cell.frame.size.height);
[[NSUserDefaults standardUserDefaults] setFloat:cell.frame.origin.y forKey:[NSString stringWithFormat:#"y:%ld",(long)indexPath.row]];
[[NSUserDefaults standardUserDefaults] setFloat:cell.frame.size.height forKey:[NSString stringWithFormat:#"h:%ld",(long)indexPath.row]];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"this index:%d",indexPath.row);
NSLog(#"this cell y:%f",cell.frame.origin.y);
NSLog(#"this cell height:%f",cell.frame.size.height);
NSLog(#"previous index:%ld",(long)ipPrev.row);
NSLog(#"previous cell y: %#",[[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:#"y:%ld",(long)ipPrev.row]]);
NSLog(#"previous cell height: %#",[[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:#"h:%ld",(long)ipPrev.row]]);
NSLog(#"------------------------");
}
return cell;
}

Seems its a serious bug in UICollectionView, when images are bigger than screen size.
1 . UICollectionView fall back to UIScrollView
2.Large UICollectionViewCell's disappearing with custom layout
3.http://stripysock.com.au/blog/2013/3/14/working-around-a-bug-in-uicollectionview
So much work and time, for this stupid bug of apple with the reusable cells that causes nothing but headache with so many strange behaviours.
For developers who didn't have the problem i can say- just try to set dynamic height images, and iPad simulator, and you will see bugs that are just unbelievable.
So no answer for me, i will have to implement the whole thing by my self with a scrollview ,since i dont want to be depended again on things such PSTCollectionView

Related

Repeatedly representing the frame of the collectionView's cell causes memory growth?

I am working on a custom keyboard app and have encountered a difficult problem. I have inquired about some documents, but unfortunately did not find the answer. I want to ask everyone to help me.
The problem is this, when I type in the keyboard, the input results will be displayed in the candidate column.I use the UICollectionView to create the candidate column.
- (void)createCandideteColection {
if (!_collectionView) {
CGFloat height = self.frame.size.height-pinyinHeight-lineHeight;
CGFloat y = pinyinHeight+lineHeight;
CGFloat showButtonH = height;
CGFloat candidateShowWidth = self.frame.size.width - showButtonH;
UICollectionViewFlowLayout *flow = [[UICollectionViewFlowLayout alloc] init];
flow.scrollDirection = UICollectionViewScrollDirectionHorizontal;
flow.minimumInteritemSpacing = 20;
flow.minimumLineSpacing = 20;
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, y, candidateShowWidth, height) collectionViewLayout:flow];
_collectionView.backgroundColor = [UIColor whiteColor];
_collectionView.dataSource = self;
_collectionView.delegate = self;
_collectionView.bounces = NO;
_collectionView.showsHorizontalScrollIndicator = NO;
[_collectionView registerClass:[GZCandidateCell class] forCellWithReuseIdentifier:#"candidateCell"];
[self addSubview:_collectionView];
}
}
Sets the frame of the cell, _data changes with the input (each time a text is entered, _data changes once, and [collectionView reloadData] is called once).The width of the cell changes with the number of characters entered.
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
NSString *str = _data[indexPath.row];
CGFloat height = self.frame.size.height-pinyinHeight-lineHeight;
CGFloat width = str.length * 20 + 10;
return CGSizeMake(width, height);
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
GZCandidateCell *cell = (GZCandidateCell*)[collectionView dequeueReusableCellWithReuseIdentifier:#"candidateCell" forIndexPath:indexPath];
NSString *str = _data[indexPath.row];
[cell setTitleText:str];
return cell;
}
Assign titleLabel value, titleLabel size will be recalculate when calling [cell setTitleText:str].
when [collectionView removeFromSuperview] is called, collectionView will be destroyed,but there is little memory that cannot be released.Every time the input is completed and the collectionView is removed, some memory will not be released and will become a larger number after it is accumulated.
The reason why I will eventually locate this problem on the collectionView is because I don't create this collectionView, the memory does not grow.
Get input data and show on the collectionView,and tabBar is the candidate column I mentioned above:
if (!_textKeyboard) {
_textKeyboard = [[GZQwertyKeyboard alloc] initWithFrame:CGRectMake(0, navigaitonHeight, SCREEN_WIDTH, height) andKeyboardType:type];
_textKeyboard.backgroundColor = Color_background_kb;
[self.view addSubview:_textKeyboard];
}
__weak KeyboardViewController *weakSelf = self;
__weak GZQwerty *getdata = [GZQwerty defaultQwerty]; //init qwerty
_textKeyboard.sendSelectedStr = ^(NSString *text) {
if (!weakSelf.tabBar) {
[weakSelf addCandidateBarView];
}
int asciiCode = [text characterAtIndex:0];
[getdata sendInput:asciiCode complation:^(NSString *compontText, NSArray *candiateArray) {
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.tabBar changeShowText:candiateArray];
});
}];
};
Call [weakSelf addCandidateBarView] to create the tabBar.Call [weakSelf.tabBar changeShowText:candiateArray] to modify the data source of the collectionView and refresh the collectionView.Like this:
- (void)changeShowText:(NSArray*)textArr {
_data = textArr;
if (!_collectionView) {
[self createCandideteColection];
}
[_collectionView reloadData];
[_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
}
When the keyboard is typed, if changeShowText: is not called, the memory is stable and there is no memory growth.
So, can anyone help me? Is there a better way than collectionView or how to avoid memory growth?Is it caused by repeated changes to the width of the cell?

Cell disappears after reloading when using estimatedSize

I'm having a weird issue, when using a collection view with dynamic sizes, this issue doesn't happens while using fixed sizes.
After a reload the first cell of each section disappears, but only if they are out of the screen. After a few tests I realize that the cell didn't disappear, but its hidden bellow the section header.
Do you have any idea what is causing this?
Collection without reloading:
Collection after reloading with cell visible:
Collection after reloading with cell out of screen:
3D view of the cell after reloading:
The code:
#pragma mark - UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return self.sections.count;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)sectionIndex {
Section *section = [self.sections objectAtIndex:sectionIndex];
return section.items.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
Section *section = [self.sections objectAtIndex:indexPath.section];
Item *item = [section.items objectAtIndex:indexPath.row];
if (self.editing) {
EditingCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell-editing" forIndexPath:indexPath];
cell.item = item;
return cell;
} else {
BasicCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
cell.item = item;
return cell;
}
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
HeaderCollectionReusableView *header = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:#"header" forIndexPath:indexPath];
Section *section = [self.sections objectAtIndex:indexPath.section];
header.title = section.title;
return header;
} else {
UICollectionReusableView *footer = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:#"footer" forIndexPath:indexPath];
return footer;
}
}
#implementation DetailCollectionViewLayout
- (instancetype)init {
if (self = [super init]) {
[self initialize];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self initialize];
}
return self;
}
- (void)prepareLayout {
CGFloat cellWidth = (isIPAD) ? 288 : CGRectGetWidth(self.collectionView.bounds);
CGFloat headerWidth = CGRectGetWidth(self.collectionView.bounds);
// CGFloat ratio = (isIPAD) ? 0.33 : 0.66;
self.estimatedItemSize = CGSizeMake(cellWidth, 53);
self.headerReferenceSize = CGSizeMake(headerWidth, 50);
self.footerReferenceSize = CGSizeMake(headerWidth, 1 + self.minimumInteritemSpacing);
[super prepareLayout];
}
- (void)initialize {
self.minimumLineSpacing = 0;
self.minimumInteritemSpacing = (isIPAD) ? 5 : 10;
self.estimatedItemSize = CGSizeZero;
self.scrollDirection = UICollectionViewScrollDirectionVertical;
self.sectionInset = UIEdgeInsetsZero;
}
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}
#end
I made a simple sample project and record a video: http://sendvid.com/330uo5jm
It looks like the issue is the position from the first cell.
UICollectionViewCell autosizing can be a little, uh... interesting even at the best of times. I've had this exact issue in the past, and similar issues too.
Use a different value for .estimatedItemSize, the closer to the actual item size the better. I noticed you're using a size of CGRectZero at first. I wouldn't recommend doing that. Just set it once, at the start, with a value close to your size. Try a few values, see what works for you. For me, it took a bit of fine tuning.
For anyone developing for iOS 10 (at the time of writing this hasn't been released) there is a new collection view property that lets the collection view determine the estimated size itself. Set the itemSize to UICollectionViewFlowLayoutAutomaticSize, you shouldn't need to set .estimatedItemSize explicitly.
make sure your estimatedItemSize in your code same as size of cell in your xib or storyboard.Don't changes it's size runtime.
can you check with use of identifier like...
NSString *CellIdentifier = [NSString stringWithFormat:#"%d_%d",indexPath.section,indexPath.row];
Sometimes it happens if you have a big difference between estimated size and real size of cell.
Check you have a clear consequence of top to bottom constraints (if you are using autolayout).
Is there something that can break the autolayout to work properly ? E.g compresion resistance settings ?
Are you sure there are data for the cell after reload ? (Will be weird, but to be sure, just double check that.)
Also as Apple denotes here Apple - self sizing guide try to set the estimation of size as close as possible to real dimensions.
You can also try to refer to invalidation of collection layout as you are using your own. Refer to Possible flow-layout help
Try to set the estimation as close as possible and you will see if it solve your problem.

Subclassing UICollectionView, skipping indexes

I was trying to subclass collection view layout , in order to get a constant spacing between vertical cells. i have 2 columns and many rows, with dynamic cells height .
The goal is Pinterest like grid.
So i have subclassed the layout class, and now has a constant space between cells, but there is a serious problem caused by that .
When i scroll down, the left cells are not being loaded in time= there are many "holes" so that there is blank space of 3-4 cells, and than- they suddenly appears at once -lately.
so i have this :
1 2
3 4
6
8
than 5 and 7 appears when i scroll down more . i just can't get rid of this !
EDIT: seems that when all cells are in the same size, this will not happens ,so when i return here a constant height :
//cell size
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
{
CGSize size=CGSizeMake( imageWidth, scale*height+[Globals sharedGlobals].gridViewStripHeight );
return size;
My subclass(which when not using it, also solves the problem )
//the subclass
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray* arr = [super layoutAttributesForElementsInRect:rect];
for (UICollectionViewLayoutAttributes* atts in arr)
{
if (nil == atts.representedElementKind)
{
NSIndexPath* ip = atts.indexPath;
atts.frame = [self layoutAttributesForItemAtIndexPath:ip].frame;
}
}
return arr;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes* atts =[super layoutAttributesForItemAtIndexPath:indexPath];
if (indexPath.item == 0 || indexPath.item == 1) // degenerate case 1, first item of section
return atts;
NSIndexPath* ipPrev =
[NSIndexPath indexPathForItem:indexPath.item-2 inSection:indexPath.section];
CGRect fPrev = [self layoutAttributesForItemAtIndexPath:ipPrev].frame;
CGFloat rightPrev = fPrev.origin.y + fPrev.size.height + 50;
if (atts.frame.origin.y <= rightPrev) // degenerate case 2, first item of line
return atts;
CGRect f = atts.frame;
f.origin.y = rightPrev;
atts.frame = f;
return atts;
}
To use it i have :
UICollectionViewFlowLayout *layout=[[TopAlignedCollectionViewFlowLayout alloc] init]; subclass
CGRect size=CGRectMake( ([UIScreen mainScreen].bounds.size.width-collectionWidth)/2,
upperLineMargin, collectionWidth, [UIScreen mainScreen].bounds.size.height-upperLineMargin);
self.GridView=[[UICollectionView alloc] initWithFrame:size collectionViewLayout:layout];
[self.GridView registerClass:[GridCell class] forCellWithReuseIdentifier:#"Cell"];

iOS UICollectionView header & footer location

Working in iOS 7, how does one specify where the header & footer boxes go in a UICollectionView?
I have a custom UICollectionViewFlowLayout. I have overwritten
-(void)prepareLayout
-(NSArray*) layoutAttributesForElementsInRect:(CGRect)rect
-(UICollectionViewLayoutAttributes*) layoutAttributesForSupplementaryViewOfKind: (NSString*)kind atIndexPath:(NSIndexPath*)indexPath
My problem is, I'm not sure how to specify header location. I have already specified that a header exists in prepareLayout:
-(void)prepareLayout
{
[super prepareLayout];
boundsSize = self.collectionView.bounds.size;
midX = boundsSize.width / 2.0f;
curIndex = 0;
self.headerReferenceSize = CGSizeMake(CELL_SIZE, TITLE_HEIGHT);
self.footerReferenceSize = CGSizeMake(0, 0);
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.sectionInset = UIEdgeInsetsMake(TOP_INSET, LEFT_INSET, BOTTOM_INSET, RIGHT_INSET);
self.minimumLineSpacing = LINE_SPACING;
self.minimumInteritemSpacing = INTERIM_SPACING;
self.itemSize = CGSizeMake(CELL_SIZE, CELL_SIZE);
}
I just don't know the right property of my custom FlowLayout to set, as there doesn't seem to be something like "HeaderLocation" to set, either as a LayoutAttributes or in the layout object itself. Right now, it is appearing to the side/between my images, when I'd like them to be appearing above each image (horizontal scroll).
I have tried the following:
-(UICollectionReusableView*) collectionView: (UICollectionView*)collectionView viewForSupplementaryElementOfKind:(NSString*)kind atIndexPath:(NSIndexPath*)indexPath
{
NSLog(#"**ViewForSupplementaryElementOfKind called***");
CGFloat centerX = collectionView.center.x;
CGFloat centerY = collectionView.center.y;
CGFloat titleWidth = [MyLayout titleWidth];
CGFloat titleHeight = [MyLayout titleHeight];
MyTitleView* titleView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:ImageTitleIdentifier forIndexPath:indexPath];
titleView.frame = CGRectMake(centerX - titleWidth/2.0,
0.0,
titleWidth,
titleHeight);
return titleView;
}
This doesn't work. The title appears above overlapped with a bunch of other titles, then the moment I start scrolling (horizontally), they jump back into the wrong place, horizontally between the images rather than above.
PS> Please do not suggest anything that has to do with NIB or XIB placement. I am using a UICollectionView, NOT a UICollectionViewController, so I actually have no prototypical cell to work with. The layout is being done entirely programatically -- from code alone -- so I can't simply open a XIB file and adjust the location of a text box.
Amending the attributes returned by -layoutAttributesForElementsInRect is the right approach, but if you want to alter the position of offscreen headers and footers, you may need to fetch the supplementary view attributes yourself.
For example, in your UICollectionViewFlowLayout subclass:
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray *attributesArray = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
// the call to super only returns attributes for headers that are in the bounds,
// so locate attributes for out of bounds headers and include them in the array
NSMutableIndexSet *omittedSections = [NSMutableIndexSet indexSet];
for (UICollectionViewLayoutAttributes *attributes in attributesArray) {
if (attributes.representedElementCategory == UICollectionElementCategoryCell) {
[omittedSections addIndex:attributes.indexPath.section];
}
}
for (UICollectionViewLayoutAttributes *attributes in attributesArray) {
if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
[omittedSections removeIndex:attributes.indexPath.section];
}
}
[omittedSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx];
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader
atIndexPath:indexPath];
[attributesArray addObject:attributes];
}];
for (UICollectionViewLayoutAttributes *attributes in attributesArray) {
if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
// adjust any aspect of each header's attributes here, including frame or zIndex
}
}
return attributesArray;
}
CollectionView Header height is set below Collectionview delegate
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
And Set view in Collectionview Header in Below Delegate
- (UICollectionReusableView*)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
UICollectionReusableView * view = nil;
if ([kind isEqualToString:UICollectionElementKindSectionHeader])
{
ColorSectionHeaderView *header = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:NSStringFromClass([ColorSectionHeaderView class])
forIndexPath:indexPath];
header.sectionIndex = indexPath.section;
header.hideDelete = collectionView.numberOfSections == 1; // hide when only one section
header.delegate = self;
view = header;
}
return view;
}
Ragistred Class in ViewDidLoad
-(void)ViewDidLoad
{
[collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([ColorSectionFooterView class]) bundle:nil]
forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
withReuseIdentifier:NSStringFromClass([ColorSectionFooterView class])];
[Super ViewDidLoad];
}

UILabel upon a few UITableViewCells

Am I able to create a UILabel that is layouted upon many UITableViewCells?
I'm trying to make something like (that is just one section of my UITableView, each section can have one or more rows):
---------------------------------------------
| Multi-lined label | row1 values |
| with some useless | row2 values |
| text | row3 values |
---------------------------------------------
I managed to create a UILabel (in the first row of a section) that is multi-lined and is not clipping to bounds. That works really well (it was a bit tricky to count each sections row heights, but doable) besides one case: when I'm scrolling UITableView from bottom to top - UITableView renders last row (without UILabel) so it has "no evidence" of having UILabel (because it is maintained in the first row of section). Can I force some kind of relayouting first cell in section? I tried reloadRowsAtIndexPaths:withRowAnimation: with first row in each section whenever I layouted not first cell in section but it gave me layouting errors that I really do not understand. Or maybe there is another idea to do so?
-- EDITED
To be clear: I have a custom UITableViewCell with an IB view, it has a few labels that each row consist of and a label named labelName that I want to be "multi-lined" along rows in that section. LabelName.text is empty for each row besides first one in each section.
I am adding somescreenshots:
Good screenshot - when I am scrolling to bottom I'm getting proper effect:
Bad screenshot - when I am scrolling up, UITableView renders last row of section firstly, and afterwards renders upper rows - that gives effect of cut label (because multi-line label is in the first row)
I am not sure if code here will add anything to question - it is rather simple and almost whole logic is in tableView:heightForRowAtIndexPath. I can only present how do I create custom UITableViewCell:
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[CustomTableViewCell reuseIdentifier]];
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithOwner:self];
cell.clipsToBounds = NO;
cell.labelName.clipsToBounds = NO;
cell.contentView.superview.clipsToBounds = NO;
}
-- EDIT 2
Here is most of the code:
- (void) reloadData
{
NSUInteger index = 0;
for (NSDictionary *object in self.list) {
CGFloat height = [[object objectForKey:#"name"] sizeWithFont:self.labelFont constrainedToSize:self.labelSize].height;
[self.labelHeights addObject:NSNumberFloat(ceilf(height))];
index++;
}
[self.tableView reloadData];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *object = [self.list objectAtIndex:indexPath.section];
CGFloat height = [[self.labelHeights objectAtIndex:indexPath.section] floatValue];
NSUInteger count = [[object objectForKey:#"list"] count];
CGFloat cellHeight = 30.f;
if((indexPath.row + 1) == count){
cellHeight = MAX(8.f + height - 30.f * indexPath.row, 30.f);
}
return cellHeight;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.list count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[self.list objectAtIndex:section] objectForKey:#"list"] count];
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *person = [self.list objectAtIndex:indexPath.section];
NSDictionary *object = [[person objectForKey:#"list"] objectAtIndex:indexPath.row];
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[CustomTableViewCell reuseIdentifier]];
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithOwner:self];
cell.backgroundColor = [UIColor clearColor];
cell.userInteractionEnabled = NO;
cell.clipsToBounds = NO;
cell.labelName.clipsToBounds = NO;
[cell.contentView.superview setClipsToBounds:NO];
}
if(indexPath.row == 0){
cell.labelName.text = [person objectForKey:#"name"];
CGFloat height = [[self.labelHeights objectAtIndex:indexPath.section] floatValue];
cell.labelName.numberOfLines = (int)(height / self.fontSizeHeight);
cell.labelName.frame = CGRectChangeHeight(cell.labelName.frame, height);
}
else{
cell.labelName.text = #"";
}
CGFloat cellHeight = [self tableView:self.tableView heightForRowAtIndexPath:indexPath];
cell.borderTop.hidden = YES;
cell.borderBottom.hidden = YES;
cell.borderBottomSmall.hidden = NO;
if(indexPath.row == 0){
cell.borderTop.hidden = NO;
}
if(indexPath.row + 1 == [[person objectForKey:#"list"] count]){
cell.borderBottom.hidden = NO;
cell.borderBottom.frame = CGRectChangeY(cell.borderBottom.frame, cellHeight - 1.f);
cell.borderBottomSmall.hidden = YES;
}
cell.labelDate.text = [object objectForKey:#"date"];
cell.labelPremium.text = [[object objectForKey:#"premium"];
return cell;
}
-- PARTIAL ANSWER
I managed to create a hack, that makes multi-line UILabel visibile when scrolling bottom to up at some point:
- (void) scrollViewDidScroll:(UIScrollView *)scrollView
{
NSArray *cells = [self.tableView visibleCells];
UITableViewCell *cell = [cells objectAtIndex:0];
[cell.superview bringSubviewToFront:cell];
}
I noticed that the part of the UILabel is covered by a row thats below of the UILabels row and that hack makes it would be properly displayed. But it has a drawback, when scrolling slowly from bottom to top it generates a flicker when label is created (part of it should be visible before real creation of UILabel).
Up mentioned answers are not solutions, but "hacks".
In the cell == nil block should be only the initialization.
You should not add any subviews in cell in cellForRowAtIndexPath.
The reason is simple: I will reuse a cell with some labels already added and add a new label.
Either use the default cell.textLabel, either create a subclass for UITableViewCell, with a
-(void)setData:(dictionary or string)object;
and in implementation just set the proper data to proper UI controls.
Add/create controls either in init method in the subclass, or in IB/Storyboard.
Call the dictionary or string should be picked in correspondence to indexPath, so you will always get proper data for proper cell at proper indexPath.
Try This
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellId = #"cellId";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (cell==nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
}
for (UIView *subview in [cell.contentView subviews]) {
[subview removeFromSuperview];
}
/// your UI on cell goes here
return cell;
}

Resources