UITableView overlap cells (force) - ios

I want to accomplish a table view cell overlap since I want to accomplish this effect:
So basically cells would kind of stack one on another. I'm targeting iOS7+ (currently testing with iOS8).
I'm currently doing a CAGradientLayer which gets positioned in layoutSubviews. I'm positioning it with:
CGRect gradientLayerFrame = self.contentView.bounds;
gradientLayerFrame.origin.y = -6.0f;
gradientLayerFrame.size.height += 6.0f;
self.gradientLayer.frame = gradientLayerFrame;
And I also did the
self.clipsToBounds = NO;
self.contentView.clipsToBounds = NO;
self.contentView.superview.clipsToBounds = NO;
in init so that nothing would clip. Now the cell clips the first time it gets rendered but when I scroll the table view down and then up again so that it displays the cells again they don't get clipped.
I already tried setting backgroundColor to clear in willDisplayCell with no luck. Also tried recursively traversing all of the cells subviews and set clipping to NO but the same thing all over again. Also tried hacking and forcefully setting setNeedsDisplay so that things would re-render but no luck.
Any help on this issue would be much appreciated.

It shouldn't be a TableView.
Try this library: TGLStackedViewController

I faced the same issue some time back.
The solution that worked for me is using a Collection view instead of a TableView. Create a custom class by extending it from UICollectionViewFlowLayout and use that in your Collection View instead.
class OverlappedCustomFlowLayout: UICollectionViewFlowLayout {
override func prepare() {
super.prepare()
// This allows us to make intersection and overlapping
// A negative number implies overlapping whereas positive implies space between the adjacent edges of two cells.
minimumLineSpacing = -100
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let layoutAttributes = super.layoutAttributesForElements(in: rect)
for currentLayoutAttributes: UICollectionViewLayoutAttributes in layoutAttributes! {
// zIndex - Specifies the item’s position on the z-axis.
// Unlike a layer's zPosition, changing zIndex allows us to change not only layer position,
// but tapping/UI interaction logic too as it moves the whole item.
currentLayoutAttributes.zIndex = currentLayoutAttributes.indexPath.row + 1
}
}
return layoutAttributes
}
P.S. - As per Apple's developer document of zIndex
This property is used to determine the front-to-back ordering of items
during layout. Items with higher index values appear on top of items
with lower values. Items with the same value have an undetermined
order. The default value of this property is 0.
Hope it helps! :)

Related

How do I center cells in table view?

I want to center my cells horizontally in my table view. Currently, they stick to the left. How do I go about doing this?
I've looked up various questions on SO and webpages, but they're all in Objective C, and I use Swift.
You shouldn't try to center the cells of your table view. You should allow Cocoa to manage the size of your cells and you should center your content within the content view of the cell.
There are 2 things you need to do:
First, you need to make sure you set the table style to Default: UITableViewCellStyleDefault. All other styles use a detailTextLabel in one way or another and you won't be able to set the textLabel's alignment property.
UITableViewCell(style: .Default, reuseIdentifier: CellIdentifier)
Then you can set the alignment of your cell's textLabel:
cell.textLabel.textAlignment = .Center
There are two approaches as listed below
1- you have to create cell w.r.t tableview width but drag your cell content in centre from nib.
2- You have to decrease tableview size so your cell fit with tableview.
if don't subclass the tableview cell, then subclass the UITableViewCell
and just override below method,
in CustomCell add below method
override func layoutSubviews()
{
super.layoutSubviews()
self.frame = CGRectMake(self.frame.origin.x + 100, self.frame.origin.y, 100 , 100);
//option 2
// self.bounds = CGRectMake( 100,0, 100 , 100);//calculate centre
}
in above u must use current frame react and calculate the centre according to your requirement, as u see i use the current frame of the cell if u ignore this, the x and y position set to zero the cell is always at the (0,0,width,height) of the tableview
and also also u can also use bounds it worked i comment it as option 2

Shadows Persist in UITableView after reloadSections:

