why attributes properties in layoutAttributesForItemAtIndexPath does not get applied? - ios

If I modified any attribute property in layoutAttributesForItemAtIndexPath the collectionView cell does not get modified
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *attributes = [super layoutAttributesForItemAtIndexPath:indexPath];
//this properties does not get applied
attributes.zIndex = 1;
attributes.alpha = 0.5;
return attributes;
}
but when moving the properties modifications to layoutAttributesForElementsInRect it gets applied:
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *attributesArray = [super layoutAttributesForElementsInRect:rect];
for (UICollectionViewLayoutAttributes *attributes in attributesArray)
{
//this properties is getting applied here
attributes.zIndex = 1;
attributes.alpha = 0.5;
}
return attributesArray;
}
I'am applying a UIPanGestureRecognizer on the UICollectionView and I want to apply these
properties on the cell I'am currently dragging.
I'am able to call layoutAttributesForItemAtIndexPath method while I'am dragging through calling this method:
[customLayoutInstance indexOfItemSelected:indexPath.row];
layoutAttributesForItemAtIndexPath is called as long as I'am dragging but the properties does not get applied.

you should override one more method:
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
{

Related

CALayer inside UICollectionViewCell pinch

I added a GIF to describe a problem. I have a UICollectionView with a lot of cells and each cell has a CALayer inside. I have a pinch gesture in my UICollectionView. When it zooming in it looks like each cell zooming apart. See spaces between cells on gif. Is it possible to zoom in each cells together? Thanks in advance
Code:
Cell subclass
#property (nonatomic, strong) CALayer *circularLayer;
- (void)layoutSubviews
{
[self updateRoundedCorners];
}
- (void)updateRoundedCorners
{
CGRect bounds = self.bounds;
self.circularLayer.bounds = bounds;
self.circularLayer.position = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
}
#pragma mark - Property Set
- (void)setCellObject:(VenueLayoutCellObject *)cellObject
{
self->_cellObject = cellObject;
self.objectBackgroundColor = cellObject.objectColor;
self.type = cellObject.type;
}
Controller
- (void)didReceivePinchGesture:(UIPinchGestureRecognizer *)gesture
{
static CGFloat scaleStart;
if (gesture.state == UIGestureRecognizerStateBegan) {
scaleStart = self.venueLayoutZoom;
}
else if (gesture.state == UIGestureRecognizerStateChanged) {
self.venueLayoutZoom = scaleStart * gesture.scale;
[self.activeCollectionView.collectionViewLayout invalidateLayout];
}
}
Updated:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
VenueLayoutCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kVenueLayoutCellReuseIdentifier forIndexPath:indexPath];
self.activeCollectionViewCellsDictionary[indexPath] = cell;
if (self.activeCollectionViewObjects.count > indexPath.section) {
NSArray *rows = self.activeCollectionViewObjects[indexPath.section];
if (rows.count > indexPath.row) {
if ([rows[indexPath.row] isKindOfClass:[VenueLayoutCellObject class]]) {
VenueLayoutCellObject *object = rows[indexPath.row];
cell.cellObject = object;
}
}
}
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(nonnull NSIndexPath *)indexPath
{
CGFloat widthAndHeight = [self widthAndHeightForActiveCollectionViewItem];
return CGSizeMake(widthAndHeight *self.venueLayoutZoom, widthAndHeight * self.venueLayoutZoom);
}
- (CGFloat)widthAndHeightForActiveCollectionViewItem
{
NSArray *array = self.activeCollectionViewObjects;
__block NSInteger maxCount = 0;
[array enumerateObjectsUsingBlock:^(NSArray *subArray, NSUInteger idx, BOOL * _Nonnull stop) {
if (subArray.count > maxCount) {
maxCount = subArray.count;
}
}];
CGFloat widthAndHeight = CGRectGetWidth(self.activeCollectionView.bounds) / maxCount;
return widthAndHeight;
}
The changes you are making to the layer are implicitly animated, meaning that they are performed with an animation, even though you haven't specifically told them to.
Since you're responding to user gestures, you don't want the changes to be animated as this is making them lag behind the gesture.
You can turn off implicit animations during layoutSubviews like this:
- (void)layoutSubviews
{
[super layoutSubviews];
[CATransaction begin];
[CATransaction setDisableActions: YES];
[self updateRoundedCorners];
[CATransaction commit];
}
Cell subclass
#property (nonatomic, strong) CALayer *circularLayer;
- (void)layoutSubviews
{
[super layoutSubviews];
[self updateRoundedCorners];
}
The only problem I see with this code importantly is missing [super layoutSubviews];
If it doesn't work do provide more code.

