Can't tap UICollectionView item - ios

I'm building an app whereas I've got a UICollectionView with a custom layout. I have a problem whereas I cannot tap some rows. Here's an overview over what happens
1) App starts and presents a UICollectionView
2) 3 test items are displayed, using the visual debugger in Xcode, you can see that there is something not right with the hierarchy
The red row can't be tapped now, but the yellow and green rows can.
3) User taps the yellow item, and segues to another page
4) User pops the shown UIViewController and returns to the UICollectionView whereas the viewWillAppear method reloads the UICollectionView like so:
[self.collectionView reloadData];
5) Re-entering the visual debugger shows that the hierarchy seems shifted, and the yellow row is now untappable, but the red and green rows can be tapped.
What could be the reason for this? I'll post any relevant code, ask for what parts you'd like to see.
Update
The UIViewController displaying the UICollectionView
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
ShareViewCell *view = [collectionView dequeueReusableCellWithReuseIdentifier:#"share" forIndexPath:indexPath];
view.share = [self.sharesController shareAtIndex:indexPath.item];
return view;
}
Custom cell:
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.leftBorderView = [UIView new];
[self.contentView addSubview:self.leftBorderView];
self.label = [UILabel new];
self.label.font = [UIFont boldSystemFontOfSize:10];
self.label.numberOfLines = 0;
self.label.lineBreakMode = NSLineBreakByWordWrapping;
[self.contentView addSubview:self.label];
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.leftBorderView.frame = CGRectMake(0, 0, 1.5, self.bounds.size.height);
CGSize labelSize = [self.label sizeThatFits:CGSizeMake(self.bounds.size.width - 10.0f, MAXFLOAT)];
NSTimeInterval durationOfShare = [self.share.endSpan timeIntervalSinceDate:self.share.startSpan] / 3600;
CGFloat middleOfCell = self.bounds.size.height / 2;
CGFloat yPositionToUse = middleOfCell - (labelSize.height / 2);
if (durationOfShare < 0.25)
{
[self.label setHidden:YES];// to small to be shown
} else if (durationOfShare > 0.5)
{
yPositionToUse = 8; // show it at the top
}
self.label.frame = CGRectMake(8, yPositionToUse, labelSize.width, labelSize.height);
}
Update 2
Is the UICollectionReusableView blocking tap of the UICollectionViewCell?

Ok,
First, you should be more precise in your question : you're using some code found on some question on StackOverflow.
The code seems to be this Calendar UI made with UICollectionView.
This is a sample code, quickly built for the sake of a StackOverflow answer (with a bounty). It's not finished, has no contributors, and you should try to read it and improve over it !
From your debugger's captures, it seems you have some view which overlays on top of your collectionView's cells.
From reading this code, I see it uses some supplementary view to present 'hour' blocks. This supplementary view class is HourReusableView. And it's the view coming on top on your debugger's captures
CalendarViewLayout is responsible to compute these supplementary views frame, as it does for colored event blocks (see method - (void)prepareLayout)
I might bet that these supplementary views's Z-order isn't predictable - all views have a default zIndex of 0. One way to fix it ? After line 67 of this file, set a negative zIndex on the UICollectionViewLayoutAttributes of the hour block. This way, you make sure hour supplementary views are always behind your cells, and don't intercept the cell's touch

Related

UISearchBar - internal UITextField height issue