Due to our designer being a sadist, I have a UITableView with a segmented control that switches between two different types of cells in separate feeds. The cells are dequeued with different identifiers and classes — this all works fine. The cells share a parent but are different sizes and for optimization reasons I set the layer.shadowpath manually in layoutSubviews() of the parent. I need the shadows: designer's wishes.
The issue is that after I switch to the second segment, some of the way down the table there are shadows dangling from what I believe are the cells above. As you can see from the first image, there are two shadows, and if I scroll down to occlude the top-most visible cell the shadow disappears, which leads me to the believe that the shadows are offset. Further scrolling makes these shadows disappear and not reappear again until switching tabs again. The rest of the shadows are fine.
two shadows
scroll down slightly
When switching back to the previous tab, where the cells are taller, there are also shadow issues, but those shadows are too short. As noted, the code that sets the shadow path is in the parent class, and the parent class is responsible for making and laying-out the top-most "card" view that contains the custom subCells.
I do everything programmatically: setting up the views and the Autolayout. Currently cell heights are hard-coded.
I'm not sure what information is relevant as I am completely at a loss, so here is how I set the shadowPath.
override func layoutSubviews() {
super.layoutSubviews()
cardView.layer.shadowPath = UIBezierPath(rect: cardView.bounds).CGPath
}
For simplicity the card is layout out in the contentView with the following visual format:
"V:|-marginV-[card]-marginV-|"
"H:|-marginH-[card]-marginH-|"
For whatever reason, even though I was using separate classes and separate reuseIdentifiers, the first reused cells just out of the view port were still sized as the tall cells in the other segment. When I changed
let cell = tableView.dequeueReusableCellWithIdentifier(booksReuseIdentifier) as! ProfileBookCell
to include the indexPath as
let cell = tableView.dequeueReusableCellWithIdentifier(booksReuseIdentifier, forIndexPath: indexPath) as! ProfileBookCell the recycling issue was remedied and bounds were properly computed. I tried layoutIfNeeded in a dozen different places to no effect, but this fixed it.
I had the same exact problem and I tried the current marked solution but that, nor anything else seemed to work. After trying so many other things I finally tried moving my add shadow code inside the layoutSubviews function of my subclassed UITableViewCell and it finally worked! I think this worked because the cell's final size isn't always calculated properly until the layouSubviews call and it needs the proper size to draw the shadow.
override func layoutSubviews() {
super.layoutSubviews()
addShadow(cell: self)
}
private func addShadow(cell:UITableViewCell) {
cell.layer.shadowOffset = CGSize(width:1, height:1)
cell.layer.shadowColor = UIColor.black.cgColor
cell.layer.shadowRadius = 1
cell.layer.shadowOpacity = 0.6
cell.clipsToBounds = false
let shadowFrame: CGRect = (cell.layer.bounds)
let shadowPath: CGPath = UIBezierPath(rect: shadowFrame).cgPath
cell.layer.shadowPath = shadowPath
}

custom tableView's header view overlap the table view cells