UICollectionView decoration view above some cells when deletesections method called

I have a custom layout inherited from UICollectionViewFlowLayout used to achieve for each section to add backgroundview. The first time to show that there is no problem, but when I use deletesections method to delete the first section, some cells on the display in the decoration view below, the strange thing is to delete the data and then call the reloadData method will normal display.
I have put the code into GitHub:https://github.com/TonightGod/UICollectionView-DecorationView
pictures:
after deleteSections method called
code:
#interface MyCustomLayout()
#property(nonatomic,strong) NSMutableArray *decorationViews;
#end
#implementation MyCustomLayout
-(NSMutableArray*)decorationViews
{
if(!_decorationViews)
{
_decorationViews=[NSMutableArray array];
}
return _decorationViews;
}
- (void)prepareLayout {
[super prepareLayout];
[self.decorationViews removeAllObjects];
NSInteger sections=[self.collectionView numberOfSections];
for(NSInteger section=0;section<sections;section++)
{
NSInteger items=[self.collectionView numberOfItemsInSection:section];
if(items>0)
{
CGRect firstItemFrame=[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]].frame;
CGRect lastItemFrame=[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:items-1 inSection:section]].frame;
UIEdgeInsets sectionInset=self.sectionInset;
if([self.collectionView.delegate respondsToSelector:#selector(collectionView:layout:insetForSectionAtIndex:)])
{
sectionInset = [(id<UICollectionViewDelegateFlowLayout>)self.collectionView.delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:section];
}
CGRect sectionFrame=CGRectUnion(firstItemFrame, lastItemFrame);
sectionFrame.origin.x=0;
sectionFrame.origin.y-=sectionInset.top;
if(self.scrollDirection==UICollectionViewScrollDirectionHorizontal)
{
sectionFrame.size.width += sectionInset.left + sectionInset.right;
sectionFrame.size.height = self.collectionView.frame.size.height;
}else
{
sectionFrame.size.width = self.collectionView.frame.size.width;
sectionFrame.size.height += sectionInset.top + sectionInset.bottom;
}
NSString *identifer=[self.delegate collectionView:self.collectionView decorationViewIdentiferAtSection:section];
if(identifer)
{
UICollectionViewLayoutAttributes *decorationAttributes=[UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:identifer withIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
decorationAttributes.frame=sectionFrame;
decorationAttributes.zIndex=-1;
[self.decorationViews addObject:decorationAttributes];
}
}else
{
continue;
}
}
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
NSMutableArray *allAttributes = [NSMutableArray arrayWithArray:attributes];
[allAttributes addObjectsFromArray:[self.decorationViews filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
UICollectionViewLayoutAttributes *obj=evaluatedObject;
return CGRectIntersectsRect(rect, obj.frame);
}]]];
return allAttributes;
}
#protocol MyCustomLayoutDelegate <UICollectionViewDelegateFlowLayout>
-(NSString*)collectionView:(UICollectionView *)collectionView decorationViewIdentiferAtSection:(NSInteger)section;
#end
#interface MyCustomLayout : UICollectionViewFlowLayout
#property(nonatomic,weak) id<MyCustomLayoutDelegate> delegate;

