Creating a swiping card stack with UICollectionView and UICollectionViewCells? - ios

So far, I made my collection views which scroll horizontally in either left or right.I added UICollectionViewCells into one UICollectionView. My problem that I'm having is trying to find the right settings to make the cards stack on top of the first card, as demonstrated in the photo below.
Here are the settings for my collectionView and how it displays its cells.
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .Horizontal
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 78
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.clearColor()
cv.showsHorizontalScrollIndicator = false
cv.translatesAutoresizingMaskIntoConstraints = false
return cv
}()

You're going to have to subclass UICollectionViewLayout, set the UICollectionViewLayoutAttributes for each cell, and make sure to set zIndex to the indexPath.row of the item in order to get overlapping. You can take a look at my sample project on GitHub that implements something very similar.
There are some bugs in UICollectionViewLayout related to animating insertions of new cells when the cells are overlapping (which is why I made the sample project in the first place).

Related

Have a fixed width of collectionView cell and dynamic height [duplicate]

This question already has answers here:
AutoSizing cells: cell width equal to the CollectionView
(2 answers)
Closed 3 years ago.
I'm working on a rebuild of UI on mobile App. I've changed my tableView by CollectionView (for have two columns of cells)
I've achieve the work to have dynamic heigh on cell and that work perfectly, as you can see (iOS left and Android right) I'm trying to achieve the android UI with iOS.
Currently that what I have follow to achieve that :
https://www.vadimbulavin.com/collection-view-cells-self-sizing/
I've trying to set fixed width but that's automatically shape to the content.
I think that probably a little thing but I havent work from long time on iOS, so I'm looking for explain to achieve the fixed width size
The controller contain only that :
#IBOutlet weak var collectionViewFlowLayout: UICollectionViewFlowLayout!{
didSet {
self.collectionViewFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
}
}
I havent do the 'max width' part of tutorial.
All label inside cell have left right constraint fixed, 0 lines and word wrap line break
Thank !
Have a nice day
Benjamin
Well, as i see from screenshot, you already have auto height on each cell.
Set fixed width for CollectionViewCell:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var height:CGFloat = 0.0
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
height = layout.itemSize.height
}
let screenSize = UIScreen.main
let wi = screenSize.bounds.size.width/2 // can calculate gap between two columns
return CGSize(width: wi, height: height)
}
You need to do a custom layout of class UICollectionViewLayout and set it to your collection view. There are a lot of Pinterest like collection view tutorials. You can check this

How to use ScrollView with CollectionView

I need to have a view controller that has a scroll in view.
The problem is that I need that both scrolls (of scrollView and collectionView) work together.
But when I add ScrollView in all frame, nothing works...
That's an image that shows what I want:
And when I scroll viewController, all content scroll together..
Someone know a good way to implement this in swift 4?
Thanks in advance.
I've seen this done many ways. The most popular of which is to have these views embedded in a UITableView.
You can set the first cell height to a proportional amount to the superview, while keeping another cell that will contain your CollectionView. You would be able to take advantage of the UITableView's scrollView as well as it's memory efficiency and delegate.
A better way to do this is to use the scroll direction in UICollectionViewDelagateFlowLayout. Just add the UICollectionViewDelegateFlowLayout protocol or extends your ViewController to it. Then use .scrollDirection in the section of your UICollectionView.
And sample declaration like this
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
...
layout.scrollDirection = .vertical //or .horizontal depends on your liking
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
...//
return cv
}()
then add to your view view.addSubview(collectionView)
More info in the apple doc or jump to definition

UICollectionViewCell dynamic width and fixed height

