UICollectionView height not dynamically increasing using UIScrollView inside
I am developing an application that displays photos to users. My design was similar to the Pinterest app. I added UICollectionView
inside UIScrollView
and disabled scrolling UICollectionView
. But when I use components like this UICollectionView not scrollable on content. Only visible are displayed, others are not displayed. The main problem in this project is the content of the CollectionViews is dynamic. This can change with user enthusiasm. And my question is how to completely scroll the UICollectionView with its dynamic content using the UIScrollView.
Let me share screenshots.
The first image describes the first open. The second image describes the scrolling problem. The third image describes my storyboard.
Thanks for the help.
Here is my story
And my code
//
// ViewController.swift
// ScrollCollectionView
//
// Created by Cbs on 31.05.2017.
// Copyright ยฉ 2017 Cbs. All rights reserved.
//
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
@IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 25
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.addShadow(opacitiy: 1, shadowRadius: 3, shadowOffsetWidth: 2, shadowOffsetHeight: 2, shadowColor: UIColor.lightGray, backgroundColor: UIColor.red)
cell.addCornerRadius(cornerRadius: 8)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cellSpacing = CGFloat(2) //Define the space between each cell
let leftRightMargin = CGFloat(40) //If defined in Interface Builder for "Section Insets"
let numColumns = CGFloat(2) //The total number of columns you want
let totalCellSpace = cellSpacing * (numColumns - 1)
let screenWidth = UIScreen.main.bounds.width
let width = (screenWidth - leftRightMargin - totalCellSpace) / numColumns
let height = CGFloat(210) //whatever height you want
return CGSize(width: width, height: height) // width & height are the same to make a square cell
}
}
EDIT:
I guess I couldn't clearly explain my problem. here I will provide some additional videos to clearly explain the problem. I have uploaded 2 videos.
In this video, my title is not scrolling with the children of the collection. So I want to scroll through whole items on the screen. ( https://youtu.be/0ArQe6mZytc )
In this short video you can see the scrolling behavior of the Pinterest app. I would like to make my application like this video ( https://youtu.be/Nm-sjXVEL5s )
source to share
There is no need to use for your requirement UIScrollView
. You can create a screen with UICollectionView
.
To show a scrollable header
, you can use UICollectionReusableView
both section header
for collection view
.
1.UICollection display code:
import UIKit
class PinterestViewController: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
}
}
extension PinterestViewController : UICollectionViewDataSource, UICollectionViewDelegate
{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 25
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView
{
if kind == UICollectionElementKindSectionHeader
{
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "headerView", for: indexPath)
return headerView
}
return UICollectionReusableView()
}
}
extension PinterestViewController : UICollectionViewDelegateFlowLayout
{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
let cellSpacing = CGFloat(2) //Define the space between each cell
let leftRightMargin = CGFloat(40) //If defined in Interface Builder for "Section Insets"
let numColumns = CGFloat(2) //The total number of columns you want
let totalCellSpace = cellSpacing * (numColumns - 1)
let screenWidth = UIScreen.main.bounds.width
let width = (screenWidth - leftRightMargin - totalCellSpace) / numColumns
let height = CGFloat(210) //whatever height you want
return CGSize(width: width, height: height) // width & height are the same to make a square cell
}
}
2. Interface:
3. Output screen:
Edit:
In the code provided at: https://www.raywenderlich.com/107439/uicollectionview-custom-layout-tutorial-pinterest
Make some changes:
class PinterestLayout: UICollectionViewFlowLayout
{
//...
override func prepare()
{
// 1
if cache.isEmpty
{
//...
var yOffset = [CGFloat](repeating: 150, count: numberOfColumns) //Height of your header view
//...
}
}
override var collectionViewContentSize: CGSize
{
return CGSize(width: contentWidth, height: contentHeight)
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
{
guard let attributes = super.layoutAttributesForElements(in: rect) else
{
return nil
}
var layoutAttributes = [UICollectionViewLayoutAttributes]()
for attr in attributes where attr.representedElementKind == UICollectionElementKindSectionHeader
{
if let supplementaryAttributes = layoutAttributesForSupplementaryView(ofKind: UICollectionElementKindSectionHeader, at: attr.indexPath)
{
layoutAttributes.append(supplementaryAttributes)
}
}
for attributes in cache
{
if attributes.frame.intersects(rect)
{
layoutAttributes.append(attributes)
}
}
return layoutAttributes
}
override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
{
if elementKind == UICollectionElementKindSectionHeader
{
let attributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, with: indexPath)
attributes.frame = CGRect(x: 0, y: 0, width: self.contentWidth, height: 150) //Height of your header view
return attributes
}
return nil
}
}
Also in Collection View Interface
:
source to share
Take NSLayoutConstraint for collection view height and change height constraint in viewdidLayoutSubviews () method as shown below.
self.hgtCollectionView.constant = CGFloat(number of rows) * 210) + CGFloat(10)
This will update the height of the collection view to match its contents.
source to share
I have developed the same application that you are trying to do. I followed Ray Wenderlich for this.
Here is the link for my repo.
source to share
It looks like your solution just breaks the main purpose of CollectionView - to save application memory and reuse device resources. But here you are triyng to increase the height of the CollectionView than the size of the device, which is bad. What if you have 1000 images? your application will just crash due to a memory issue. Use custom cells and headers with a subclass of collectionviewflowlayout. Have a look at this sample code for example. https://github.com/zwaldowski/AdvancedCollectionView
I looked at the Pinterest app but couldn't find any titles. What custom header do you need? What behavior should this have? Do you need a sticky header for multiple sections, or should it go away with cells?
take a look at this tutorial https://www.objc.io/issues/3-views/collection-view-layouts/
What is the purpose of deploying your application? if iOS 9 then UICollectionViewFlowLayout sectionHeadersPinToVisibleBounds if you need it.
source to share
Don't use side view to view images, you can just use UICollectionReusableView for your title
Add this to your class
override func collectionView(_ collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
//1
switch kind {
//2
case UICollectionElementKindSectionHeader:
//3
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "customHeaderView", for: indexPath) as! customHeaderView
headerView.imageView.image = //some random image local or dynamic you can use here..
return headerView
default:
//4
assert(false, "Unexpected element kind")
}
}
The check is the storyboard image I show you
The header class should look like this:
class customHeaderView: UICollectionReusableView {
@IBOutlet weak var imageView: UIImageView!
//you can add more functions here you want
}
source to share