Custom collection view layout attributes not being set before being applied to a cell

I am writing my own custom layout collection view layout (minor customizations to flow layout) and to simplify a few things I am trying to subclass UICollectionViewFlowLayoutAttributes too. Everything is working perfectly fine but when I try to apply my layout attributes in my custom cell every attribute is nil.
Cell (attributes are all nil here):
- (void)applyLayoutAttributes:(CustomLayoutAttributes *)layoutAttributes
{
[super applyLayoutAttributes:layoutAttributes];
NSLog(#"layoutAttributes %#", layoutAttributes.description);
}
Custom Flow Layout (Attributes being applied perfectly!!!):
+ (Class)layoutAttributesClass
{
return [CustomLayoutAttributes class];
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *attributesInRect = [super layoutAttributesForElementsInRect:rect];
[attributesInRect enumerateObjectsUsingBlock:^(CustomLayoutAttributes *layoutAttributes, NSUInteger idx, BOOL *stop) {
if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) {
[self configureLayoutAttributes:layoutAttributes];
}
}];
return attributesInRect;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomLayoutAttributes *layoutAttributes = (CustomLayoutAttributes *)[super layoutAttributesForItemAtIndexPath:indexPath];
if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) {
[self configureLayoutAttributes:layoutAttributes];
}
return layoutAttributes;
}
- (void)configureLayoutAttributes:(CustomLayoutAttributes *)layoutAttributes
{
CustomCollectionView *collectionView = (CustomCollectionView *)self.collectionView;
layoutAttributes.messageTopLabelHeight = 20.0;
layoutAttributes.messageBottomLabelHeight = 20.0;
layoutAttributes.messageBubbleFont = _messageBubbleFont;
}
Am I doing something wrong or missing something? :).
Note: I created all my cells in a storyboard, and set my custom layout in storyboard as well.
Thanks guys.
figured out the problem...apparently UICollectionViewLayoutAttributes must conform to NSCopying and implement copyWithZone:. After doing that everything worked perfectly.

UICollectionViewFlowLayout animation between datasets

I have a UICollectionView that when a cell is pressed, there will be a new set of data and will animate the flow layout.
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
for(int i=0;i<200;i++)
{
[tempArray addObject:[NSNumber numberWithInt:i]];
}
items = [NSArray arrayWithArray:tempArray];
CollapseLayout *currentLayout = (CollapseLayout *)_collectionView.collectionViewLayout;
currentLayout.collapse = YES;
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.collectionView.numberOfSections)]];
} completion:nil];
}
It is using a Custom UICollectionViewFlowLayout that performs an animation when reloading the data.
My CustomFlowLayout Class is here
#import "CollapseLayout.h"
#implementation CollapseLayout
- (void)prepareLayout {
self.itemSize = CGSizeMake(200, 100);
self.minimumInteritemSpacing = 30;
self.scrollDirection = UICollectionViewScrollDirectionVertical;
}
- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray* allAttributesInRect = [super layoutAttributesForElementsInRect:rect];
if(_collapse)
{
for(UICollectionViewLayoutAttributes *attribute in allAttributesInRect)
{
attribute.frame = CGRectMake(attribute.frame.origin.x - self.collectionView.frame.size.width, attribute.frame.origin.y, attribute.frame.size.width, attribute.frame.size.height);
}
}
return allAttributesInRect;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes* allAttributesInRect = [super layoutAttributesForItemAtIndexPath:indexPath];
if(_collapse)
{
allAttributesInRect.frame = CGRectMake(allAttributesInRect.frame.origin.x - self.collectionView.frame.size.width, allAttributesInRect.frame.origin.y, allAttributesInRect.frame.size.width, allAttributesInRect.frame.size.height);
}
return allAttributesInRect;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
- (UICollectionViewLayoutAttributes*)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath*)itemIndexPath
{
UICollectionViewLayoutAttributes* layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:itemIndexPath];
return layoutAttributes;
}
- (void)invalidateLayout
{
}
The BOOL
_collapse
is set is called in my collection view to trigger the animation. I am hoping to have the old cells, move from left to right, outside the view, and the new cells animate in from the right side of the screen. Right now, the cells are just moving off screen.
My issue is that the code in layoutAttributesForItemAtIndexPath and layoutAttributesForElementsInRect are not animating correctly. Would adding the animation code in these methods be the best place to perform the animations, or im totally off?
Thanks
You shouldn't do anything to the result of
[super layoutAttributesForItemAtIndexPath:indexPath];
nor
[super layoutAttributesForElementsInRect:rect];
If you want your cells to animate from where they are to off the screen, then you need to modify only their "final" position by overriding finalAttributesForDisappearingElementAtIndexPath:, but not their "regular position".
If you want them to come back from the side of the screen then you need to modify their position through initialLayoutAttributesForAppearingItemAtIndexPath:

