Ognjen
Ognjen

Reputation: 1195

autolayout with dynamics subviews

enter image description here

I am trying to make home screen on my ios 10 app like on photo from above. Green view is actually scroll view and i set constraints for it to cover whole View. Everything on scrollView i want to make scrollable. Yellow part is collection view with prototype cell. Number of items on this view is 6. Cell consists of photo and title. Table view is list of news (photo + title). When start app in table view I load 10 last news and rest of the news I getting with "load more" mechanism. I need proper work of app even on landscape orientation. I have problem to define this layout because collection view and tableView have dynamic height and space between them must be fixed. Usually on almost all tutorials people just fixed scrollView and GridView and in that case app looks good on portrait orientation but i need a more flexibility. Is it possible to achieve this through auto layout and constraints and if yes what are correct directions

UPDATE:

enter image description here

Content view enter image description here

Collection view

enter image description here

What I want to achieve is to make collection view as a two columns and 3 rows in portrait orientation and 3 columns and 2 rows on landscape. Currently I have collectionView with a scroll but I want to be expanded al the time because content of collectionView should consists of 6 highlighted news.

On viewDidLoad I tried to set table view on correct position (after collection view):

    override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    collection.dataSource = self
    collection.delegate = self

    tableView.delegate = self
    tableView.dataSource = self


   self.view.addConstraint(
        NSLayoutConstraint(
            item: tableView,
            attribute: .top,
            relatedBy: .equal,
            toItem: collection,
            attribute: .bottom,
            multiplier: 1.0,
            constant: 20
    ))



    tableView.frame = CGRect(x: 0,y: collection.collectionViewLayout.collectionViewContentSize.height,width: tableView.frame.width,height: tableView.frame.width ); // set new position exactly

    downloadArticles(offset: "0") {}
}

An example of what I want to achieve is:

enter image description here

Currently I have this:

enter image description here

Upvotes: 0

Views: 114

Answers (1)

Zhang
Zhang

Reputation: 11607

I think I got it working like this:

import UIKit

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UITableViewDataSource, UICollectionViewDelegateFlowLayout, UITableViewDelegate {
    @IBOutlet var collectionView: UICollectionView!
    @IBOutlet var tableView: UITableView!

    @IBOutlet var collectionViewHeightConstraint: NSLayoutConstraint!
    @IBOutlet var tableViewHeightConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "gridCell")

        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "listCell")
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        // shrink wrap the collectionView and tableView to fit their content height snuggly
        self.collectionViewHeightConstraint.constant = self.collectionView.contentSize.height
        self.tableViewHeightConstraint.constant = self.tableView.contentSize.height
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // MARK: - CollectionView Methods -
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 6;
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath)

        return cell
    }

    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        cell.backgroundColor = UIColor.lightGray
    }

    func calculateGridCellSize() -> CGSize {
        // -----------------------------------------------------
        // Calculate the size of the grid cells
        // -----------------------------------------------------
        let screenWidth = self.view.frame.size.width
        let screenHeight = self.view.frame.size.height

        var width:CGFloat = 0
        var height:CGFloat = 0

        if(UIDeviceOrientationIsPortrait(UIDevice.current.orientation)) {
            width = screenWidth / 2.0 - 0.5
            height = width
        }
        else {
            width = screenWidth / 3.0 - 1.0
            height = screenHeight / 2.0 - 0.5
        }

        let size:CGSize = CGSize(width: width, height: height)

        return size
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return self.calculateGridCellSize()
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {

        coordinator.animate(alongsideTransition: { (context) in
            print("New screen size = \(size.width) x \(size.height)")
            self.collectionView.collectionViewLayout.invalidateLayout()
            self.collectionViewHeightConstraint.constant = self.collectionView.contentSize.height
            self.tableViewHeightConstraint.constant = self.tableView.contentSize.height
            self.view.layoutIfNeeded()
        }) { (context) in
            self.collectionViewHeightConstraint.constant = self.collectionView.contentSize.height
            self.tableViewHeightConstraint.constant = self.tableView.contentSize.height
            self.view.layoutIfNeeded()
        }
    }

    // MARK: - TableView Methods -
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10;
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "listCell", for: indexPath)

        return cell
    }

    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        cell.textLabel?.text = "list cell \(indexPath.row)"
    }
}

For the interface layout, I did this:

  1. Add scrollView to main view
  2. Pin scrollview all four sides main view
  3. Add contentView to scrollView
  4. Pin contentView all four sides to scrollView
  5. Make contentView width equal to scrollView width
  6. Add collectionView to contentView
  7. Add tableView to contentView and vertically below collectionView
  8. Pin left, top, right of collectionView to contentView
  9. Pin left, bottom, right of tableView to contentView and top of tableView to bottom of collectionView
  10. Make collectionView height 667 and create IBOutlet NSLayoutConstraint for collectionView height (so we can update it later)
  11. Make tableView height 667 and create IBOutlet NSLayoutConstraint for tableView height (also to update later)
  12. Make collectionView min item spacing 1 and line spacing 1
  13. Disable scrollingEnabled for collectionView
  14. Disable scrollingEnabled for tableView
  15. Connect collectionView datasource and delegate to controller
  16. Connect tableView datasource and delegate to controller

Here's a screenshot of the layout if it's any help.

layout screenshot

Usually I build my UI using pure code and you would be able to copy and paste, hit the run button but since you're using using Storyboard, I showed it using Storyboard, hopefully you can follow my layout setup instructions.

Here's the result:

portrait screenshot 1 portrait screenshot 2 portrait screenshot 3 landscape screenshot 1 landscape screenshot 2 landscape screenshot 3

Is that what you wanted?

Upvotes: 1

Related Questions