I'm working on an app where I place a UISearchBar at the top of a UIViewController that contains a UITableViewController. The UISearchBar filters the contents of the UITableView.
I've left things alone so far (aside from customizing the colors to match my app's theme, which was hard enough!), but on anything except iPhone 4/5, the UISearchBar is dramatically too small.
Therefore, I'm trying to update the size of the font and the height of the internal UITextField.
All of this has proved remarkably difficult to accomplish, requiring quite a bit of customization. So, if you know of a library that makes this easier, please let me know in the comments.
Here's the code I'm using right now:
// In a category for UISearchBar
- (void)setup {
self.tintColor = [UIColor offWhite];
for (UIView *view in self.subviews) {
[self configureView:view];
}
}
- (void)configureView:(UIView *)view {
if ([view isKindOfClass:[UITextField class]]) {
CGFloat fontSize, frameHeight;
if (IS_IPHONE_4) {
fontSize = 14.0f;
frameHeight = 24.0f;
} else if (IS_IPHONE_5) {
fontSize = 14.0f;
frameHeight = 24.0f;
} else if (IS_IPHONE_6) {
fontSize = 17.0f;
frameHeight = 28.0f;
} else if (IS_IPHONE_6PLUS) {
fontSize = 20.0f;
frameHeight = 32.0f;
} else {
// iPad
fontSize = 24.0f;
frameHeight = 36.0f;
}
UITextField *textfield = (UITextField *)view;
textfield.font = [UIFont buttonFontOfSize:fontSize];
textfield.textColor = [UIColor offWhite];
textfield.tintColor = [UIColor offWhite];
CGRect frame = textfield.frame;
frame.origin.y = (self.frame.size.height - frameHeight) / 2.0f;
frame.size.height = frameHeight;
textfield.frame = frame;
}
if (view.subviews.count > 0) {
for (UIView *subview in view.subviews) {
[self configureView:subview];
}
}
}
Note: I structured my code this way in case Apple changes the internal structure of the UISearchBar. I didn't want to hard-code index values.
So, this code "works", in that the end result is what I desire, namely, a taller UISearchBar with text sized as specified and the internal UITextField also taller, as specified. What I don't understand is the process of getting there.
If I call [self.searchBar setup] in my general AutoLayout process, it doesn't work (the internal UITextField is the wrong height). This makes sense to me, since the frame is (0,0,0,0) until the view is actually laid out.
If I call [self.searchBar setup] in my -viewWillAppear: method, it doesn't work (the internal UITextField is the wrong height). This doesn't make sense to me, since debugging shows the frames to still be (0,0,0,0), but I thought -viewWillAppear: was called when everything was laid out and set up.
If I call [self.searchBar setup] in my -viewDidLayoutSubviews method, it "works", but the internal UITextField starts out the "normal" height and then "jumps" to the correct height some time after the view actually appears.
I set up the entire UIViewController in code, using pure AutoLayout. I simply cannot get the UISearchBar set up the way I want BEFORE the view finished loading and is displayed on screen. I've seen some funky stuff in the past, but I've always been able to force a view to render as desired. Is there something special behind the scenes with UISearchBar? Does anybody know how to get this done?
You're mixing auto-layout and manual layout (someView.frame = …) on the same view. You can't do that.
Instead, to change the height, set the constant on your view's height constraint to frameHeight. Let the auto-layout engine set the frame for you.

Changing Frame in Collection View's cellForItemAtIndexPath Method

