I have UICollectionView with customLayOut, when I start scrolling,
I change x coordinate for the cell that should start to be visible - in order to be visible from the begining.
-(void)layoutSubviewsWithAttributes:(NSMutableArray *)theAttributes {
CGFloat halfScreen = [UIScreen mainScreen].bounds.size.width / 2;
CGPoint startOfScreen = CGPointMake(self.timeLineCollection.bounds.origin.x, 25.f);
for(UICollectionViewLayoutAttributes *attribute in theAttributes) {
if(CGRectContainsPoint(attribute.frame, startOfScreen)) {
NSLog(#"Intersect %#", NSStringFromCGRect(attribute.frame));
attribute.frame = CGRectMake(startOfScreen.x, startOfScreen.y, attribute.frame.size.width, attribute.frame.size.height);
}
}
but in some cases this cell appears on the top of the next one cell (wrong behaviour),
another time, that cell in under the next one cell (as I need it to be placed).
Could you advice, how to sort that cells to be placed hierarchically one on another (like cards).
Thank you in advance, for any help.
Try this with negative value it may help as i haven't tried but i am sure it should work like this.
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
return -5.0;
}
UICollectionViewLayoutAttributes has such a property as zIndex, that - Specifies the item’s position on the z axis.
So to tell cells to be set one after another, I just set to each attribute, zIndex - number of each cell and it places it one after another, inside the customLayOut.
for (NSInteger item = 0; item < self.cellCountPrograms; item++) {
indexPath = [NSIndexPath indexPathForItem:item inSection:0];
UICollectionViewLayoutAttributes *itemAttributes =
[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
itemAttributes.frame = [self frameForItemAtIndexPath:indexPath];
itemAttributes.zIndex = item;
cellProgramLayoutInfo[indexPath] = itemAttributes;
}
Related
I created a collection view cell with the Xib file. And I have only one cell, but that cell displaying in the center of the collectionView. I want to display cell at starting position of the collectionView.
I fixed contentInset in viewDidLoad and loading cell xib file here,
_listCollectionView.contentInset = UIEdgeInsetsMake(0 , 0, 0, 0);
And my cell size is
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(156,140);
}
Cell displaying like this.
Im not very clear with your question but i think what you are referring to is to align the collectionnView element to the view . You have to use UICollectionViewLayoutAttributes or layoutAttributesforElements to achieve this . Take look at the below code and to make if you want it in Objective c.
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
CGFloat leftMargin = self.sectionInset.left; //initalized to silence compiler, and actaully safer, but not planning to use.
CGFloat maxY = -1.0f;
//this loop assumes attributes are in IndexPath order
for (UICollectionViewLayoutAttributes *attribute in attributes) {
if (attribute.frame.origin.y >= maxY) {
leftMargin = self.sectionInset.left;
}
attribute.frame = CGRectMake(leftMargin, attribute.frame.origin.y,
attribute.frame.size.width, attribute.frame.size.height);
leftMargin += attribute.frame.size.width + self.minimumInteritemSpacing;
maxY = MAX(CGRectGetMaxY(attribute.frame), maxY);
}
return attributes;
}
In collectionView: cellForItemAtIndexPath: function i made changes like this.
Here just i fixed my collectionviewcell x origin position to 0 after loading the cell.
deviceCell= (DeviceCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"DeviceCollectionViewCell" forIndexPath:indexPath];
if (listArray.count == 1) {//Here i fixed the my cell x origin position
CGRect frameRect = deviceCell.frame;//Copy cell frame
frameRect.origin.x = 0;//Set cell origin.x
deviceCell.frame = frameRect;//Reset frame
}
Is there any way to customize UICollectionViewFlowLayout, without UICollectionViewDelegateFlowLayout methods ?
And could parse the UICollectionViewLayoutAttributes with size given, without giving origin?
So I need not implement the frame of per UICollectionViewLayoutAttributes in the - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect method.
Not calculating the origin of each Item does a lot convenience.
Here is my Sample code:
// call the two layout calculating methods , and combine.
- (void)prepareLayout{
[super prepareLayout];
if(self.attributesArray.count>0){
[self.attributesArray removeAllObjects];
}
NSInteger indexCount = [self.collectionView numberOfItemsInSection: 0];
for (NSInteger i = 0; i<indexCount; i++) {
NSIndexPath * indexPath = [NSIndexPath indexPathForItem:i inSection: 0];
UICollectionViewLayoutAttributes * attributes = [self layoutAttributesForItemAtIndexPath: indexPath];
[self.attributesArray addObject: attributes];
}
UICollectionViewLayoutAttributes * headAttributes = [self layoutAttributesForSupplementaryViewOfKind: UICollectionElementKindSectionHeader atIndexPath: [NSIndexPath indexPathForItem:0 inSection: 0]];
[self.attributesArray addObject: headAttributes];
}
// calculate the items layout
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewLayoutAttributes * cellAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath: indexPath];
CGFloat baseOriginY = 133;
switch (indexPath.item) {
case 0:
{
cellAttributes.frame = CGRectMake(0, baseOriginY, KScreenWidth, 50);
}
break;
case 6: //7:
case 7: //8:
{
NSInteger count = indexPath.item-6;
cellAttributes.frame = CGRectMake(0, 155 + 50*count + baseOriginY, KScreenWidth, 50);
}
break;
case 5: //6:
{
cellAttributes.frame = CGRectMake(0, 145 + baseOriginY, KScreenWidth, 10);
}
break;
default:
{
NSInteger i = indexPath.item - 1;
cellAttributes.frame = CGRectMake(i*KScreenWidth/4.0, 50 + baseOriginY, KScreenWidth/4.0, 95);
}
break;
}
return cellAttributes;
}
// calculate the headers layout
- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath{
UICollectionViewLayoutAttributes * headerAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind: UICollectionElementKindSectionHeader withIndexPath: indexPath];
// headerAttributes.size = CGSizeMake(KScreenWidth, 133);
headerAttributes.frame = CGRectMake(0, 0, KScreenWidth, 133);
return headerAttributes;
}
// return the final layout results
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
return self.attributesArray;
}
In other words, use the adjusts dynamically feature of UICollectionViewFlowLayout, without conforms to UICollectionViewDelegateFlowLayout protocol.
The adjusts dynamically feature, as Apple says:
Flow layouts lay out their content using a fixed distance in one
direction and a scrollable distance in the other. For example, in a
vertically scrolling grid, the width of the grid content is
constrained to the width of the corresponding collection view while
the height of the content adjusts dynamically to match the number of
sections and items in the grid.
I think it is quite possible, I don't know how to implement it now. Maybe a Apple's API.
Any advice or explanation is very welcomed.
Many Thanks in advance.
1.When the content inside the cell is too long, cell expansion, Or then shrink, UITableView will scroll to the back of the cell position.
2.I want cell to roll back to where it started expansion.
my code:
((PartnershipsTableViewCell *)cell).commentSpreadButtonClickHandler = ^() {
// just call - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
[weakSelf.tableView beginUpdates];
[weakCell configUI];
[weakSelf.tableView endUpdates];
// if here use "[weakSelf.tableView reloadData]",
// it can be correct,
// Unless on the first cell which have the expansion button.
};
then update uitableview cell's height. but the result isn't what i want
- (void)configUI {
if ([self.baseModel isKindOfClass: [UserWorldDynamicModel class]]) {
self.model = (id)self.baseModel;
}
[self setupValue];
}
- (void)setupValue {
// setup the property value, and update the constraints with masonry
}
// the button : read less or read more
- (void)setSpreadButton {
NSString *text = self.model.isContentOpen ? #"read less" : #"read more";
if (!self.spreadButton) {
self.spreadButton = [MYSUtil createButtonWithTitle: text target: self sel: #selector(spreadButtonClick:) image: nil font: Font14 color: DarkBlueTextColor cornerRadius: 0];
self.spreadButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
}
if (self.model.shouldShowSpreadButton) {
if (!self.spreadButton.superview) {
[self.whiteBackgroudView addSubview: self.spreadButton];
}
[self.spreadButton setTitle: text forState: UIControlStateNormal];
self.spreadButton.selected = [self.model.isSpreadState intValue];
self.spreadButton.frame = CGRectMake(CGRectGetMinX(self.contentLabel.frame), CGRectGetMaxY(self.contentLabel.frame), 80, 30);
self.tempView = self.spreadButton;
} else {
if (self.spreadButton.superview) {
[self.spreadButton removeFromSuperview];
}
}
}
// calculate the height of the label and compare it with the fixed value
NSMutableAttributedString *string = [self createMutableAttibuteStringWithNSString: text withFont: font];
self.contentLabel.attributedText = string;
// here calculate maxContentLabelHeight with
CGFloat maxContentLabelHeight = self.contentLabel.font.pointSize * (numberOfLines + 1) + 16;
// here calculate the NSMutableAttributedString's height
YYTextContainer *container = [YYTextContainer containerWithSize:CGSizeMake(width, MAXFLOAT)];
YYTextLayout *textLayout = [YYTextLayout layoutWithContainer:container text: string];
CGSize size = textLayout.textBoundingSize;
CGFloat height = size.height;
// then compare the NSMutableAttributedString's height with the fixed value. if true, show the spreadButton
if (height > maxContentLabelHeight) {
self.model.shouldShowSpreadButton = YES;
// storage the real height and temp height, use to calculate the tableView's contentOffset, when cell from expansion state to shrinking state in block.
self.model.contentHeight = height;
self.model.tempContentHeight = maxContentLabelHeight;
}
// if height > maxContentLabelHeight and the property "isContentOpen" of the viewModel, the height value is maxContentLabelHeight, Or not, the height value is height
if (!self.model.isContentOpen && height > maxContentLabelHeight) {
height = maxContentLabelHeight;
}
// no matter cell is expansion state or shrinking state, reset label's frame.
self.contentLabel.frame = CGRectMake(x, CGRectGetMaxY(self.headerImageView.frame) + Margin_Top, width, height);
readMore/ readLess block
before tableView reloadData on mainQueue, record it's contentOffset, Used to calculate the position of the tableView need to scroll. like this:
CGPoint point = weakSelf.tableView.contentOffset;
reloadData : refresh tableView On mainQueue.
when reloadData complete, scroll tableView to the position which expanded. when tableView from expansion state to shrinking state, and the height of expansion state is greater than 70% of the Screen's height, scroll the tableView (70% is ma condition, you can change is according to your condition)
- (UITableViewCell *)tableView:(UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath {
// here is your code
PartnershipsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellIdentifierString];
[cell config];
((PartnershipsTableViewCell *)cell).spreadButtonClickHandler = ^() {
CGPoint point = weakSelf.tb.contentOffset;
[weakSelf.tb reloadData];
if (!baseModel.isContentOpen && baseModel.contentHeight > SCREEN_HEIGHT * 0.7) {
point.y -= baseModel.contentHeight - baseModel.tempContentHeight;
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.tb.contentOffset = point;
});
}
};
return cell;
}
- (CGFloat)tableView:(UITableView *) tableView heightForRowAtIndexPath:(NSIndexPath *) indexPath {
return 70;
}
in my issue, I find a another interesting problem, when use the follow method in your code. if your cell have great changes, especially the height of the cell. when you use the method [tableView reloadData], you'd better not use the follow method.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 70;
}
then use the method [tableView reloadData] to refresh UI, tableView will scroll to any position, so I delete it. because, this method use to estimated height of cell, then use to estimate tableView's contentSize, if the height between estimated and actual is bigger difference, use the method [tableView reloadData], will cause the tableView scroll to anywhere.(don't ask me how to know, this is a painful process for me).
I have solved the problem, leave some notes to myself, and for every one, and hope my solution can help you too.
thanks for #pckill and #Theorist, without your suggestion, I can't solve my question so perfect, thank you very much.
#Theorist I have reedited my code in my mind. Perhaps, the readability is better now.
How to get the frame (CGRect value) of a whole section in a UICollectionView instance?
I know that UITableView has a rectForSection: method. I am looking for a similar thing for UICollectionView.
I checked the documents for UICollectionViewFlowLayout but couldn't find anything that looks like rectForSection:.
to collectionView add category
#implementation UICollectionView (BMRect)
- (CGRect)bm_rectForSection:(NSInteger)section {
NSInteger sectionNum = [self.dataSource collectionView:self numberOfItemsInSection:section];
if (sectionNum <= 0) {
return CGRectZero;
} else {
CGRect firstRect = [self bm_rectForRowAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
CGRect lastRect = [self bm_rectForRowAtIndexPath:[NSIndexPath indexPathForItem:sectionNum-1 inSection:section]];
return CGRectMake(0, CGRectGetMinY(firstRect), CGRectGetWidth(self.frame), CGRectGetMaxY(lastRect) - CGRectGetMidY(firstRect));
}
}
- (CGRect)bm_rectForRowAtIndexPath:(NSIndexPath *)indexPath {
return [self layoutAttributesForItemAtIndexPath:indexPath].frame;
}
#end
Collection view section frame is depend on the collection view layout.It might be flow layout or custom layout. So you will not got the any predefined solution. But you can calculate it for your layout using
UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath];
UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
I will suggest you to subclass UICollectionViewLayout and put you section frame calculation code inside it, so that you section frame will always be the updated one.
NOTE : These point will not give you superset of rect in case of section start point and end point not vertically aligned.To be honest there is no better way to get section frame.
In my UITableView i've set different heights for different rows using the delegate method: tableView:heightForRowAtIndexPath:
Now given a NSIndexPath, I would like to get the height I've previously assigned for a specific row.
You can use this
CGRect frame = [tableView rectForRowAtIndexPath:indexPath];
NSLog(#"row height : %f", frame.size.height);
Using frame.size.height you can get height of particular row
Swift 3
let frame = tableView.rectForRow(at: indexPath)
print(frame.size.height)
Simplly Use
tableView:heightForRowAtIndexPath
method
int row = 1;
int section = 0;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
float height = [self tableView:tableView heightForRowAtIndexPath:indexPath];
Hope this wil help for you :)
-(CGFloat)getHeightAtIndexPath:(NSIndexPath *)indexPath{
if(indexPath.row == 0)
return 20.0;
else if(indexPath.row == 4)
return 40.0;
return 50.0; //Default
}
Call the above function in tableView:heightForRowAtIndexPath: as
return [self getHeightAtIndexPath:indexPath];
And you can use the same function on say button click action of a button inside a specific cell, with having button.tag=indexPath.row
-(IBAction)btnClick:(id)sender{
UIButton *btn=(UIButton *)sender;
NSIndexPath *indexPath=[NSIndexPath indexPathForRow:btn.tag inSection:0];
CGFloat height=[self getHeightAtIndexPath:indexPath];
}
By using below code you can get the cell height from tableview
let height = super.tableView(tableView, heightForRowAt: indexPath)