I need to make UICollectionView cells in oval shape where height is fixed but width is dynamic and it has a limit also, if text longer than that, then text should scroll. Any third party option available for this or need to create own using UICollectionView. Please guide.
Below is the image what i am trying to achieve. I want to know before starting should i look for third parties or use UICollectionView to make own. I have short time to complete that's why to avoid time on searching asking in starting itself which direction to follow.Please guide.
You can use a UICollectionViewFlowLayout and Auto Layout to achieve this.
Create a UICollectionViewCell with a container view.
Pin this container view the edges of the cell with auto layout
Add a UILabel to this container view and pin it to all edges of the container view (give it a background color to distinguish from the cell background)
In the UICollectionViewCell subclass you'll want to round the corners of the container view, e.g. self.containerView.layer.cornerRadius = self.containerView.height / 2
In the UICollectionViewFlowLayoutDelegate method, estimatedSizeForItem return an approximate size for the cell (auto layout will calculate the actual size.)
The important thing to remember is your cell needs to have enough constraints so that the auto layout engine can calculate the actual height and width based on the content.
Edit: If you want a fixed height, ensure your label can only have a single line. Or add a height constraint.
Finally, i found a library TagListView that can be installed through cocoapods with lots of customisation and swift 4 support also.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let nw = intersts[indexPath.row]
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
let size = CGSize(width: 250, height: 1500)
let estimatedFrame = NSString(string: nw).boundingRect(with: size, options: options, attributes: [NSAttributedStringKey.font : UIFont.systemFont(ofSize: 17)], context: nil)
let attributes = [NSAttributedStringKey.font : UIFont.systemFont(ofSize: 17)]
let yourLabelSize: CGSize = nw.size(withAttributes:attributes )
var width1 = yourLabelSize.width + 30
if width1 < 30 {
width1 = 30
}
return CGSize(width: estimatedFrame.width+20, height: estimatedFrame.height+20)
}
I just extend or implement #Tim answer. So after you build the cell as described in his answer, then specify the cell width and height to be flexible using the sample code below i.e similar to what he described
let collectionViewLayout = UICollectionViewFlowLayout()
collectionViewLayout.scrollDirection = .horizontal
let itemWidth = Constants.myCollectionViewItemWidth
let itemHeight = Constants.myCollectionViewItemHeight
collectionViewLayout.estimatedItemSize = CGSize(width: itemWidth, height: itemHeight)
myCollectionView.collectionViewLayout = collectionViewLayout

Grid layout with CollectionView in Swift

