Change Vertical Spacing of CollectionView Cell - ios

Am using UICollectionView and changing the size of the item in the delegate method of collection view like this
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
lastIndex = 0;
layoutDiff = 4;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row == (lastIndex + layoutDiff) - 1){
layoutDiff = (layoutDiff == 4)?5:4;
lastIndex = indexPath.row + 1;
return CGSizeMake(213.2, 213.2);
}
return CGSizeMake(106.6, 106.6);
}
and here is my current screen.
and all the below cells to be follow this behaviour, means the pattern i want is like one collection view item to big depending on my logic where i want to make it big but i don't want this space. I think if the space will not b there then all things will be right. Can anyone help with this?

FlowLayout can't help with this.
You hava to implement your subclass of UICollectionLayout.
Here are some articles that take about custom Collection View Layout.
Custom Collection View Layouts.
https://www.objc.io/issues/3-views/collection-view-layouts/
Knowing When to Subclass the Flow Layout
https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html#//apple_ref/doc/uid/TP40012334-CH3-SW4

Related

UICollectionView reuse cell issue

I have a problem with collection view cell. When my collection view first loaded, it display items like that:
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath;
{
CalendarCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"CalendarCell" forIndexPath:indexPath];
[cell bindDate:_datesMgr.currentMonthItems[indexPath.row] andNowDate:_datesMgr.nowDate];
// bind events
if (_eventsMgr.eventsArray.count > 0){
for (int i = 0; i < _eventsMgr.eventsArray.count ; i ++) {
[cell bindConference:_eventsMgr.eventsArray[i]];
}
}
return cell;
}
Inside those methods are logic for adding subviews to custom cell class, which depend on certain circumstances.
Its all work, but, when collection view reloaded (i did force reload after 1 second) some of cells are reused and placed on others, therefore, it show "old" images and subviews.
I could see possible solution in forcing uicollection view to stop reusing cells (it is, load new cells every time). Is there any way to do this?
Try to implement prepareForReuse method for reset your old content in your custom UICollectionViewCell
-(void)prepareForReuse {
[super prepareForReuse];
self.yourimageview.image = nil; //and etc.
}

UICollectionView header view not dequeueing properly

I am trying to add a header to a collection view. I am using a custom layout that scrolls horizontally, it is used to view a list of avatar images for friends. I can get the header to appear but it does NOT dequeue. As soon as the header view goes off screen, its gone for good. Can anyone figure out why this is?
Thank you!
Collection View data source:
- (UICollectionReusableView *)collectionView:(SWAvatarViewerCollectionView *)collectionView
viewForSupplementaryElementOfKind:(NSString *)kind
atIndexPath:(NSIndexPath *)indexPath
{
if (self.showAddAvatarHeaderView && [kind isEqualToString:UICollectionElementKindSectionHeader]) {
return [collectionView dequeueAddAvatarViewHeaderForIndexPath:indexPath];
}
return nil;
}
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(SWAvatarViewerCollectionViewFlowLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
if (!self.showAddAvatarHeaderView) {
return CGSizeZero;
}
return CGSizeMake(kSWAvatarViewerAddAvatarHeaderViewWidth, CGRectGetHeight(collectionView.bounds));
}
Avatar collection view:
- (SWAvatarViewerAddAvatarHeaderView *)dequeueAddAvatarViewHeaderForIndexPath:(NSIndexPath *)indexPath {
SWAvatarViewerAddAvatarHeaderView *headerView = [super dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:[SWAvatarViewerAddAvatarHeaderView headerReuseIdentifier]
forIndexPath:indexPath];
headerView.delegate = self;
return headerView;
}
Nib file registration:
[self registerNib:[SWAvatarViewerAddAvatarHeaderView nib]
forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:[SWAvatarViewerAddAvatarHeaderView headerReuseIdentifier]];
Layout:
#pragma mark - Initialization
- (void)configureFlowLayout {
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
// Padding for cells is taken into account in the cell's layout. Remove all
// padding between cells
self.sectionInset = UIEdgeInsetsMake(0, 00.0f, 0, 00.0f);
self.minimumLineSpacing = 0.0f;
self.minimumInteritemSpacing = CGFLOAT_MAX;
_cellBottomLabelFont = [UIFont systemFontOfSize:12.0];
CGSize defaultAvatarSize = CGSizeMake(44.0f, 44.0f);
_avatarViewSize = defaultAvatarSize;
_springinessEnabled = YES;
_springResistanceFactor = 1000;
}
You're apparently using a third-party layout I've never heard of. After some discussion with you, my feeling is that my first comment under your question was probably right: the layout itself may be buggy.
In a collection view, the layout attributes of the cells (position, size, transform, alpha, etc.) are the responsibility of the layout. So if something disappears merely because it is scrolled off the screen and then back on, it sounds like the layout itself is not doing its job correctly.
Quick googleing did not unveil the SWAvatarViewerCollectionViewFlowLayout. If you have the sources, you can take a look at the layout code, there should be a method called layoutAttributesForElementsInRect:.
The collection view is dequeuing items as soon as they go offscreen which is determined with the help of the aforementioned method (The layout attributes contain the views center and dimensions). If the method returns always the layout attributes for the header it will not get dequeued.
However if you do not have access to the code (like a static lib) you probably can not do much about it.
Just put this method that will solve all the problems
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
return UIEdgeInsetsMake(-50, 10, 10, 10); //asuming
//UIEdgeInsetsMake(<#CGFloat top#>, <#CGFloat left#>, <#CGFloat bottom#>, <#CGFloat right#>)
}
Enjoy.