How to set the position of cells on UICollectionView

I have an UIViewController which contains a UICollectionView. But the UICollectionView does not fill all the UIViewController.
I find that there is space whose height equals the height of NavigationBar between the cell and the top edge of the UICollectionView. I don't know how I can set the cell position to (0,0) in the UICollectionView. (like this, the space is in the red rectangle)
I found this link How do I set the position of a UICollectionViewCell? And I subclass UICollectionViewFlowLayout (the following are my code)
MZMCollectionViewFlowLayout.h
#import <UIKit/UIKit.h>
#interface MZMCollectionViewFlowLayout : UICollectionViewFlowLayout
#end
MZMCollectionViewFlowLayout.m
#import "MZMCollectionViewFlowLayout.h"
#implementation MZMCollectionViewFlowLayout
- (CGSize)collectionViewContentSize
{
return [super collectionViewContentSize];
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
return [super layoutAttributesForElementsInRect:rect];
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"MZMCollectionViewFlowLayout layoutAttributesForItemAtIndexPath");
if (indexPath.section == 0 && indexPath.row == 1) // or whatever specific item you're trying to override
{
UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
layoutAttributes.frame = CGRectMake(0,0,100,100); // or whatever...
return layoutAttributes;
}
else
{
return [super layoutAttributesForItemAtIndexPath:indexPath];
}
}
#end
and using it like this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
// hide the tool bar
[self.navigationController setToolbarHidden:YES animated:YES];
// set title
self.navigationItem.title = #"User Album";
[self.userAlbumView setCollectionViewLayout:[[MZMCollectionViewFlowLayout alloc] init]];
}
But it does not work. The log NSLog(#"MZMCollectionViewFlowLayout layoutAttributesForItemAtIndexPath"); doesn't show. And the blank is still there.
Thanks for any help!
FYI, this is answered more correctly here: UICollectionView adds top margin
The problem is the view controller is automatically adjusting scroll view insets. That can be turned off either in code or IB. In code just set:
self.automaticallyAdjustsScrollViewInsets = NO;
Answer myself question.
I printed every UICollectionViewLayoutAttribute information in layoutAttributesForElementsInRect: and found what I need is to change the frame.orgin.y
following is my code:
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:[attributes count]];
for (int i=0; i< [attributes count]; i++) {
UICollectionViewLayoutAttributes *attr = (UICollectionViewLayoutAttributes *)[attributes objectAtIndex:i];
// the key code "attr.frame.origin.y - 63"
[attr setFrame:CGRectMake(attr.frame.origin.x, attr.frame.origin.y - 63, attr.bounds.size.width, attr.bounds.size.height)];
//NSLog(#"attr h=%f w=%f x=%f y=%f", attr.bounds.size.height, attr.bounds.size.width, attr.frame.origin.x, attr.frame.origin.y);
[result addObject:attr];
}
return result;
}
then it works fine.

Resources