Taka
Taka

Reputation: 55

Swift 5 Collection View Layout of Cell and all the items inside the cell

I'm new to IOS development and I'm just trying to get my feet wet. For the past couple of days I've been trying to wrap my head around how to embed horizontal and vertical scrolling inside swift for views. One thing that people seemed to mentioned and use when it came to this was Collection Views. I've been trying to learn how to properly use them and build the picture you can see below in Swift.

Goal or what I'm trying to build in Swift

Under the label Browse Categories there is a container that allows horizontal scrolling,the categories container. Below the label discover there is another container that should allow for vertical scrolling, the feed container(Similar to instagram, Airbnb , Pinterest etc)

2. What I've tried

From countless hours of googling and stackoverflowing I found out that Collection Views might be suitable for this task so I digged in. The apple documentation lacked some examples so I searched for tutorials to help me out. At the end of the day I was able to achieve the horizontal scrolling for the categories. This looked like this. Horizontal Scroll for categories

So far so good! The trouble actually starts as soon as I try adding the next collection scroll view(the vertical for the feed). After adding the feed collection the screen looks like this.

Feed Collection View

So what exactly is my problem? Well my problem is that although I set the constraints within the storyboard for the UIIMage view to fill the entire Content View I am presented with something else. I want to know how I can perfectly control the size and shape of the UIImageView and any other element. For simplicity I omitted things such as the star, username etc. But when I tried implementing this the project included everything.

I can't wrap my head around how to control the size and position of the contents of the CollectionViewCell.

I was assuming that positioning and size was taken care of by the constraints I set inside the storyboard for each element but that does not seem to be the case and now I'm lost.

I saw some post where people mentioned UICollectionViewLayoutAttributes but I just don't know how ti incorporate this in to my project.

I'm open to suggestions and I also have some more closing questions.

  1. Am I going in the right direction?
  2. Is there a more simple way to do this? Seems to be a lot of code for what the web takes maybe 15 lines of code.

Thx in advance

Github: https://github.com/Taharcca/LayoutChallenge.git

Code Snippets

        import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var categoriesCollectionView: UICollectionView!
    @IBOutlet weak var feedCollectionView: UICollectionView!

    let categories = Category.load() // Load data
    let feeds = Feed.load()
    override func viewDidLoad() {
        super.viewDidLoad()
        categoriesCollectionView.dataSource = self
        categoriesCollectionView.delegate = self
        feedCollectionView.dataSource = self
        feedCollectionView.delegate = self
        // Do any additional setup after loading the view.
    }
}

extension ViewController: UICollectionViewDelegate {
    // Not sure when to use this
}

// Diese Erweiterung legt die Datenquelle einer Collection View fest
extension ViewController: UICollectionViewDataSource {
    // Wie viele Reihen?
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    // Wie viele Objekete soll es geben?
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if collectionView == categoriesCollectionView {
            return categories.count
        }
        else {
            return feeds.count
        }
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if collectionView == categoriesCollectionView {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoryCollectionViewCell", for: indexPath) as! CategoryCollectionViewCell
            let category = categories[indexPath.item]
            cell.category = category
            return cell
        }
        else {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FeedCollectionViewCell", for: indexPath) as! FeedCollectionViewCell
            let feed = feeds[indexPath.item]
            cell.feed = feed
            return cell
        }
    }
}

// Größe der Zellen...Sehr intuitiv..... Danke Apple
extension ViewController: UICollectionViewDelegateFlowLayout {
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout:
            UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            if collectionView == categoriesCollectionView {
                return CGSize(width: 150, height: 75) // Collection View size right?
            }
            else {
                return CGSize(width: 374, height: 494)
            }

}

}

    import UIKit

class FeedCollectionViewCell: UICollectionViewCell {
    @IBOutlet weak var backgroundImage:UIImageView!

    var feed:Feed! {
        didSet {
            self.update()
        }
    }
    func update() {
        if let feed = feed {
            backgroundImage.image = feed.image
        }
    }
}

Upvotes: 2

Views: 11924

Answers (2)

Taka
Taka

Reputation: 55

Well after some more Stackoverflowing and watching other Tutorials I finally found the real solution to my problem.

The answer is fairly easy: constraints.

When you create everything from the storyboard you have to make sure that you set the right constraints. This included constraints for trailing, top, etc. And more importantly also width and height.

Lastly when working with images you also have to pay close attetntion to the content mode.

Depending on the content mode you have chosen you might get funny results.

In the end Apple's Collection View works as expected when using the storyboard. The items layout is set using the storyboard.

If you want you can change the layout of the cell item either by extending the following protocol: UICollectionViewDelegateFlowLayout. You could also just set a property that has a reference to UICollectionFlowLayout

and then probably take it from there.

In the end using Collection View for my use case with the feed for one item is probably overkill and you might want to use TableView as suggested by others in this post.

Upvotes: 0

Dharmesh Kheni
Dharmesh Kheni

Reputation: 71854

You can achieve it easily with UITableView by adding UICollectionView in tableHeaderView.

I have written it like below:

import UIKit

class DemoWithTableViewController: UIViewController {

    //MARK:- @IBOutlet
    @IBOutlet weak var categoriesCollectionView: UICollectionView!
    @IBOutlet weak var tblFeed: UITableView!

    //MARK:- Properties
    let categories = Category.load()
    let feeds = Feed.load()

    override func viewDidLoad() {
        super.viewDidLoad()

        setupUI()
    }
}

//MARK:- Setup UI
extension DemoWithTableViewController {

    func setupUI() {

        categoriesCollectionView.dataSource = self
        tblFeed.dataSource = self
        tblFeed.delegate = self
    }
}

//MARK:- UICollectionViewDataSource
extension DemoWithTableViewController: UICollectionViewDataSource {

    // Wie viele Objekete soll es geben?
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return categories.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoryCollectionViewCell", for: indexPath) as! CategoryCollectionViewCell
        let category = categories[indexPath.item]
        cell.category = category
        return cell
    }
}

//MARK:- UITableViewDataSource
extension DemoWithTableViewController: UITableViewDataSource, UITableViewDelegate {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return feeds.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tblFeed.dequeueReusableCell(withIdentifier: "FeedTableViewCell") as! FeedTableViewCell
        let feed = feeds[indexPath.item]
        cell.feed = feed
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 360
    }
}

And your view controller in storyboard will look like:

enter image description here

And your result will be with above code:

enter image description here

And for more info you can check demo project here.

Upvotes: 1

Related Questions