iOS UICollectionViewCells Layout with intersection

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.

Autolayout in UIView where is safe to ask computed layout values?

I've got this issue. I'm using a collection view as a subview of the main view of a UIViewController. The collection view is pinned to the superview size, thus it has 4 constraints for lead,trail, top and bottom with constants equal to 0.
The cell is a subclass to UICollectionViewCell and is composed by:
first header view
second header view
UITableView
The collection view cell is loaded from xib and the interface of the views is made for 4 inches devices.
The first header view is pinned at top, trail, lead with constant 0 and fixed height to its superview.
The second header view is constrained to first header view with vertical spacing equal to 0, fixed height, pinned trail, lead with constant 0 to its superview.
The table view is constrained to second header view with vertical spacing equal to 0 and pinned trail, lead and bottom with constant 0 to its superview.
On 4 inches screen everything is OK, but when I load on 3.5 I have some problems, the fact is that I want to create UITableViewCell with a dynamic height. The height should be the height of the UITableView dived by the number of rows, in this way they will appear on screen.
The table view delegate and datasource are valorized only when I set the data that need to be loaded and that happens after creating it, but before the collection view cell is returned from the data source method.
In the -layoutSubviews method of the UICollectionViewCell subclass I set the tableview row height, to the desired value that is
- (void) layoutSubviews {
[super layoutSubviews];
self.answerTableView.rowHeight = self.answerTableView.bounds.size.height / self.quizSection.answers.count;
}
The fact is that here the table view is stil not resized thus the resulting rowHeight is wrong, the value is the same I receive in 4 inches display. The superview (the contentView of the collection view cell) is ok, but the table view has a size that is not correct. When displayed the collection view is fine except for the fact that the table view's cells are wrong in size.
So I started to trace the cycle of the collection view cell.
In the initWithFrame methods the collection view cell has the same size of the original xib (4 inches screen)
In the view controller if I ask the size of the collection view cell right after dequeuing, if resized to the correct screen size, but the table view inside not
After loading the data and setting delegate and datasource, collection view is ok, but tableview not
In the -updateConstraints of the collection view cell the table view size is still wrong
In the -layoutSubviewsof the collection view cell the table view size is still wrong
The first call of the dtasource method of the table view returns a correct size
I've found the solution that is use the delegate methods - (float) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath, but I'm really curios why the other approach doesn't work can someone explain why?
THX
[CODE FOR HELP]
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) { // Initialization code
NSArray *arrayOfViews = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
if ([arrayOfViews count] < 1) { return nil; }
if (![[arrayOfViews objectAtIndex:0] isKindOfClass:[UICollectionViewCell class]]) { return nil; }
self = [arrayOfViews objectAtIndex:0];
[self.answerTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:AnswerCellIdentifier];
}
return self;
}
- (void) layoutSubviews {
[super layoutSubviews];
self.answerTableView.rowHeight = self.answerTableView.bounds.size.height / self.quizSection.answers.count; //WRONG
}
- (void) updateConstraints {
[super updateConstraints];
self.answerTableView.rowHeight = self.answerTableView.bounds.size.height / self.quizSection.answers.count; //WRONG
}
#pragma mark -
#pragma mark custom getter/setter
- (void) setQuizSection:(QuizSection *)quizSection {
if (_quizSection == quizSection) {
return;
}
_quizSection = quizSection;
//Set data
self.questionLabel.text = _quizSection.question[KEY_TEXT];
self.answerTableView.delegate = self;
self.answerTableView.dataSource = self;
}
#pragma mark -
#pragma mark TableViewDelegate TableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.quizSection.answers.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:AnswerCellIdentifier];
NSDictionary * answerDict = self.quizSection.answers[indexPath.row];
cell.textLabel.text = answerDict[KEY_TEXT];
return cell;
}