I would like to achieve this result:
Searching around I found out that probably the way to do it is using UICollectionView, so no problem with that since there are many tutorials and questions on Stack Overflow. I have 3 questions:
I cannot find anything about the "separators" (the line that divides all the boxes). I like that it doesn't touch the screen borders horizontally. Is it done programmatically?
To divide the space equally in all devices (3 boxes/buttons horizontally) I found this answer answer. Is this the right approach?
For the Blur effect I found this answer: How to implement UIVisualEffectView in UITableView with adaptive segues
For a TableView it would be:
if (!UIAccessibilityIsReduceTransparencyEnabled()) {
tableView.backgroundColor = UIColor.clearColor()
let blurEffect = UIBlurEffect(style: .Light)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
tableView.backgroundView = blurEffectView
}
Can I do something like this?
#IBOutlet var collectionView: UICollectionView!
if (!UIAccessibilityIsReduceTransparencyEnabled()) {
collectionView.backgroundColor = UIColor.clearColor()
let blurEffect = UIBlurEffect(style: .Light)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
collectionView.backgroundView = blurEffectView
}
Create the UICollectionViewController like this in a file that sub-classes from UICollectionViewController:
convenience override init() {
var layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.itemSize = CGSizeMake(<width>, <height>)
// Setting the space between cells
layout.minimumInteritemSpacing = <Space between columns>
layout.minimumLineSpacing = <Space between rows>
return (self.init(collectionViewLayout: layout))
}
In the viewDidLoad you an set the background color like this:
self.collectionView.backgroundColor = UIColor.orangeColor()
My guess is you can set a background image like this:
self.collectionView?.backgroundColor = UIColor(patternImage: UIImage(named: "image.png")!)
The blur effect that you found looks good. I am having trouble figuring out how it would work though. Probably set it using the backgroundView property.
I'll update if I find the answer.
Update:
Here is an idea of something that might work for blurring the cells.
Create a cocoa-touch class that sub-classes from UICollectionViewCell, then add this code to it:
convenience override init(frame: CGRect) {
self.init(frame: frame)
var blurEffect: UIVisualEffect
blurEffect = UIBlurEffect(style: .Light)
var visualEffectView: UIVisualEffectView
visualEffectView = UIVisualEffectView(effect: blurEffect)
visualEffectView.frame = self.maskView!.bounds
self.addSubview(visualEffectView)
}
override func layoutSubviews() {
super.layoutSubviews()
self.maskView!.frame = self.contentView.bounds
}
Then in the CollectionViewController file, in the viewDidLoad, change this line of code:
self.collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
Change UICollectionViewCell.self to <Name of CollectionViewCell file>.self
Result:
1) First of all, I think you need to change how you look at that layout. There are no separators. Just UICollectionView Cells with spacing between cells, lowered opacity and some blur.
This settings will give you something close to image you posted, you can edit it for your needs later:
On storyboard go to your UICollectionView's size inspector.
Min Spacing-> For Cells = 2, For Lines = 2.
Section Insets-> Left = 7, Right = 7.
2) I'm using this on my app to divide space equally for 3 cells. Changed it for your settings. Just copy/paste and you are good to go.
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let screenSize: CGRect = UIScreen.mainScreen().bounds
let screenWidth = screenSize.width
return CGSize(width: (screenWidth/3)-6, height: (screenWidth/3)-6);
}
}
And as the last step put two images on top of CollectionView, to the left and right of the view and make widths equal to 7 and heights equal to UICollectionView. These images should have same opacity/background with cells. This will make it look like the image you want.
I hope my answer works for you. Good luck.
The first thing I would like to say is, your all above result can be achieved from UICollectionViewFlowLayout, Which is the default layout for UICollectionView.
UICollectionViewDelegateFlowLayout has all of the methods that can fulfill your requirements.
The flowLayout has minimumLineSpacingForSectionAtIndex and minimumInteritemSpacingForSectionAtIndexfor giving the spacing between the cells(both horizontally and vertically).
Its not a good way of giving cell frame in cellForItemAtIndexPath (like you submit the answer link). For that flowLayout provides a delegate for sizing cell sizeForItemAtIndexPath.
About the third question, yes you can use UIVisualEffectView for bluring purpose but compatible for only after iOS 8 and has issue with iPad2 I guess. But for your problem I would blur each cell rather than collectionView itself(since cell spacing is not blur).
I cannot find anything about the "separators" (the line that divides all the boxes). I like that it doesn't touch the screen borders horizontally. Is it done programmatically?
Yes, it looks like it is rendered on to a layer. You should read the Quartz 2D Programming Guide to get a handle on drawing and working with layers.
To divide the space equally in all devices (3 boxes/buttons horizontally) I found this answer answer. Is this the right approach?
This would be an option, but would not give you the separators look you like from your screen shot.
I would have my cell view's backgroundColor is set to clearColor, and then set the UICollectionView's backgroundView property to a view containing your separators and the blur effect. Make sure the UICollectionView's backgroundColor property is set to clearColor.
About the third question, yes you can use UIVisualEffectView for bluring purpose but compatible for only after iOS 8 and has issue with iPad2 I guess. But for your problem I would blur each cell rather than collectionView itself(since cell spacing is not blur).
If you use the backgroundView property of the UICollectionView to handle your separators and blur then your cells would only need to have their backgroundColor set to clearColor.
You should note that there is more than one way to do this, each way will have it's own drawbacks choose what works for you best.
I have created this meethod for custom layout. You can use by modifying according to your request.
func setCollectionLayout() {
let layout:UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top:0,left:0,bottom:0,right:0)
layout.itemSize = CGSize(width: UIScreen.main.bounds.size.width/2 - 1, height: 136)
layout.minimumInteritemSpacing = 1
layout.minimumLineSpacing = 1
collectionView.collectionViewLayout = layout
}

self-sizing collection view cells using swift not working

I'm trying to create a collection view with cells that can autosize. previously i had used sizeForItemAtIndexPath but could not get the cell height exactly right for a textView with attributedString. I've decided to abandon that approach and use the auto-sizing feature. I have found some information here but mostly with objective-C. I am only familiar with Swift. Even so, I have picked through it and it is still not working.
What I have done so far is to include the following code in my viewDidLoad:
if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.estimatedItemSize = CGSize(
width: self.view.frame.size.width, height: 100)
}
the cell width is showing correctly however all the cell heights are stuck at 100 regardless of the content.
Is there something else I need to do to allow the autosizing to kick in? I'm pretty sure my storyboard constraints are set up correctly.
In addition to setting the estimatedItemSize, you should also make sure that you are not providing an item size via a delegate or datasource method. The point of the self-sizing mechanism is that UIKit will use the estimated item size as its initial estimate, and then calculate the exact height based on the Auto Layout constraints you've configured on the cell's contentView. Also, you need a Base SDK of iOS 8 or later.
This repo reproduces Apple's example from the WWDC session where they introduced self-sizing cells.
One workaround might be to size the collectionView cells height and width in proportion to the screen size:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let deviceSize = UIScreen.mainScreen().bounds.size
//let cellSize = sqrt(Double(deviceSize.width * deviceSize.height) / (Double(33)))
let cellWidth = ((deviceSize.width / 2) - 10)
let cellHeight = (deviceSize.height / 4)
return CGSize(width: cellWidth , height: cellHeight)
}

Resources