Octavio Rojas
Octavio Rojas

Reputation: 187

Create a dynamically sized scrollView with 3 dynamically sized tableViews within?

I'm creating a scrollable view that is the checkout section of a shopping cart.

This is the view layout

enter image description here

It's a scrollable view that has a UIView inside, whose content size should adjust itself depending on the size of each table view within, each table view inside the UIView should adjust it's height to it's content size which would vary depending on the number of baskets and products. (Please note that what I want is to adjust the size of the entire tableView not just one of it's cells).

I checked this answer Change UITableViewHeight Dynamically and it offers some suggestions on how to make my UITableViews auto-sizable, but I need both the UIView and the TableViews inside to be dynamically sizeable.

Any help is greatly appreciated.

Upvotes: 0

Views: 112

Answers (1)

Brandon
Brandon

Reputation: 23500

I don't understand why you don't want to combine them into one table..

Anyway.. UIScrollView doesn't work with Auto-Layout. You have to add a contentView with the same dimensions as the scrollView's parent OR with a fixed size.

Now that is out of the way, you constrain the tableViews to the scrollView's contentView, then override viewDidLayoutSubviews of the controller. In that function, you need to get the contentSize of each tableView and constrain the height of each one to its content size. This will make the table full size and not scrollable.

Since the scrollView auto sizes based on its contents, you don't have to do anything else.

Alternatively, if you don't want to use AutoLayoutScrollView, you can set the UIScrollView contentSize to the sum of the contentSize of the 3 tables in viewDidLayoutSubviews of the controller.

Note: I do not have any sample dynamic content for the tableViews but if you want them to use dynamic row sizes, you need to do:

table.estimatedRowHeight = 300  //Estimation of the average size of the rows.

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