I designed a custom view as my UITableView's header view. just like this
(I just put image link here instead of image since I don't have 10 reputations.)
http://i.stack.imgur.com/KhNbE.png
Then in my UITableViewController I use this view as tableHeaderView
override func viewDidLoad() {
tableView.tableHeaderView = headerView!
//...other things
}
I got text from a JSON to fulfill the ContentLabel. If the text is long, the headerView will overlap cells just like below image.(short text is OK)
http://i.stack.imgur.com/gtO2g.png
Section is visible but two lines of cell have been overlapped by the headerView.I'm not sure if I did wrong constraints or code on ContentLabel. Below is the code I configured the contentLabel in TopicHeaderView.swift
var content: String? {
didSet {
self.contentLabel.text = content!
self.setNeedsUpdateConstraints()
self.updateConstraintsIfNeeded()
self.setNeedsLayout()
self.layoutIfNeeded()
}
}
func setFrameHeight(height: CGFloat) {
var frame = self.frame
frame.size.height = height
self.frame = frame
}
override func layoutSubviews() {
super.layoutSubviews()
self.contentLabel.preferredMaxLayoutWidth = self.contentLabel.alignmentRectForFrame(contentLabel.frame).width
self.titleLabel.preferredMaxLayoutWidth = self.titleLabel.bounds.size.width
self.authorLabel.preferredMaxLayoutWidth = self.authorLabel.bounds.size.width
self.setFrameHeight(CGRectGetMaxY(contentLabel.frame) + 8)
}
I browsed similar questions in SO but seems I can't find a solution to fix my problem. Can anyone help on this?
EDITED:
I logged the origin CGPoint of my first tableView cell and headerView's height. It shows the right number which means the first cell is right next to the header view vertically. There is a 22 points gap because of the height of section of course.
headerheight:600.0
first cell's y: 622.0
So maybe it's the label problem that its height is too big to exceed the bounds of TableView headerView? I'm not sure.
EDITED:
Strange things happen. I logged the y value of headerView's bottom,contentLabel's bottom and first UITableViewCell's origin. Please see the image from the link in the question comment below(still need 10 reputation)
As you can see, from the value in console, the view sequence from top should be "contentLabel's bottom(value:224) - headerView's bottom bounds(value: 232) - first cell's origin(value:254)". But in simulator, the sequence is totally messed up.It turns "headerView's bottom bounds - first cell's origin - contentLabel's bottom"
I really appreciate if anyone can help on this.
Problem is, that UITableView does not automatically change positions of cells when its headerView's height changes. Thus you need to reload UITableView every time TopicHeaderView.content changes.
Select that header view, or imageView what you have there, and check Clip Subviews in Attributes Inspector tab.
This worked for me.

layoutAttributesForElementsInRect: method trouble, how do I make my items in collection view fade from center?

I have a UICollectionView with items. I want items in center be full color, and items closer to vertical borders of collection view to slowly fade.
I came up with my custom implementation of layoutAttributesForElementsInRect but it doesn't seem to work as intended.
First, it's getting rarely called. Once at startup and sometimes later, usually when I'm near collectionView end.
Second, I fail to get normalized distance, it goes from -2 to 6, and I just want 0 to 1.
What exactly am I failing here? I take the visible rect and calculate distance from cell center to its edges and then normalize it.
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *attribs = [super layoutAttributesForElementsInRect:rect];
CGRect visibleRect;
visibleRect.origin = self.collectionView.contentOffset;
visibleRect.size = self.collectionView.bounds.size;
for (UICollectionViewLayoutAttributes *attributes in attribs) {
if (CGRectIntersectsRect(attributes.frame, rect)) {
CGFloat distanceFromCenter = CGRectGetMidY(visibleRect) - attributes.center.y;
CGFloat normalizedDistance = distanceFromCenter / (visibleRect.size.height / 2);
attributes.alpha = 1 - normalizedDistance;
}
}
return attribs;
}
Update:
Here is what I want:
When the top line of dates (22-28) approaches the edge of collection view, I want them to slowly fade out. Same goes about the bottom row. As you can see dates 1 and 2 are already not visible since they were light grey color. Dates 1-31 are black because I highlight the month that occupies the majority of view now.
I do this via scroll view delegate method after each scroll now, but this is very ugly code.
I want to do the same in the layout attributes method.
I'm afraid I don't think you are going to have much luck with this. I think layoutAtrributesForElementsInRect is getting called so rarely because, as far as the collectionView is concerned, it's "content view" is not changing - only the contentOffset. Your scrollView delegate code should be able to recognise when the offset changes, so (presumably) you can force reload the relevant cells to give them the right alpha.
As regards your normalising code, I guess the Rect being passed to the method must be way bigger than the bounds of the collectionView - have you tried logging to establish exactly which Rect is being passed? Or maybe super is lazy and passes attributes for everything, whether they are in that Rect or not. Your CGRectIntersectsRect ought to catch that, but that too will potentially include cells whose edge falls within the Rect but whose centre falls outside. And to cope with the negative values, you should use fabs to get the absolute value of the distance from centre.

Specifying one Dimension of Cells in UICollectionView using Auto Layout