How to expand UICollectionView contentSize when paging enable?

I'm making a simple UICollectionView with a paging mechanism enabled, and everything works fine. Though, when scroll to the last page the number of the cells are not fully visible in the screen, and the last page contains some cells of the previous page.
How do I expand the contentSize of the UICollectionView so that the last page doesn't contain any cells of the previous page?
An example here: the UICollectionView scrolls horizontally with 6 cells, this way:
Page 1:
cell0 - cell1 - cell2 - cell3
Page 2:
cell4 - cell5 is expected, but unexpectedly
cell2 - cell3 - cell4 - cell5
How to change it?
SUMMARY:
I want to set
collectionView.contentSize = numberOfPage * collectionView.frame
NOT
collectionView.contentSize = numberOfCell * (cellFrame + spacing)
You need to subclass UICollectionViewLayout and override the collectionViewContentSize method. I subclassed UICollectionViewFlowLayout so I wouldn't have to re-write all the layout code.
I'm building a 4x4 grid, so my method looks like this:
- (CGSize)collectionViewContentSize
{
NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
NSInteger pages = ceil(itemCount / 16.0);
return CGSizeMake(320 * pages, self.collectionView.frame.size.height);
}
Side note, when you use a custom layout, you lose the ability to set the some of the display properties in the Interface Builder. You can set them programatically in the init method of your custom UICollectionViewLayout subclass. Here's mine for reference:
- (id)init
{
self = [super init];
if (self) {
[self setup];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
[self setup];
}
return self;
}
- (void)setup
{
self.itemSize = CGSizeMake(65.0f, 65.0f);
self.minimumLineSpacing = 15;
self.sectionInset = UIEdgeInsetsMake(7.5f, 7.5f, 30.0f, 7.5f);
[self setScrollDirection:UICollectionViewScrollDirectionHorizontal];
}
For horizontal paging
if(CollectionView.pagingEnabled)
{
int numberOfPages = floor(collectionView.contentSize.width /
collectionView.frame.size.width) + 1;
CGFloat
float requierdWidth=self.collectionView.frame.size.width*numberOfPages;
self.Layout.footerReferenceSize=CGSizeMake(requierdWidth-self.collectionView.contentSize.width,0);
}
The answer works well, though for our code we had one section per page. So it meant the override for our layout class was just
-(CGSize) collectionViewContentSize {
return CGSizeMake(CGRectGetWidth(self.collectionView.frame) *
[self.collectionView numberOfSections],
CGRectGetHeight(self.collectionView.frame)) ;
}
I think you have to subclass UICollectionViewLayout and create your custom layout to manage these kind of problems.
I have an answer that doesn't require any subclassing.
In -viewDidLoad, calculate how many items per page you will have and how many pages you will have. Store these values in properties.
self.numberOfItemsPerPage = NumberOfRows * NumberOfColumns;
self.numberOfPages = ceilf((CGFloat)self.items.count / (CGFloat)self.numberOfItemsPerPage);
Then in -collectionView:numberOfItemsInSection: just lie to it:
return self.numberOfItemsPerPage * self.numberOfPages;
You will of course have more cells than content, right? So in -collectionView:cellForItemAtIndexPath: just return nil for the extra cells:
if (indexPath.item > [self.items count] - 1) {
//We have no more items, so return nil. This is to trick it to display actual full pages.
return nil;
}
There you go: full, scrollable final page. In my opinion, the horizontal scroll mode should just default to this.
You can adjust the content with the viewDidLayoutSubviews: method. This method gets called when the collection view and all the cells are placed in the view, so that you can adjust cell.

Resources