Now for the actual code with hardcoded row heights (because I don't have dynamic test data):

//
//  ViewController.swift
//  SO
//
//  Created by Brandon T on 2017-01-17.
//  Copyright © 2017 XIO. All rights reserved.
//

import UIKit


class AutoLayoutScrollView : UIScrollView {
    private(set) weak var contentView: UIView!
    private var hConstraint: NSLayoutConstraint!
    private var vConstraint: NSLayoutConstraint!

    init() {
        super.init(frame: .zero)
        self.layout()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.layout()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.layout()
    }

    private func layout() {
        let view = UIView()
        self.contentView = view
        self.addSubview(self.contentView)

        self.contentView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
        self.contentView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
        self.contentView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
        self.contentView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
        self.contentView.translatesAutoresizingMaskIntoConstraints = false
    }

    override func didMoveToSuperview() {
        super.didMoveToSuperview()

        if let parent = self.superview {
            self.leftAnchor.constraint(equalTo: parent.leftAnchor).isActive = true
            self.rightAnchor.constraint(equalTo: parent.rightAnchor).isActive = true
            self.topAnchor.constraint(equalTo: parent.topAnchor).isActive = true
            self.bottomAnchor.constraint(equalTo: parent.bottomAnchor).isActive = true
            self.translatesAutoresizingMaskIntoConstraints = false

            self.hConstraint = self.contentView.widthAnchor.constraint(equalTo: parent.widthAnchor)
            self.vConstraint = self.contentView.heightAnchor.constraint(equalTo: parent.heightAnchor)
            self.hConstraint.isActive = true
            self.vConstraint.isActive = true
        }
    }

    func setHorizontalScrollEnabled(enabled: Bool) {
        self.hConstraint.isActive = !enabled
    }

    func setVerticalScrollEnabled(enabled: Bool) {
        self.vConstraint.isActive = !enabled
    }
}

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    private var scrollView: AutoLayoutScrollView!
    private var topTableView: UITableView!
    private var middleTableView: UITableView!
    private var bottomTableView: UITableView!

    private var topTableHeight: NSLayoutConstraint!
    private var middleTableHeight: NSLayoutConstraint!
    private var bottomTableHeight: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.layout()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func layout() {
        //Init Views
        self.scrollView = AutoLayoutScrollView()
        self.topTableView = UITableView(frame: .zero, style: .plain)
        self.middleTableView = UITableView(frame: .zero, style: .plain)
        self.bottomTableView = UITableView(frame: .zero, style: .plain)

        self.registerClasses()

        //Add Views
        self.view.addSubview(self.scrollView)
        self.scrollView.contentView.addSubview(self.topTableView)
        self.scrollView.contentView.addSubview(self.middleTableView)
        self.scrollView.contentView.addSubview(self.bottomTableView)

        //Layout Views
        self.topTableView.leftAnchor.constraint(equalTo: self.scrollView.contentView.leftAnchor).isActive = true
        self.topTableView.rightAnchor.constraint(equalTo: self.scrollView.contentView.rightAnchor).isActive = true
        self.topTableView.topAnchor.constraint(equalTo: self.scrollView.contentView.topAnchor).isActive = true
        self.topTableView.translatesAutoresizingMaskIntoConstraints = false

        self.middleTableView.leftAnchor.constraint(equalTo: self.scrollView.contentView.leftAnchor).isActive = true
        self.middleTableView.rightAnchor.constraint(equalTo: self.scrollView.contentView.rightAnchor).isActive = true
        self.middleTableView.topAnchor.constraint(equalTo: self.topTableView.bottomAnchor).isActive = true
        self.middleTableView.translatesAutoresizingMaskIntoConstraints = false

        self.bottomTableView.leftAnchor.constraint(equalTo: self.scrollView.contentView.leftAnchor).isActive = true
        self.bottomTableView.rightAnchor.constraint(equalTo: self.scrollView.contentView.rightAnchor).isActive = true
        self.bottomTableView.topAnchor.constraint(equalTo: self.middleTableView.bottomAnchor).isActive = true
        self.bottomTableView.bottomAnchor.constraint(equalTo: self.scrollView.contentView.bottomAnchor).isActive = true
        self.bottomTableView.translatesAutoresizingMaskIntoConstraints = false

        self.topTableHeight = self.topTableView.heightAnchor.constraint(equalToConstant: 0)
        self.middleTableHeight = self.middleTableView.heightAnchor.constraint(equalToConstant: 0)
        self.bottomTableHeight = self.bottomTableView.heightAnchor.constraint(equalToConstant: 0)


        //Set Views Properties
        self.scrollView.setVerticalScrollEnabled(enabled: true)

        self.topTableView.delegate = self
        self.topTableView.dataSource = self
        self.middleTableView.delegate = self
        self.middleTableView.dataSource = self
        self.bottomTableView.delegate = self
        self.bottomTableView.dataSource = self


        //Display Views
        self.topTableView.reloadData()
        self.middleTableView.reloadData()
        self.bottomTableView.reloadData()
    }

    func registerClasses() {
        self.topTableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellID")
        self.middleTableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellID")
        self.bottomTableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellID")
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        //Update tables constraints to full size.
        self.topTableHeight.constant = self.topTableView.contentSize.height
        self.middleTableHeight.constant = self.middleTableView.contentSize.height
        self.bottomTableHeight.constant = self.bottomTableView.contentSize.height

        self.topTableHeight.isActive = true
        self.middleTableHeight.isActive = true
        self.bottomTableHeight.isActive = true
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if (tableView == self.topTableView) {
            return 10
        }

        if (tableView == self.middleTableView) {
            return 15
        }

        if (tableView == self.bottomTableView) {
            return 3
        }

        return 0
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if (tableView == self.topTableView) {
            return 100
        }

        if (tableView == self.middleTableView) {
            return 250
        }

        if (tableView == self.bottomTableView) {
            return 500
        }

        return 0.0
    }

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

        let cell = tableView.dequeueReusableCell(withIdentifier: "cellID", for: indexPath)

        if (tableView == self.topTableView) {
            cell.contentView.backgroundColor = UIColor.red
        }

        if (tableView == self.middleTableView) {
            cell.contentView.backgroundColor = UIColor.green
        }

        if (tableView == self.bottomTableView) {
            cell.contentView.backgroundColor = UIColor.blue
        }

        return cell
    }
}

Upvotes: 1

Related Questions