I am new to IOS.
I have used collection view. I am using Prototype cell in it. I have not used Auto-Layout in my app.
I need to change frame of buttons and view in cellForItemAtIndexPath. But when I first scroll to move to next cell it not reflecting first time. I can only see blank with out any buttons or view. I have checked all the frames are correctly updating. Still I can not see any thing.
Though if I again go to previous call and come back it will seen properly.
Please find below code of cellForItemAtIndexPath
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
Feed *feed;
NSMutableAttributedString * string;
feed = [arrTop objectAtIndex:indexPath.row];
ShuffleCell *myCell = [collectionView
dequeueReusableCellWithReuseIdentifier:#"ShuffleCell"
forIndexPath:indexPath];
myCell.index = indexPath;
myCell.delegate = self;
// Below frame changes are not working
myCell.vwBack.frame = collect.frame;
myCell.imgShare.frame = CGRectMake(myCell.imgShare.frame.origin.x, myCell.imgShare.frame.origin.y, self.view.frame.size.width,self.view.frame.size.width );
myCell.lblText.center = CGPointMake(myCell.imgShare.frame.size.width / 2, myCell.imgShare.frame.size.height / 2);
myCell.lblText.frame = CGRectMake(20
, myCell.imgShare.frame.origin.y + 20, self.view.frame.size.width - 20,self.view.frame.size.width -20);
int totHeight = (collect.frame.size.height - self.view.frame.size.width);
myCell.vwBtn.frame = CGRectMake(myCell.vwBtn.frame.origin.x, collect.frame.size.height -totHeight, myCell.vwBtn.frame.size.width, totHeight);
[myCell.vwBtn setFrame:CGRectMake(myCell.vwBtn.frame.origin.x, collect.frame.size.height -totHeight, myCell.vwBtn.frame.size.width, totHeight)];
myCell.btnFavoriteBack.frame = CGRectMake(myCell.btnFavoriteBack.frame.origin.x, 0, myCell.btnFavoriteBack.frame.size.width, totHeight/3);
[myCell.btnFavoriteBack setFrame:CGRectMake(myCell.btnFavoriteBack.frame.origin.x, 0, myCell.btnFavoriteBack.frame.size.width, totHeight/3)];
myCell.btnShare.frame = CGRectMake(myCell.btnShare.frame.origin.x, (totHeight/3), myCell.btnShare.frame.size.width, totHeight/3);
myCell.btnReport.frame = CGRectMake(myCell.btnReport.frame.origin.x, (totHeight/3)*2, myCell.btnReport.frame.size.width, totHeight/3);
myCell.vwLikeBtn.frame = CGRectMake(self.view.frame.size.width - 82, (totHeight/3 - 32)/2, 82,32);
NSLog(#"Y :- %f",myCell.vwBtn.frame.size.height);
return myCell;
I have also attached screen shot of same.
Thanks for Help.
Adding this line should fix the problem. Let the subviews of cell translate their frames to constraints.
cell.label.translatesAutoresizingMaskIntoConstraints = YES;

Collapsing UICollectionView

I'm having a fight with UICollectionView to adopt it to my needs.
I'm trying to build a collapsing tag cloud. All elements can dynamically change size based on text inside.
When collection is displayed, I would like to display only first row and hide rest of content.
If there is more content than just for one row I want to show a button as a last item in first row - after selecting it, I will change collection size to fit it's content.
First step:
After click:
I was able to achieve desired effect with this code:
- (void) updateFrame
{
if(self.showFull)
{
self.showMoreButton.hidden = YES;
self.bottomConstrain.constant = 0;
self.frame = CGRectMake(0, 0, self.collectionView.frame.size.width, self.collectionView.contentSize.height);
}
else
{
self.showMoreButton.hidden = NO;
self.bottomConstrain.constant = self.originalConstrainValue;
self.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
}
}
But button in my case is placed completely outside the collection:
I'm hitting my head against a brick wall how to make this button to be a part of collection view.
It's the first time I'm playing with UICollectionView and UICollectionViewFlowLayout and it's too complex for me still to understand where would be best spot to put it.
One idea I had was to play a button over UICollectionView and position it at the end of first row - but I don't know how to get first row size from collection. With this approach it will be problematic also to cover last item in a row.
I think the best will be to put this element as a UICollectionViewCell, but I have no idea how to approach that - how to predict where it will placed and how to hide it later.
Any ideas will be highly appreciated.
Current template project is here:
https://www.zipshare.com/download/eyJhcmNoaXZlSWQiOiJjNTg2MGFkNC1mYWYxLTRlMzItOTA1YS1hMWFjOGFkMjMzYjUiLCJlbWFpbCI6Imdya3J1a293c2tpQGdtYWlsLmNvbSJ9
I'd suggest putting this button as a different cell INSIDE the collection view. Then, in collectionView:didSelectItemAtIndexPath: you would check to see if the special item was selected and react accordingly. I would let this button always be the last item in the collection view so you know where it is. When the items have expanded and everything is displayed, make sure you flip your logic and compress the collection view when this special cell is pressed.
You'll need to save some state to do this:
1.The array of tags
whether or not the collection view is expanded or not
Once you have this info, you can just flip the bits and reload the collection view section.
I've managed to achieve what I wanted with this code (thanks for atreat for suggestion).
I'm estimating which element won't fit in first row, and pushing "More" tag into tags array.
There is some logic also to make sure that More button will fit after last element in this row, if not it's pushed instead of this element. Maybe some will find it usefull.
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout*)self.collectionView.collectionViewLayout;
CGFloat collectionWidth = self.frame.size.width;
CGFloat lastFittingItemRight = 0.0f;
CGFloat spacingBetweenElements = flowLayout.minimumInteritemSpacing;
CGFloat sectionInsetLeft = flowLayout.sectionInset.left;
CGFloat sectionInsetRight = flowLayout.sectionInset.right;
CGFloat rightEdge = sectionInsetLeft;
for (int i = 0; i < [self collectionView:self.collectionView numberOfItemsInSection:0]; i++)
{
CGSize elementSize = [self collectionView:self.collectionView layout:flowLayout sizeForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
CGFloat elementWidth = elementSize.width;
rightEdge += elementWidth;
if(rightEdge > collectionWidth - sectionInsetRight)
{
self.moreButtonIndexPath = [NSIndexPath indexPathForItem:i inSection:0];
break;
}
lastFittingItemRight = rightEdge;
rightEdge += spacingBetweenElements;
}
if(self.moreButtonIndexPath)
{
NSString* showMoreText = #"More";
CGSize moreButtonSize = [self sizeForTagWithText:showMoreText];
CGFloat rightEdgeMoreButton = lastFittingItemRight + spacingBetweenElements + moreButtonSize.width;
NSInteger moreButtonIndex = self.moreButtonIndexPath.row;
if(rightEdgeMoreButton > collectionWidth - sectionInsetRight)
{
moreButtonIndex = moreButtonIndex-1;
}
[self.tags insertObject:showMoreText atIndex:moreButtonIndex];
}

Perfecting subview resizing in UICollectionView layout transition

I'm developing an app that has a UICollectionView - the collection view's job is to display data from a web service.
One feature of the app I am trying to implement is enabling the user to change the layout of this UICollectionView from a grid view to a table view.
I spent a lot of time trying to perfect this and I managed to get it to work. However there are some issues. The transition between the two layout doesn't look good and sometimes it breaks between switching views and my app is left with a view in an unexpected state. That only happens if the user switches between grid and table view very quickly (pressing the changeLayoutButton) continuously.
So, obviously there are some problems and I feel the code is a little fragile. I also need to fix the above mentioned issues.
I'll start off with how I implemented this view.
Implementation
Since I needed the two different cells (grideCell and tableViewCell) to show different things - I decided it would be better to subclass UICollectionViewFlowLayout since it does everything I need - all I need to do is change the cell sizes.
With that in mind I created two classes that subclassed UICollectionViewFlowLayout
This is how those two classes look:
BBTradeFeedTableViewLayout.m
#import "BBTradeFeedTableViewLayout.h"
#implementation BBTradeFeedTableViewLayout
-(id)init
{
self = [super init];
if (self){
self.itemSize = CGSizeMake(320, 80);
self.minimumLineSpacing = 0.1f;
}
return self;
}
#end
BBTradeFeedGridViewLayout.m
#import "BBTradeFeedGridViewLayout.h"
#implementation BBTradeFeedGridViewLayout
-(id)init
{
self = [super init];
if (self){
self.itemSize = CGSizeMake(159, 200);
self.minimumInteritemSpacing = 2;
self.minimumLineSpacing = 3;
}
return self;
}
#end
Very simple and as you can see - just changing the cell sizes.
Then in my viewControllerA class I implemented the UICollectionView like so:
Created the properties:
#property (strong, nonatomic) BBTradeFeedTableViewLayout *tableViewLayout;
#property (strong, nonatomic) BBTradeFeedGridViewLayout *grideLayout;
in viewDidLoad
/* Register the cells that need to be loaded for the layouts used */
[self.tradeFeedCollectionView registerNib:[UINib nibWithNibName:#"BBItemTableViewCell" bundle:nil] forCellWithReuseIdentifier:#"TableItemCell"];
[self.tradeFeedCollectionView registerNib:[UINib nibWithNibName:#"BBItemGridViewCell" bundle:nil] forCellWithReuseIdentifier:#"GridItemCell"];
The user taps a button to change between layouts:
-(void)changeViewLayoutButtonPressed
I use a BOOL to determine which layout is currently active and based on that I make the switch with this code:
[self.collectionView performBatchUpdates:^{
[self.collectionView.collectionViewLayout invalidateLayout];
[self.collectionView setCollectionViewLayout:self.grideLayout animated:YES];
} completion:^(BOOL finished) {
}];
In cellForItemAtIndexPath
I determine which cells I should use (grid or tableView) and the load the data - that code looks like this:
if (self.gridLayoutActive == NO){
self.switchToTableLayout = NO;
BBItemTableViewCell *tableItemCell = [collectionView dequeueReusableCellWithReuseIdentifier:tableCellIdentifier forIndexPath:indexPath];
if ([self.searchArray count] > 0){
self.switchToTableLayout = NO;
tableItemCell.gridView = NO;
tableItemCell.backgroundColor = [UIColor whiteColor];
tableItemCell.item = self.searchArray[indexPath.row];
}
return tableItemCell;
}else
{
BBItemTableViewCell *gridItemCell= [collectionView dequeueReusableCellWithReuseIdentifier:gridCellIdentifier forIndexPath:indexPath];
if ([self.searchArray count] > 0){
self.switchToTableLayout = YES;
gridItemCell.gridView = YES;
gridItemCell.backgroundColor = [UIColor whiteColor];
gridItemCell.item = self.searchArray[indexPath.row];
}
return gridItemCell;
}
Lastly in the two cell classes - I just use the data to set the image / text as I need.
Also in grid cell - the image is bigger and I remove text I don't want - which was the primary reason for uses two cells.
I'd be interested in how to make this view look a little more fluid and less buggy in the UI. The look I am going for is just like eBays iOS app - they switch between three different views. I just need to switch between two different views.
#jrturton's answer is helpful, however unless I'm missing something it is really overcomplicating something very simple. I'll start with the points we agree on...
Prevent interaction while changing layouts
First off, I agree with the approach of disabling user interaction at the start of the layout transition & reenabling at the end (in the completion block) using [[UIApplication sharedApplication] begin/endIgnoringInteractionEvents] - this is much better than trying cancel an in-progress transition animation & immediately begin the reverse transition from the current state.
Simplify the layout transition by using a single cell class
Also, I very much agree with the suggestion to use the same cell class for each layout. Register a single cell class in viewDidLoad, and simplify your collectionView:cellForItemAtIndexPath: method to just dequeue a cell and set its data:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
BBItemCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
if ([self.searchArray count] > 0) {
cell.backgroundColor = [UIColor whiteColor];
cell.item = self.searchArray[indexPath.row];
}
return cell;
}
(Notice that the cell itself shouldn't (in all but exceptional cases) need to be aware of anything to do with what layout is currently in use, whether layouts are transitioning, or what the current transition progress is)
Then when you call setCollectionViewLayout:animated:completion: the collection view doesn't need to reload any new cells, it just sets up an animation block to change each cell's layout attributes (you don't need to call this method from inside an performBatchUpdates: block, nor do you need to invalidate the layout manually).
Animating the cell subviews
However as pointed out, you will notice that subviews of the cell jump immediately to their new layout's frames. The solution is to simply force immediate layout of the cells subviews when the layout attributes are updated:
- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
[super applyLayoutAttributes:layoutAttributes];
[self layoutIfNeeded];
}
(No need to create special layout attributes just for the transition)
Why does this work? When the collection view changes layouts, applyLayoutAttributes: is called for each cell as the collection view is setting up the animation block for that transition. But the layout of the cell's subviews is not done immediately - it is deferred to a later run loop - resulting in the actual subview layout changes not being incorporated into the animation block, so the subviews jump to their final positions immediately. Calling layoutIfNeeded means that we are telling the cell that we want the subview layout to happen immediately, so the layout is done within the animation block, and the subviews' frames are animated along with the cell itself.
It is true that using the standard setCollectionViewLayout:... API does restrict control of the animation timing. If you want to apply a custom easing animation curve then solutions like TLLayoutTransitioning demonstrate a handy way of taking advantage of interactive UICollectionViewTransitionLayout objects to take control of the animation timing. However, as long as only a linear animation of subviews is required I think most people will be satisfied with the default animation, especially given the one-line simplicity of implementing it.
For the record, I'm not keen on the lack of control of this animation myself, so implemented something similar to TLLayoutTransitioning. If this applies to you too, then please ignore my harsh reproval of #jrturton's otherwise great answer, and look into TLLayoutTransitioning or UICollectionViewTransitionLayouts implemented with timers :)
Grid / table transitions aren't as easy as a trivial demo would have you believe. They work fine when you've got a single label in the middle of the cell and a solid background, but once you have any real content in there, it falls over. This is why:
You have no control over the timing and nature of the animation.
While the frames of the cells in the layout are animated from one value to the next, the cells themselves (particularly if you are using two separate cells) don't seem to perform internal layout for each step of the animation so it seems to "flick" from one layout to the next inside each cell - your grid cell looks wrong in table size, or vice versa.
There are many different solutions. It's hard to recommend anything specific without seeing your cell's contents, but I've had success with the following:
take control of the animation using techniques like those shown here. You could also check out Facebook Pop to get better control over the transition but I haven't looked into that in any detail.
use the same cell for both layouts. Within layoutSubviews, calculate a transition distance from one layout to the other and use this to fade out or in unused elements, and to calculate nice transitional frames for your other elements. This prevents a jarring switch from one cell class to the other.
That's the approach I used here to fairly good effect.
It's harder work that relying on resizing masks or Autolayout but it's the extra work that makes things look good.
As for the issue when the user can toggle between the layouts too quickly - just disable the button when the transition starts, and re- enable it when you're done.
As a more practical example, here's a sample of the layout change (some of it is omitted) from the app linked above. Note that interaction is disabled while the transition occurs, I am using the transition layout from the project linked above, and there is a completion handler:
-(void)toggleLayout:(UIButton*)sender
{
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
HMNNewsLayout newLayoutType = self.layoutType == HMNNewsLayoutTable ? HMNNewsLayoutGrid : HMNNewsLayoutTable;
UICollectionViewLayout *newLayout = [HMNNewsCollectionViewController collectionViewLayoutForType:newLayoutType];
HMNTransitionLayout *transitionLayout = (HMNTransitionLayout *)[self.collectionView transitionToCollectionViewLayout:newLayout duration:0.5 easing:QuarticEaseInOut completion:^(BOOL completed, BOOL finish)
{
[[NSUserDefaults standardUserDefaults] setInteger:newLayoutType forKey:HMNNewsLayoutTypeKey];
self.layoutType = newLayoutType;
sender.selected = !sender.selected;
for (HMNNewsCell *cell in self.collectionView.visibleCells)
{
cell.layoutType = newLayoutType;
}
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
}];
[transitionLayout setUpdateLayoutAttributes:^UICollectionViewLayoutAttributes *(UICollectionViewLayoutAttributes *layoutAttributes, UICollectionViewLayoutAttributes *fromAttributes, UICollectionViewLayoutAttributes *toAttributes, CGFloat progress)
{
HMNTransitionLayoutAttributes *attributes = (HMNTransitionLayoutAttributes *)layoutAttributes;
attributes.progress = progress;
attributes.destinationLayoutType = newLayoutType;
return attributes;
}];
}
Inside the cell, which is the same cell for either layout, I have an image view and a label container. The label container holds all the labels and lays them out internally using auto layout. There are constant frame variables for the image view and the label container in each layout.
The layout attributes from the transition layout are a custom subclass which include a transition progress property, set in the update layout attributes block above. This is passed into the cell using the applyLayoutAttributes method (some other code omitted):
-(void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
self.transitionProgress = 0;
if ([layoutAttributes isKindOfClass:[HMNTransitionLayoutAttributes class]])
{
HMNTransitionLayoutAttributes *attributes = (HMNTransitionLayoutAttributes *)layoutAttributes;
self.transitionProgress = attributes.progress;
}
[super applyLayoutAttributes:layoutAttributes];
}
layoutSubviews in the cell subclass does the hard work of interpolating between the two frames for the images and labels, if a transition is in progress:
-(void)layoutSubviews
{
[super layoutSubviews];
if (!self.transitionProgress)
{
switch (self.layoutType)
{
case HMNNewsLayoutTable:
self.imageView.frame = imageViewTableFrame;
self.labelContainer.frame = labelContainerTableFrame;
break;
case HMNNewsLayoutGrid:
self.imageView.frame = imageViewGridFrame;
self.labelContainer.frame = self.originalGridLabelFrame;
break;
}
}
else
{
CGRect fromImageFrame,toImageFrame,fromLabelFrame,toLabelFrame;
if (self.layoutType == HMNNewsLayoutTable)
{
fromImageFrame = imageViewTableFrame;
toImageFrame = imageViewGridFrame;
fromLabelFrame = labelContainerTableFrame;
toLabelFrame = self.originalGridLabelFrame;
}
else
{
fromImageFrame = imageViewGridFrame;
toImageFrame = imageViewTableFrame;
fromLabelFrame = self.originalGridLabelFrame;
toLabelFrame = labelContainerTableFrame;
}
CGFloat from = 1.0 - self.transitionProgress;
CGFloat to = self.transitionProgress;
self.imageView.frame = (CGRect)
{
.origin.x = from * fromImageFrame.origin.x + to * toImageFrame.origin.x,
.origin.y = from * fromImageFrame.origin.y + to * toImageFrame.origin.y,
.size.width = from * fromImageFrame.size.width + to * toImageFrame.size.width,
.size.height = from * fromImageFrame.size.height + to * toImageFrame.size.height
};
self.labelContainer.frame = (CGRect)
{
.origin.x = from * fromLabelFrame.origin.x + to * toLabelFrame.origin.x,
.origin.y = from * fromLabelFrame.origin.y + to * toLabelFrame.origin.y,
.size.width = from * fromLabelFrame.size.width + to * toLabelFrame.size.width,
.size.height = from * fromLabelFrame.size.height + to * toLabelFrame.size.height
};
}
self.headlineLabel.preferredMaxLayoutWidth = self.labelContainer.frame.size.width;
}
And that's about it. Basically you need a way of telling the cell how far through the transition it is, which you need the layout transitioning library (or, as I say, Facebook pop might do this) for, and then you need to make sure you get nice values for layout when transitioning between the two.

CollectionView layout doesn't refresh until scroll

The best way to demonstrate my problem is with the video below:
http://foffer.dk/collectionview.mp4
As you can see, my collectionView doesn't update it's layout to correctly display the items until I start scrolling. I'm using a separate collectionViewFlowLayout model. Here is my code:
CollectionViewFlowLayout.m:
#implementation CollectionViewFlowLayout
- (void)prepareLayout {
CGFloat halfWidth = self.collectionView.bounds.size.width / 2;
CGFloat halfHeight = self.collectionView.bounds.size.height / 2;
self.itemSize = CGSizeMake(halfWidth, halfHeight);
self.minimumInteritemSpacing = 0;
self.minimumLineSpacing = 0;
}
// indicate that we want to redraw as we scroll
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}
#end
my PhotosCollectionViewController (I've left out the standart collectionView stuff as I didn't think it is important to this issue, let me know if it is and I will post it):
-(instancetype)init {
UICollectionViewFlowLayout *layout = [[CollectionViewFlowLayout alloc] init];
return (self = [super initWithCollectionViewLayout:layout]);
}
-(void) viewDidLoad{
[super viewDidLoad];
[self.collectionView registerClass:[FOFPhotoCell class] forCellWithReuseIdentifier:#"photo"];
self.collectionView.backgroundColor = [UIColor whiteColor];
CGFloat bottomLayoutGuide = self.tabBarController.tabBar.frame.size.height;
self.collectionView.contentInset = UIEdgeInsetsMake(0, 0, bottomLayoutGuide, 0);
}
-(BOOL)shouldAutorotate{
return YES;
}
Do I need to call invalidateLayout somewhere? I can't seem to figure out where to implement that method. [self.collectionView invalidateLayout] doesn't work.
Could someone get me on the right track here?
Thanks in advance
Chris
So, it turns out I had it set up all wrong. As this article from apple says:
Before you start building custom layouts, consider whether doing so is
really necessary.
The UICollectionViewFlowLayout class provides a
significant amount of behavior that has already been optimized for
efficiency and that can be adapted in several ways to achieve many
different types of standard layouts. The only times to consider
implementing a custom layout are in the following situations:
The layout you want looks nothing like a grid or a line-based breaking
layout (a layout in which items are placed into a row until it’s full,
then continue on to the next line until all items are placed) or
necessitates scrolling in more than one direction.
You want to change all of the cell positions frequently enough that it would be more work
to modify the existing flow layout than to create a custom layout.
And my layout config is really simple. So I was overcomplicating it.

Resources