I try to make UICollectionView with cells, that intersect and partially overlay each other as it is done at screenshot:
This layout was reached by setting
self.minimumLineSpacing = -100;
at my UICollectionViewFlowLayout subclass.
When I scroll down, everything is OK. I see what I want. But when I scroll up, I see another behaviour, not like I expected:
So my question is: how can I make my layout look as at the first screen regardless scroll view direction.
Note: I have to support both iOS 6 and 7.
Thanks very much for any advices and any help.
Hmm, interesting. Since the collection view recycles cells, they are continuously added to and removed from the view hierarchy as they move on and off the screen. That being said, it stands to reason and when they are re-added to the view, they are simply added as subviews meaning that when a cell gets recycled, it now has the highest z-index of all of the cells.
One fairly pain-free way to rectify this would be to manually adjust the z position of each cell to be incrementally higher with the index path. That way, lower (y) cells will always appear above (z) the cells above (y) them.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID = #"CELLID";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
if (cell.layer.zPosition != indexPath.row) {
[cell.layer setZPosition:indexPath.row];
}
return cell;
}
Found another sollution to solve this problem. We need to use UICollectionViewFlowLayout subclass.
#interface MyFlowLayout : UICollectionViewFlowLayout
#end
#implementation MyFlowLayout
- (void)prepareLayout {
[super prepareLayout];
// This allows us to make intersection and overlapping
self.minimumLineSpacing = -100;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *layoutAttributes = [super layoutAttributesForElementsInRect:rect];
for (UICollectionViewLayoutAttributes *currentLayoutAttributes in layoutAttributes) {
// Change zIndex allows us to change not only visible position, but logic too
currentLayoutAttributes.zIndex = currentLayoutAttributes.indexPath.row;
}
return layoutAttributes;
}
#end
Hope that helps someone else.
Related
I am learning about UITableview on iOS and following a course online. I get the table showing fine, but the images on my cells are not all the way to the left (whereas the instructor's ones are). Here is a screenshot of the cells in question:
I don't want that gap, I want the images to be positioned right at the beggining of the cell, all the way to the left. I have done some research and it seems Apple has changed the default look of the cells between ios6 and ios7 so that now the images in cells show a little gap at the left. To get rid of it, I have tried UIEdgeInsets:
[tableView setSeparatorInset:UIEdgeInsetsZero];
and that's not working. I also have tried this approach:
cell.imageView.frame = CGRectMake( 0, 0, 50, 55 );
Nothing happens. So how would I go about it? Thanks
edit-------------------------------------------------------------------------------------------------------------------------------
Still not have found the answer to this. The solutions posted here don't work. I found this piece of code:
self.tableView.contentInset = UIEdgeInsetsMake(0, -50, 0, 0);
Which besides completely puzzling me (as the parameter affected should be the y?) I thought solved the issue by making the image on the cell appear all the way to the left, until I realised it only moved the whole view to the left (as I should have expected I guess) leaving an equal gap on the other side of the screen. All I want is for my images in the cells to appear all the way to the left of the cell as it used to be the case on previous ios. Thanks
It happens because default table content offset from left is 15, you should change it with 0.
See this once, you get idea Remove empty space before cells in UITableView
If you create custom cells. UITableViewCell have owner imageView. Change title of image in your cell.
If you use default cell, use custom cell with constraint Leading space = 0.
It is better not use default imageView of the cell. Drag and drop UIImageView from objective library, create a custom table view cell (Child class of UITableViewCell) then create and outlet of the image view just dragged.
The spacing in the UITableViewCell is because of the default TRUE returned by shouldIndentWhileEditingRowAtIndexPath method of UITableViewDelegate.
I was able to reproduce your problem by the below scenario:
UITableView is in editable mode:
self.tableView.editing = true
And you have implemented:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView
editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleNone;
}
To correct your code:
If you do not want to set Editing Style then you can turn off the editing mode by
self.tableView.editing = false
and remove editingStyleForRowAtIndexPath.
Else if you need editing mode then set the appropiate Editing style(UITableViewCellEditingStyleDeleteor UITableViewCellEditingStyleInsert) or simply turn the indentation off.
- (BOOL)tableView:(UITableView *)tableView
shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath {
return FALSE;
}
You must create a custom cell, by adding a new class as a subclass of UITableViewCell. then you can design cell with autolayout and constraints which will resolve the issue.
there is a another concrete way to achieve this by creating subclass uitableviewcell (custom class).
steps to follow
create a class subclass of UITableViewCell.
in .h file create properties and outlets of UI components.
go to storyboard and add table view cell inside the tableview.
now add UI components like: imageview or button etc and set the x, y values according to.
make class of custom cell your className using identity inspector see image.
connect all outlets of UI components.
use below code uitableview
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *MyIdentifier = #"uniqueIdentifire";
yourCustomClassForCell *cell = (yourCustomClassForCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil){
cell = [[yourCustomClassForCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier];
}
cell.imageView.image = [imageAry objectAtIndex:indexPath.row];
}
Dont forget to give identifire by selecting your cell using storyboard Attribute inspector uniqueIdentifire to identifire property see image.
Also you can give some vertical space between cells by just to add this below code (Method only) inside customeCellClass.
- (void)setFrame:(CGRect)frame { // method to insert gap between table view cell
frame.origin.y += 6;
frame.size.height -= 2 * 6;
[super setFrame:frame];
}
You can not really change the frame of the inbuilt subviews of uitableviewcell like imageview, accessoryview. But if you create a custom tableviewcell class(even if you do not add any other subelement to it), you can change the frame of the inbuilt imageview by overriding the layoutSubviews method inside the UITableViewCell. I have tried it and it works.
#import "TableViewCell.h"
#implementation TableViewCell
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
-(void) layoutSubviews{
[super layoutSubviews];
CGRect frame = self.imageView.frame;
frame.origin.x = 0;
self.imageView.frame = frame;
}
#end
I want to make a usual horizontalScrolling flowLayout UICollectionView with estimatedItemSize and preferredLayoutAttributesFittingAttributes in cell. But there is something wrong with last cell. Any idea where is the issue?
Project itself
#implementation RowCollectionView
- (instancetype) initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
{
if (self = [super initWithFrame:frame collectionViewLayout:layout])
{
[self configureRowCollectionView];
}
return self;
}
- (void) awakeFromNib
{
[super awakeFromNib];
[self configureRowCollectionView];
}
- (void) configureRowCollectionView
{
self.backgroundColor = [UIColor lightGrayColor];
self.dataSource = self;
self.delegate = self;
// Horizontal Direction
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *) self.collectionViewLayout;
flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
// Estimated Item Size
flowLayout.estimatedItemSize = CGSizeMake(self.bounds.size.height, self.bounds.size.height);
[self registerClass:[RowCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass([RowCollectionViewCell class])];
}
- (NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 10;
}
- (UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([RowCollectionViewCell class]) forIndexPath:indexPath];
cell.contentView.backgroundColor = [UIColor redColor];
return cell;
}
#end
#implementation RowCollectionViewCell
- (UICollectionViewLayoutAttributes *) preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
[super preferredLayoutAttributesFittingAttributes:layoutAttributes];
UICollectionViewLayoutAttributes *attributes = [layoutAttributes copy];
attributes.size = CGSizeMake(80, 80);
return attributes;
}
#end
I face a similar issue and the problem was solved by giving a proper minimum inter item spacing, using the delegate methods - minimumInteritemSpacingForSectionAt- of UICollectionViewDelegateFlowLayout.
You are setting estimatedItemSize in the init of view itself.
You need to set it in some controller.
Also,
If all of your cells are the same height, use the itemSize property, instead of this property, to specify the cell size instead.
Documentation: estimatedItemSize
there is a simple method to resolve this. You can add number of prototype cells to check the cell at required position. Once you find the issue at last cell . Check the cell insets in Inspector window.
You call super method but you did not use super returned layoutAttributes.
[super preferredLayoutAttributesFittingAttributes:layoutAttributes];
You can try to print out original layoutAttributes vs super's layoutAttributes.
Sometimes, you don't need to call super function.
Second, You can create custom flowlayout or set inset to let your cell align top. I did this in my project.
You can consider it a Suggestion. According to me the height of UICollectionView is more than UICollectionViewCell Height, thats why its happening. please make them equal them
Custom cell size must be same as that of collection view cell,please check that.It may solve the problem for you.
I have done the similar small project (one raw (1*N) horizontal collection view), here is the github. I hope it would be helpful for your requirement.
https://github.com/texas16/HorizontalCollectionView
I had the same issue and the simplest solution in case you have an horizontal collection having that issue is to make the collection height equal to the items height.
Had same problem and what fix it in my case was to make sure :
All cells height are equal.
The collectionView height is bigger then cell height + space between cells.
I'm building a status item kind of thing for a UICollectionView. My problem is when I want to add some text to the status area I can't get the thing to auto resize to the new text. I have auto layout on and I've tried all kinds of things found on stacky.
The one which I think is the closest to being correct is this:
-(UICollectionViewCell *) collectionView:(UICollectionView*)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
StatusItemModel *statusModel = [self.items objectAtIndex[indexPath indexPosition:0]];
StatusItemEventCell *statusCell = [collectionView dequeueResusableCellwithReuseIdentifier: #"EventStatusItem" forIndexPath:indexPath];
statusCell.statusTitleLabel.text = [statusModel.statusDetails valueForKey:#"title"];
statusCell.statusContentTextView.text = [statuaModel.statusDetails valueForKey:#"content"];
[statusCell layoutIfNeeded];
return statusCell;
}
// After which I believe we have to do some magic in this but what?
- (CGSize) collectionView:(UiCollectionView *) collectionView layout:(UICollectionViewLayout *) collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
// How do I get the size of the textview statusContentTextView.text?
// With that I'll be able to figure out what needs to be returned.
return CGSizeMake(299.f, 200.f);
}
The autolayout is setup with constraints for all elements in the cell. I've even played around with the intrinsic size and placeholders, however still now luck. Please can someone point me in the right direction.
So after going around in circles thinking there was a better way, no we need to know the size before we can set the size of the cell for the collection view. Pretty counter productive, because sometimes we don't know the size of it at run time. The way I solved this was to create a mock UITextView object and then called sizeThatFits.
So here is what I did with my code:
- (CGSize) collectionView:(UICollectionView *) collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
StatusItemModel *statusModel = [self.items objectAtIndex:[indexPath indexAtPosition:0]];
UITextView *temporaryTextView = [[UITextView alloc] init];
temporaryTextView.text = [statusModel.statusDetails valueForKey:#"content"];
CGSize textBoxSize = [temporaryTextView sizeThatFits:CGSizeMake(299.0f, MAXFLOAT)];
// Can now use the height of the text box to work out the size of the cell and
// the other components that make up the cell
return textBoxSize;
}
I need to have just simple UICollectionViewCell style with cells on top of eachoher. Like tableview. But I need Dynamic height dependent of the content, size the content is comments it can vary.
I got
viewDidLoad:
[self.commentsCollectionView registerClass:[GWCommentsCollectionViewCell class] forCellWithReuseIdentifier:#"commentCell"];
in .h I got:
and I #import my custom UICollectionViewCell that sets all constraints with programmatic autolayout.
I instantiate the UICollectionView with:
UICollectionViewFlowLayout *collViewLayout = [[UICollectionViewFlowLayout alloc]init];
self.commentsCollectionView = [[UICollectionView alloc]initWithFrame:CGRectZero collectionViewLayout:collViewLayout];
I use autolatyout to get the UICollectionView be where I want (thats why CGRectZero).
And finally I was hoping to do this:
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
GWCommentsCollectionViewCell *cell = (GWCommentsCollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];
return cell.singleCommentContainerview.bounds.size;
}
singleCommentContainerview is a direct subview of the contentView and withing the singleCommentContainerview I have UILabels, UIImageViews etc, all set witih autolayoutcode.
But I just get cgsize value of (0,0)
How can I fix this to get the proper size I need for each cell?
From what I have read UICollectionView needs the sizes worked out before laying out the cell. So the above method of yours that cell hasn't yet been drawn so it has no size. Also it could be an issue or combined with the issue that the cell is cached/pooled with the same identifier #"commentCell", I tag unique cells with a new identifier and class normally.
My thoughts are to catch the cell before it is drawn, push the size into a dictionary for use later, using:
- (void)collectionView:(UICollectionView *)collectionView
willDisplayCell:(UICollectionViewCell *)cell
forItemAtIndexPath:(NSIndexPath *)indexPath{
GWCommentsCollectionViewCell *cell = (GWCommentsCollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];
// Need to add it to the view maybe in order for it the autolayout to happen
[offScreenView addSubView:cell];
[cell setNeedsLayout];
CGSize *cellSize=cell.singleCommentContainerview.bounds.size
NSString *key=[NSString stringWithFormat:#"%li,%li",indexPath.section,indexPath.row];
// cellAtIndexPath is a NSMutableDictionary initialised and allocated elsewhere
[cellAtIndexPath setObject:[NSValue valueWithCGSize:cellSize] forKey:key];
}
Then when you need it use that dictionary based off the key to get the size.
Its not a really super pretty way as its dependent on the views being drawn, autolayout doing its thing before you get the size. And if you are loading images even more it could throw up issues.
Maybe a better way would be to preprogram the sizes. If you have data on the images sizes that may help. Check this article for a really good tutorial (yah programatically no IB):
https://bradbambara.wordpress.com/2014/05/24/getting-started-with-custom-uicollectionview-layouts/
Add
class func size(data: WhateverYourData) -> CGSize { /* calculate size here and retrun it */}
to your custom cell and instead of doing
return cell.singleCommentContainerview.bounds.size
it should be
return GWCommentsCollectionViewCell.size(data)
I'm attempting to implement a collection view with an infinite scrolling behaviour. The scrolling should be circular - like in a UIPickerView. Can it be done with Apple's UICollectionView or there is no choice but to create a horizontal PickerView ?
What do you think?
You should watch the WWDC 2011 session video "Advanced ScrollView Techniques", they talk about infinite scrolling, and I'm pretty sure it's possible to use that with UICollectionViews. It recenters while you scroll, and places the new items after the current visible items (or before, depending on the scroll direction).
Like they say in the session, you should not use an approach that has an ending. Like stated in the session: People will definitely find the edge at one point.
Yes, you can do this. Return a very large number for collectionView:numberOfItemsInSection:, and in collectionView:cellForItemAtIndexPath:, you use the mod operator to always return a number for indexPath.Row that's between 0 and the last index in your data array. So if you had 20 items in your array, you would do something like this:
item.whatever.text = [self.theData objectAtIndex:indexPath.row %20];
Thanks to rdelmar for a tricky solution. That idea of "large number for collectionView:numberOfItemsInSection" worked like a charm. I have tried to implement the same with UIScrollView delegate methods. But the output was jerky.
Here's some part of my code.
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 100;}
// CellforRow
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// Setup cell identifier
static NSString *cellIdentifier = #"cvCell";
CVCell *cell;
cell = (CVCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
[cell.titleLabel setText:[self.dataArray objectAtIndex:(indexPath.row)%self.dataArray.count]];
return cell;
}
// DidSelect
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"Index path is %d", indexPath.row%self.dataArray.count);
}
This will give infinite scrolling effect.
To enable infinite scrolling both sides, use ScrolltoIndexpath:(middle of Itemscount) in ViewWillAppear !!
Hope this will help someone who are about to build an infinite scrolling UICollectionView.
Override this method to enable infinite scrolling both ways. It center content whenever it is too far from the center and user never hits the end.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint currentOffset = self.collectionView.contentOffset;
CGFloat contentWidth = self.collectionView.contentSize.width;
CGFloat centerOffsetX = (contentWidth - self.collectionView.bounds.size.width) / 2.0;
CGFloat distanceFromCenter = fabs(currentOffset.x - centerOffsetX);
if (distanceFromCenter > (contentWidth / 4.0)) {
self.collectionView.contentOffset = CGPointMake(centerOffsetX, currentOffset.y);
}
}