5

I'm developing an app that show photos for users. My design was like Pinterest app. I added UICollectionView inside UIScrollView and disabled UICollectionView's scroll. But when I use this components like this UICollectionView not scrollable acording to its content. Only visible are showing and other are is not showing. The main pain in this project is CollectionViews' contents are dynamic. It could change with user interacyion. And my question is how to fully scroll UICollectionView with its dynamic content using with UIScrollView.

Let me share screenshots.

First image describes first open. Second image describes scrolling issue. Third image describes my storyboard.

Thanks for helpings.

Here is my storyBoard

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 think I couldn't clearly explain my issue. here I'll provide some additional videos to be clearly tell about the issue. I've upload 2 videos.

In this video my header is not scrolling with collectionview childs. So I want to scroll whole elements in screen. (https://youtu.be/0ArQe6mZytc)

In this short video you can see Pinterest App behaviour when scroll. I would like to make my app like this video (https://youtu.be/Nm-sjXVEL5s)

salih
  • 727
  • 13
  • 36
  • 2
    why adding collection view inside scroll view collection view is subclass of scroll view itself. – Tushar Sharma May 31 '17 at 11:09
  • 1
    My suggestion is not to include the UICollectionView inside UIScrollView because UICollectionView is already a Subclass of UIScrollView. Use CollectionView header to add your image and label at the top. – Imad Ali May 31 '17 at 11:10
  • @Imad But I have to use custom header for my functionality. When use custom layout header section gone :S – salih May 31 '17 at 11:12
  • @salih collection view have supplementary header view function . – Tushar Sharma May 31 '17 at 11:15
  • 2
    @salih take a look on this for implementing a header to your collection view: https://stackoverflow.com/questions/21731318/add-a-simple-uiview-as-header-of-uicollectionview – Kingalione May 31 '17 at 11:20
  • @TusharSarma My aim is cell should be custom layout using with header. Is this possible ? – salih May 31 '17 at 11:21
  • Yes you can use custom views in header. – Darshan Karekar May 31 '17 at 13:01
  • Use this [link](https://stackoverflow.com/questions/31693901/design-uitableviews-section-header-in-interface-builder) to add custom header. Hope it Helps. – Darshan Karekar May 31 '17 at 13:05
  • I want custom layout with header in collectionview – salih May 31 '17 at 13:11
  • https://stackoverflow.com/questions/50062974/how-to-make-scrollview-grow-based-on-collection-view-number-of-rows-in-ios/72300356#72300356 – Mohan May 19 '22 at 07:30

6 Answers6

4

For your requirement, there is no need to use UIScrollView. You can create the screen just using a UICollectionView.

To show a scrollable header, you can use UICollectionReusableView as the section header of the collection view.

1.UICollection view 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:

enter image description here

3. Output screen:

enter image description here

Edit:

In the code provided in : 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:

enter image description here

enter image description here

PGDev
  • 23,751
  • 6
  • 34
  • 88
  • Your approach worked correctly. But I want cell views be like pinterest as uploaded youtube https://www.youtube.com/watch?v=0ArQe6mZytc&feature=youtu.be – salih Jun 19 '17 at 06:36
  • Child views like pinterest means? What exactly do you want? – PGDev Jun 19 '17 at 06:37
  • I mean cells that collectionview have. The size of cells could be be changeable according to its content – salih Jun 19 '17 at 06:40
  • You need to make custom collection view layout for that. Refer to : https://www.raywenderlich.com/107439/uicollectionview-custom-layout-tutorial-pinterest. This link provides exactly what you want. – PGDev Jun 19 '17 at 07:21
  • I make this layout but custom layout not working with uicollectionreusableview – salih Jun 19 '17 at 07:26
  • I have edited the answer. I think this will satisfy your requirement now. – PGDev Jun 19 '17 at 10:57
  • @RamMadhavan I don't understand what you are trying to ask. – PGDev Aug 28 '17 at 13:07
2

Take NSLayoutConstraint for height of collection view and change that height constraint in viewdidLayoutSubviews() method like following. self.hgtCollectionView.constant = CGFloat(number of rows) * 210) + CGFloat(10)

This will update the height of collection view according to its content.

Dipal Patel
  • 327
  • 3
  • 8
1

I developed the same kind of app, you're trying to. I followed Ray Wenderlich tutorial for this.

Here is the link for my repo.

https://github.com/mvpVaibhav/Pixabay

Vaibhav Parmar
  • 461
  • 4
  • 14
  • Hello @Vaibhav your answer is good but we tried some approach it works too. we want header scrollable with pinterest layout too. I explain my situation with a image. https://i.stack.imgur.com/D8mOI.png – salih Jun 14 '17 at 07:34
  • It's easy to achieve. Just put UICollectionView and enable horizontal scrolling. No need to put that UICollectionView in UIScrollView – Vaibhav Parmar Jun 14 '17 at 08:42
  • I want only vertical scrolling. When I scroll header should have gone. – salih Jun 14 '17 at 08:52
  • Try to add collection view in header section view. – Vaibhav Parmar Jun 14 '17 at 09:38
  • I've tried to add header section view but It couldn't applicable with custom layout. – salih Jun 14 '17 at 10:31
0

It looks like your solution just breaks main CollectionView purpose - to save app memory and reuse resources of device. But here you are triyng to make CollectionView height bigger than device size which is bad. What if you would have 1000 images? your app just will crash with out of memory issue. Use custom cells and headers with collectionviewflowlayout subclass. Have a look at this sample code for example. https://github.com/zwaldowski/AdvancedCollectionView

I've watched Pinterest app but did not find any headers there. What kind of custom header do you need? What behaviour should it have? Do you need sticky header for several sections or should it go away with cells?

have a look at this tutorial https://www.objc.io/issues/3-views/collection-view-layouts/

What deployment target of your app? if iOS 9, then UICollectionViewFlowLayout sectionHeadersPinToVisibleBounds property if you need it.

Nikolay Shubenkov
  • 3,133
  • 1
  • 29
  • 31
0

Dont use image view out side collection view, you can simply use the UICollectionReusableView for your header

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")
  }
}

Chek this image of storyboard i am showing to you Your stroyboard should look like this

Your header class should look like this

class customHeaderView: UICollectionReusableView {
  @IBOutlet weak var imageView: UIImageView!
  //you can add more functions here you want
}
Er. Khatri
  • 1,384
  • 11
  • 29
0

For your information UICollectionView is already a subclass of UIScrollView. There is no need to add it again to UIScrollView.

Following is the hierarchy :

UICollectionView->UIScrollView->UIView->UIResponder->NSObjec‌​t

Ram Madhavan
  • 2,362
  • 1
  • 17
  • 20