In iOS 8 the UICollectionViewFlowLayout supports automatically resizing cells based on their own content size. This resizes the cells in both width and height according to their content.
Is it possible to specify a fixed value for the width (or height) of all the cells and allow the other dimensions to resize?
For a simple example consider a multi-line label in a cell with constraints positioning it to the sides of the cell. The multi-line label could be resized different ways to accommodate the text. The cell should fill the width of the collection view and adjust it's height accordingly. Instead, the cells are sized haphazardly and it even causes a crash when the cell size is larger than the non-scrollable dimension of the collection view.
iOS 8 introduces the method systemLayoutSizeFittingSize: withHorizontalFittingPriority: verticalFittingPriority: For each cell in the collection view the layout calls this method on the cell, passing in the estimated size. What would make sense to me would be to override this method on the cell, pass in the size that is given and set the horizontal constraint to required and a low priority to the vertical constraint. This way the horizontal size is fixed to the value set in the layout and the vertical size can be flexible.
Something like this:
- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
UICollectionViewLayoutAttributes *attributes = [super preferredLayoutAttributesFittingAttributes:layoutAttributes];
attributes.size = [self systemLayoutSizeFittingSize:layoutAttributes.size withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel];
return attributes;
}
The sizes given back by this method, however, are completely strange. The documentation on this method is very unclear to me and mentions using the constants UILayoutFittingCompressedSize UILayoutFittingExpandedSize which just represent a zero size and a pretty large one.
Is the size parameter of this method really just a way to pass in two constants? Is there no way to achieve the behavior I expect of getting the appropriate height for a given size?
Alternate Solutions
1) Adding constraints that will be specify a specific width for the cell achieves the correct layout. This is a poor solution because that constraint should be set to the size of the cell's collection view which it has no safe reference to. The value for that constraint could be passed in when the cell is configured, but that also seems completely counterintuitive. This is also awkward because adding constraints directly to a cell or it's content view is causing many problems.
2) Use a table view. Table views work this way out of the box as cells have a fixed width, but this would not accommodate other situations like an iPad layout with fixed width cells in multiple columns.
It sounds like what you are asking for is a way to use UICollectionView to produce a layout like UITableView. If that's really what you want, the right way to do this is with a custom UICollectionViewLayout subclass (maybe something like SBTableLayout).
On the other hand, if you're really asking if there is a clean way to do this with the default UICollectionViewFlowLayout, then I believe there is no way. Even with iOS8's self-sizing cells, it is not straightforward. The fundamental problem, as you say, is that the flow layout's machinery provides no way to fix one dimension and let another respond. (In addition, even if you could, there would be additional complexity around needing two layout passes to size the multi-line labels. This might not fit with how self-sizing cells want to compute all sizing via one call to systemLayoutSizeFittingSize.)
However, if you still want to create a tableview-like layout with a flow layout, with cells that determine their own size, and respond naturally to the collection view's width, of course it is possible. There is still the messy way. I have done it with a "sizing cell", i.e., a non-displayed UICollectionViewCell that the controller keeps only for calculating cell sizes.
There are two parts to this approach. The first part is for the collection view delegate to calculate the correct cell size, by taking in the collection view's width and using the sizing cell to calculate the cell's height.
In your UICollectionViewDelegateFlowLayout, you implement a method like this:
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
// NOTE: here is where we say we want cells to use the width of the collection view
let requiredWidth = collectionView.bounds.size.width
// NOTE: here is where we ask our sizing cell to compute what height it needs
let targetSize = CGSize(width: requiredWidth, height: 0)
/// NOTE: populate the sizing cell's contents so it can compute accurately
self.sizingCell.label.text = items[indexPath.row]
let adequateSize = self.sizingCell.preferredLayoutSizeFittingSize(targetSize)
return adequateSize
}
This will cause the collection view to set the width of the cell based on the enclosing collection view, but then ask the sizing cell to calculate the height.
The second part is to get the sizing cell to use its own AL constraints to calculate the height. This can be harder than it should be, because of the way multi-line UILabel's effectively require a two-stage layout process. The work is done in the method preferredLayoutSizeFittingSize, which is like so:
/*
Computes the size the cell will need to be to fit within targetSize.
targetSize should be used to pass in a width.
the returned size will have the same width, and the height which is
calculated by Auto Layout so that the contents of the cell (i.e., text in the label)
can fit within that width.
*/
func preferredLayoutSizeFittingSize(targetSize:CGSize) -> CGSize {
// save original frame and preferredMaxLayoutWidth
let originalFrame = self.frame
let originalPreferredMaxLayoutWidth = self.label.preferredMaxLayoutWidth
// assert: targetSize.width has the required width of the cell
// step1: set the cell.frame to use that width
var frame = self.frame
frame.size = targetSize
self.frame = frame
// step2: layout the cell
self.setNeedsLayout()
self.layoutIfNeeded()
self.label.preferredMaxLayoutWidth = self.label.bounds.size.width
// assert: the label's bounds and preferredMaxLayoutWidth are set to the width required by the cell's width
// step3: compute how tall the cell needs to be
// this causes the cell to compute the height it needs, which it does by asking the
// label what height it needs to wrap within its current bounds (which we just set).
let computedSize = self.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
// assert: computedSize has the needed height for the cell
// Apple: "Only consider the height for cells, because the contentView isn't anchored correctly sometimes."
let newSize = CGSize(width:targetSize.width,height:computedSize.height)
// restore old frame and preferredMaxLayoutWidth
self.frame = originalFrame
self.label.preferredMaxLayoutWidth = originalPreferredMaxLayoutWidth
return newSize
}
(This code is adapted from the Apple sample code from the sample code of the WWDC2014 session on "Advanced Collection View".)
A couple points to notice. It's using layoutIfNeeded() to force layout of the entire cell, in order to compute and set the width of the label. But that's not enough. I believe you also need to set preferredMaxLayoutWidth so that the label will use that width with Auto Layout. And only then can you use systemLayoutSizeFittingSize in order to get the cell to compute its height while taking the label into account.
Do I like this approach? No!! It feels way too complex, and it does layout twice. But as long as performance doesn't become an issue, I'd rather perform layout twice at runtime than have to define it twice in code, which seems to be the only other alternative.
My hope is that eventually self-sizing cells will work differently and this will all get a lot simpler.
Example project showing it at work.
But why not just use self-sizing cells?
In theory, iOS8's new facilities for "self-sizing cells" should make this unnecessary. If you've defined a cell with Auto Layout (AL), then the collection view should be smart enough to let it size itself and lay itself out correctly. In practice, I haven't seen any examples that have gotten this to work with multi-line labels. I think this is partly because the self-sizing cell mechanism is still buggy.
But I'd bet it's mostly because of the usual trickiness of Auto Layout and labels, which is that UILabels require a basically two-step layout process. It's not clear to me how you can perform both steps with self-sizing cells.
And like I said, this is really a job for a different layout. It is part of flow layout's essence that it positions things that have a size, rather than fixes a width and lets them choose their height.
And what about preferredLayoutAttributesFittingAttributes: ?
The preferredLayoutAttributesFittingAttributes: method is a red herring, I think. That is only there to be used with the new self-sizing cell mechanism. So this isn't the answer as long as that mechanism is unreliable.
And what's up with systemlayoutSizeFittingSize:?
You're right the docs are confusing.
The docs on systemLayoutSizeFittingSize: and systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority: both suggest that you should only pass UILayoutFittingCompressedSize and UILayoutFittingExpandedSize as the targetSize. However, the method signature itself, the header comments, and the behavior of the functions indicate that they are responding to the exact value of the targetSize parameter.
In fact, if you set the UICollectionViewFlowLayoutDelegate.estimatedItemSize, in order to enable the new self-sizing cell mechanism, that value seems to get passed in as the targetSize. And UILabel.systemLayoutSizeFittingSize seems to return the exact same values as UILabel.sizeThatFits. This is suspicious, given that the argument to systemLayoutSizeFittingSize is supposed to be a rough target and the argument to sizeThatFits: is supposed to be a maximum circumscribing size.
More Resources
While it is sad to think that such a routine requirement should require "research resources", I think it does. Good examples and discussions are:
http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html
http://devetc.org/code/2014/07/07/auto-layout-and-views-that-wrap.html
code for WWDC2014 session 232, "Advanced User Interfaces with Collection Views"
There's a cleaner way to do this than some of the other answers here, and it works well. It should be performant (collection views load fast, no unnecessary auto layout passes etc), and doesn't have any 'magic numbers' like a fixed collection view width. Changing the collection view size, e.g. on rotation, and then invalidating the layout should work great too.
1. Create the following flow layout subclass
class HorizontallyFlushCollectionViewFlowLayout: UICollectionViewFlowLayout {
// Don't forget to use this class in your storyboard (or code, .xib etc)
override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
let attributes = super.layoutAttributesForItemAtIndexPath(indexPath)?.copy() as? UICollectionViewLayoutAttributes
guard let collectionView = collectionView else { return attributes }
attributes?.bounds.size.width = collectionView.bounds.width - sectionInset.left - sectionInset.right
return attributes
}
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let allAttributes = super.layoutAttributesForElementsInRect(rect)
return allAttributes?.flatMap { attributes in
switch attributes.representedElementCategory {
case .Cell: return layoutAttributesForItemAtIndexPath(attributes.indexPath)
default: return attributes
}
}
}
}
2. Register your collection view for automatic sizing
// The provided size should be a plausible estimate of the actual
// size. You can set your item size in your storyboard
// to a good estimate and use the code below. Otherwise,
// you can provide it manually too, e.g. CGSize(width: 100, height: 100)
flowLayout.estimatedItemSize = flowLayout.itemSize
3. Use the predefined width + custom height in your cell subclass
override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
layoutAttributes.bounds.size.height = systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
return layoutAttributes
}
A simple way to do it in iOS 9 in a few lines of codes - the horizontal way exemple (fixing its height to its Collection View height) :
Init your Collection View Flow Layout with an estimatedItemSize to enable self-sizing cell :
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.estimatedItemSize = CGSizeMake(1, 1);
Implement the Collection View Layout Delegate (in your View Controller most of the time), collectionView:layout:sizeForItemAtIndexPath: . The goal here is to set the fixed height (or width) to the Collection View dimension. The 10 value can be anything, but you should set it to a value that doesn't break constraints :
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(10, CGRectGetHeight(collectionView.bounds));
}
Override your custom cell preferredLayoutAttributesFittingAttributes: method, this part actually calculate your dynamic cell width based on your Auto Layout constraints and the height you have just set :
- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
UICollectionViewLayoutAttributes *attributes = [layoutAttributes copy];
float desiredWidth = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].width;
CGRect frame = attributes.frame;
frame.size.width = desiredWidth;
attributes.frame = frame;
return attributes;
}
Try fixing your width in the preferred layout attributes:
- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
UICollectionViewLayoutAttributes *attributes = [[super preferredLayoutAttributesFittingAttributes:layoutAttributes] copy];
CGSize newSize = [self systemLayoutSizeFittingSize:CGSizeMake(FIXED_WIDTH,layoutAttributes.size) withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel];
CGRect newFrame = attr.frame;
newFrame.size.height = size.height;
attr.frame = newFrame;
return attr;
}
Naturally you also want to ensure that you setup your layout correctly to:
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *) self.collectionView.collectionViewLayout;
flowLayout.estimatedItemSize = CGSizeMake(FIXED_WIDTH, estimatedHeight)];
Heres something I put on Github that uses constant width cells and supports dynamic type so the height of the cells updates as the system font size changes.
YES it can be done using auto layout programmatically and by setting constraints in storyboard or xib. You need to add constraint for width size to remain constant and set height greater than or equal to.
http://www.thinkandbuild.it/learn-to-love-auto-layout-programmatically/
http://www.cocoanetics.com/2013/08/variable-sized-items-in-uicollectionview/
Hope this will be helpful and solve your issue.

Resources