{"id":13462702,"url":"https://github.com/malcommac/ScrollStackController","last_synced_at":"2025-03-25T05:32:11.134Z","repository":{"id":49440713,"uuid":"212644860","full_name":"malcommac/ScrollStackController","owner":"malcommac","description":"🧩 Easy scrollable layouts in UIKit - an UIStackView which scroll and uses root views of child view controllers.","archived":false,"fork":false,"pushed_at":"2024-11-13T14:43:08.000Z","size":5408,"stargazers_count":439,"open_issues_count":4,"forks_count":38,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-03-24T21:05:10.147Z","etag":null,"topics":["scrollableview","stackview","stackviewanimation","uistackview-scrollable","uiviewcontroller"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/malcommac.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-10-03T18:00:24.000Z","updated_at":"2025-02-18T14:03:49.000Z","dependencies_parsed_at":"2024-05-20T16:20:21.221Z","dependency_job_id":"5fca0c66-0299-4e33-a517-17cbe256d025","html_url":"https://github.com/malcommac/ScrollStackController","commit_stats":{"total_commits":116,"total_committers":10,"mean_commits":11.6,"dds":0.1293103448275862,"last_synced_commit":"be6fe363d0a67045336f65a6d53a6395460f71a1"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malcommac%2FScrollStackController","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malcommac%2FScrollStackController/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malcommac%2FScrollStackController/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malcommac%2FScrollStackController/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/malcommac","download_url":"https://codeload.github.com/malcommac/ScrollStackController/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245351961,"owners_count":20601090,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["scrollableview","stackview","stackviewanimation","uistackview-scrollable","uiviewcontroller"],"created_at":"2024-07-31T13:00:19.140Z","updated_at":"2025-03-25T05:32:11.125Z","avatar_url":"https://github.com/malcommac.png","language":"Swift","readme":"\u003cp align=\"center\"\u003e\n\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"./Resources/scrollstack-dark.png\" width=\"350\"\u003e\n  \u003cimg alt=\"logo-library\" src=\"./Resources/scrollstack-light.png\" width=\"350\"\u003e\n\u003c/picture\u003e\n\u003c/p\u003e\n\n[![Swift](https://img.shields.io/badge/Swift-5.3_5.4_5.5_5.6-orange?style=flat-square)](https://img.shields.io/badge/Swift-5.3_5.4_5.5_5.6-Orange?style=flat-square)\n[![Platform](https://img.shields.io/badge/Platforms-iOS-4E4E4E.svg?colorA=28a745)](#installation)\n[![Swift Package Manager](https://img.shields.io/badge/Swift_Package_Manager-compatible-orange?style=flat-square)](https://img.shields.io/badge/Swift_Package_Manager-compatible-orange?style=flat-square)\n[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/ScrollStackController.svg?style=flat-square)](https://img.shields.io/cocoapods/v/ScrollStackController.svg)\n\n\nCreate complex scrollable layout using UIViewControllers or plain UIViews and simplify your code!\n\nScrollStackController is a class you can use to create complex layouts using scrollable `UIStackView` but where each row is handled by a separate `UIViewController`; this allows you to keep a great separation of concerns.\n\nYou can think of it as `UITableView` but with several differences:\n\n- **Each row can manage different `UIViewController` independently**: no more massive controllers, a much cleaner and maintainable architecture.\n- **You can still use plain `UIView` instances if need a lightweight solution**: this is especially useful when you are using ScrollStackController as layout-helper or your view don't have a complex logic and you can still use the main controller.\n- **Powered by AutoLayout since the beginning**; it uses a combination of `UIScrollView + UIStackView` to offer an animation friendly controller ideal for fixed and dynamic row sizing.\n- **You don't need to struggle yourself with view recycling**: suppose you have a layout composed by several different screens. There is no need of view recycling but it cause a more difficult managment of the layout. With a simpler and safer APIs set `ScrollStackView` is the ideal way to implement such layouts.\n\n\n|  \t| Features Highlights \t|\n|---\t|---------------------------------------------------------------------------------\t|\n| 🕺 \t| Create complex layout without the boilerplate required by view recyling of `UICollectionView` or `UITableView`. \t|\n| 🧩 \t| Simplify your architecture by thinking each screen as a separate-indipendent `UIVIewController`. \t|\n| 🧩 \t| Support for lightweight mode to layout `UIView` without `UIViewController`. \t|\n| 🌈 \t| Animate show/hide and resize of rows easily even with custom animations! \t|\n| ⏱ \t| Compact code base, less than 1k LOC with no external dependencies. \t|\n| 🎯 \t| Easy to use and extensible APIs set. \t|\n| 🧬 \t| It uses standard UIKit components at its core. No magic, just a combination of `UIScrollView`+`UIStackView`. \t|\n| 🧨 \t| Support SwiftUI's View and autosizing based upon View's content \t|\n| 🐦 \t| Fully made in Swift 5 from Swift ❥ lovers \t|\n\n## ❤️ Your Support\n\n*Hi fellow developer!*  \nYou know, maintaing and developing tools consumes resources and time. While I enjoy making them **your support is foundamental to allow me continue its development**.  \n\nIf you are using `ScrollStackController` or any other of my creations please consider the following options:\n\n- [**Make a donation with PayPal**](https://www.paypal.com/paypalme/danielemargutti/20)\n- [**Become a Sponsor**](https://github.com/sponsors/malcommac)\n- [Follow Me](https://github.com/malcommac)\n\n\u003ca name=\"index\"/\u003e\n\n## Table of Contents\n\n- [❤️ Your Support](#️-your-support)\n- [Table of Contents](#table-of-contents)\n  - [When to use `ScrollStackController` and when not](#when-to-use-scrollstackcontroller-and-when-not)\n  - [How to use it](#how-to-use-it)\n  - [Adding Rows](#adding-rows)\n  - [Removing / Replacing Rows](#removing--replacing-rows)\n  - [Move Rows](#move-rows)\n  - [Hide / Show Rows](#hide--show-rows)\n  - [Hide / Show Rows with custom animations](#hide--show-rows-with-custom-animations)\n  - [Reload Rows](#reload-rows)\n  - [Sizing Rows](#sizing-rows)\n  - [Fixed Row Size](#fixed-row-size)\n  - [Fitting Layout Row Size](#fitting-layout-row-size)\n  - [Collapsible Rows](#collapsible-rows)\n  - [Working with dynamic UICollectionView/UITableView/UITextView](#working-with-dynamic-uicollectionviewuitableviewuitextview)\n  - [Rows Separator](#rows-separator)\n  - [Using plain UIViews instead of view controllers](#using-plain-uiviews-instead-of-view-controllers)\n  - [Tap On Rows](#tap-on-rows)\n  - [Get the row/controller](#get-the-rowcontroller)\n  - [Set Row Insets](#set-row-insets)\n  - [Change ScrollStack scrolling axis](#change-scrollstack-scrolling-axis)\n  - [Subscribe to Row Events](#subscribe-to-row-events)\n  - [System Requirements](#system-requirements)\n  - [Example App](#example-app)\n  - [Installation](#installation)\n- [Contributing](#contributing)\n- [Copyright \\\u0026 Acknowledgements](#copyright--acknowledgements)\n\n\u003ca name=\"whentousescrollstackcontrollerandwhennot\"/\u003e\n\n### When to use `ScrollStackController` and when not\n\n`ScrollStackController` is best used for shorter screens with an heterogeneous set of rows: in these cases you don't need to have view recycling.\n\nThanks to autolayout you will get updates and animations for free.\n\nYou can also manage each screen independently with a great separation of concerns; morehover unlike `UITableView` and `UICollectionView`, you can keep strong references to `UIViewController` (and its views) in an `ScrollStack` view and make changes to them at any point.\n\n`ScrollStackController` is not suitable in all situations. \n`ScrollStackController` lays out the entire UI at first time when your screen loads.\nIf you have a long list of rows you may experience delays.\n\nSo, `ScrollStackController` is generally not appropriate for screens that contain many views of the same type, all showing similar data (in these cases you should use `UITableView` or `UICollectionView`).\n\n![Demo Project](https://media.giphy.com/media/fAi3hyXNalzd4SkVgI/giphy.gif)\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"howtouseit\"/\u003e\n\n### How to use it\n\nThe main class of the package is `ScrollStack`, a subclass of `UIScrollView`. It manages the layout of each row, animations and keep a strong reference to your rows.\n\nThis is an overview of the architecture:\n\n![](./Resources/architecture.png)\n\n- `ScrollStackController `: is a subclass of `UIViewController`. You would to use it and add as a child controller of your view controller. This allows you to manage any child-controllers related events for each row you will add to the stack controller.\n- `ScrollStack`: the view of the `ScrollStackController ` is a `ScrollStack`, a subclass of `UIScrollView` with an `UIStackView` which allows you to manage the layout of the stack. You can access to it via `scrollStack` property of the controller.\n- Each row is a `ScrollStackRow`, which is a subclass of `UIView`. Inside there are two views, the `contentView` (a reference to managed `UIViewController`'s `view`) and the `separatorView`. A row strongly reference managed view controller, so you don't need to keep a strong reference by your own.\n- Separator view are subclass of `ScrollStackSeparator` class.\n\nAs we said, usually you don't want to intantiate a `ScrollStack` control directly but by using the `ScrollStackController` class.\nIt's a view controller which allows you to get the child view controller's managment for free, so when you add/remove a row to the stack you will get the standard UIViewController events for free!\n\nThis is an example of initialization in a view controller:\n\n```swift\nclass MyViewController: UIViewController {\n\n    private var stackController = ScrollStackViewController()\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        stackController.view.frame = contentView.bounds\n        contentView.addSubview(stackController.view)\n    }\n    \n}\n```\n\nNow you are ready to use the `ScrollStack` control inside the `stackController` class.\n`ScrollStack` have an extensible rich set of APIs to manage your layout: add, remove, move, hide or show your rows, including insets and separator management.\n\nEach row managed by `ScrollStack` is a subclass of `ScrollStackRow`: it strongly reference a parent `UIViewController` class where you content is placed. `UIViewController`'s `view` will be the `contentView` of the row itself.\n\nYou don't need to handle lifecycle of your rows/view controller until they are part of the rows inside the stack.\n\nTo get the list of rows of the stack you can use the `rows` property.\n\n```swift\n// Standard methods\nlet allRows = scrollStack.rows\nlet isEmpty = scrollStack.isEmpty // true if it does not contains row\nlet notHiddenRows = scrollStack.rows.filter { !$0.isHidden }\n\n// By Visibility\nlet currentlyVisibleRows = scrollStack.visibleRows // only currently visible rows (partially or enterly)\nlet enterlyVisibleRows = scrollStack.enterlyVisibleRows // only enterly visible rows into the stack\n\n// Shortcuts\nlet firstRow = scrollStack.firstRow\nlet lastRow = scrollStack.lastRow\n```\n\nLet's take a look below.\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"addingrows\"/\u003e\n\n### Adding Rows\n\n`ScrollStack` provides a comprehensive set of methods for managing rows, including inserting rows at the beginning and end, inserting rows above or below other rows.\n\nTo add row you can use one the following methods:\n\n- `addRow(controller:at:animated:) -\u003e ScrollStackRow?`\n- `addRows(controllers:at:animated:) -\u003e [ScrollStackRow]?`\n\nBoth of these methods takes as arguments:\n\n- `controller/s`: one or more `UIViewController` instances; each view of these controllers will be as a row of the stack inside a `ScrollStackRow` (a sort of cell).\n- `at`: specify the insertion point. It's an enum with the following options: `top` (at first index), `bottom` (append at the bottom of the list), `atIndex` (specific index), `after` or `below` (after/below a row which contain a specific `UIViewController`).\n- `animated`: if true insertion will be animated\n- `completion`: completion callback to call at the end of the operation.\n\nThe following code add a rows with the view of each view controller passed:\n\n```swift\n   let welcomeVC = WelcomeVC.create()\n   let tagsVC = TagsVC.create(delegate: self)\n   let galleryVC = GalleryVC.create()\n        \n   stackView.addRows(controllers: [welcomeVC, notesVC, tagsVC, galleryVC], animated: false)\n```\n\nAs you noticed there is not need to keep a strong reference to any view controller; they are automatically strong referenced by each row created to add them into the stack.\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"removingreplacingrows\"/\u003e\n\n### Removing / Replacing Rows\n\nA similar set of APIs are used to remove existing rows from the stack:\n\n- `removeAllRows(animated:)`: to remove all rows of the stack.\n- `removeRow(index:animated:) -\u003e UIViewController?`: to remove a specific row at given index. It returns a reference to removed view controller.\n- `removeRows(indexes:animated:) -\u003e [UIViewController]?`: to remove rows at specified indexes from the stack. Removed managed `UIViewController` instances are returned.\n- `replaceRow(index:withRow:animated:completion:)`: replace an existing row with a new row which manage new passed view controller.\n\nAn example:\n\n```swift\nlet newVC: UIViewController = ...\nstackView.replaceRow(index: 1, withRow: newVC, animated: true) {\n\tprint(\"Gallery controller is now in place!!\")\n}\n```\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"moverows\"/\u003e\n\n### Move Rows\n\nIf you need to adjust the hierarchy of the stack by moving a row from a position to another you can use:\n\n- `moveRow(index:to:animated:completion:)`: move a row at passed inside to another index (both of indexes must be valid).\n\nThe following method move the first row at a random position, by animating the transition:\n\n```swift\nlet randomDst = Int.random(in: 1..\u003cstackView.rows.count)\nstackView.moveRow(index: 0, to: randomDst, animated: true, completion: nil)\n```\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"hideshowrows\"/\u003e\n\n### Hide / Show Rows\n\n`ScrollStack` uses the power of `UIStackView`: you can show and hide rows easily with a gorgeous animation by using one of the following methods:\n\n- `setRowHidden(index:isHidden:animated:completion:)`: hide or show a row at index.\n- `setRowsHidden(indexes:isHidden:animated:completion:)`: hide or show multiple rows at specified indexes.\n\nExample:\n\n```swift\nstackView.setRowsHidden(indexes: [0,1,2], isHidden: true, animated: true)\n```\n\nKeep in mind: when you hide a rows the row still part of the stack and it's not removed, just hidden! If you get the list of rows by calling `rows` property of the `ScrollStack` you still see it.\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"customanimations\"/\u003e\n\n### Hide / Show Rows with custom animations\n\nYou can easily show or hide rows with any custom transition; your view controller just need to be conform to the `ScrollStackRowAnimatable` protocol.  \nThis protocol defines a set of animation infos (duration, delay, spring etc.) and two events you can override to perform actions:\n\n```swift\npublic protocol ScrollStackRowAnimatable {\n    /// Animation main info.\n    var animationInfo: ScrollStackAnimationInfo { get }\n    \n    /// Animation will start to hide or show the row.\n    func willBeginAnimationTransition(toHide: Bool)\n    \n    /// Animation to hide/show the row did end.\n    func didEndAnimationTransition(toHide: Bool)\n    \n    /// Animation transition.\n    func animateTransition(toHide: Bool)\n}\n```\n\nSo for example you can replicate the following animation:\n\n![](./Resources/custom_transition.gif)\n\nby using the following code:\n\n```swift\nextension WelcomeVC: ScrollStackRowAnimatable {\n    public var animationInfo: ScrollStackAnimationInfo {\n        return ScrollStackAnimationInfo(duration: 1, delay: 0, springDamping: 0.8)\n    }\n\n    public func animateTransition(toHide: Bool) {\n        switch toHide {\n            case true:\n                self.view.transform = CGAffineTransform(translationX: -100, y: 0)\n                self.view.alpha = 0\n            \n            case false:\n                self.view.transform = .identity\n                self.view.alpha = 1\n        }\n    }\n    \n    public func willBeginAnimationTransition(toHide: Bool) {\n        if toHide == false {\n            self.view.transform = CGAffineTransform(translationX: -100, y: 0)\n            self.view.alpha = 0\n        }\n    }\n    \n}\n```\n\n\u003ca name=\"reloadrows\"/\u003e\n\n### Reload Rows\n\nReload rows method allows you to refresh the layout of the entire stack (using `layoutIfNeeded()`) while you have a chance to update a specific row's `contentView` (aka the view of the managed `UIViewController`).\n\nThere are three methods:\n\n- `reloadRow(index:animated:completion:)`: reload a specific row at index.\n- `reloadRows(indexes:animated:completion:)`: reload a specific set of rows.\n- `reloadAllRows(animated:completion:)`: reload all rows.\n\nIf your `UIViewController` implements `ScrollStackContainableController` protocol you will get notified inside the class about this request, so you have the opportunity to refresh your data:\n\nExample:\n\n```swift\nclass MyViewController: UIViewController {\n\n\tprivate let scrollStackController = ScrollStackController()\n\t\n\t@IBAction func someAction() {\n\t\tscrollStackController.scrollStack.reloadRow(0)\n\t}\n\n}\n\n// Your row 0 manages the GalleryVC, so in your GalleryVC implementation:\n\nclass GalleryVC: UIViewController, ScrollStackContainableController {\n\n    public func func reloadContentFromStackView(stackView: ScrollStack, row: ScrollStackRow, animated: Bool) {\n\t\t// update your UI\n\t}\n\t\n}\n```\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"sizingrows\"/\u003e\n\n### Sizing Rows\n\nYou can control the size of your `UIViewController` inside a row of a `ScrollStack` in two ways:\n\n- Creating contrains in your `UIViewController`'s view with Autolayout.\n- Implementing `ScrollStackContainableController` protocol in your `UIViewController` class and return a non `nil` value in `scrollStackRowSizeForAxis(:row:in:) -\u003e ScrollStack.ControllerSize?` delegate method.\n\nIn both case `ScrollStack` class will use only one dimension depending by the active scroll axis to layout the view controller content into the stack (if scroll axis is `horizontal` you can control only the `height` of the row, if it's `vertical` only the `width`. The other dimension will be the same of the scroll stack itself.\n\nEach of the following cases is covered inside the demo application:\n\n- Fixed row size in [GalleryVC](https://github.com/malcommac/ScrollStackController/blob/master/ScrollStackControllerDemo/Child%20View%20Controllers/GalleryVC.swift)\n- Collapsible / Expandable row in [TagsVC](https://github.com/malcommac/ScrollStackController/blob/master/ScrollStackControllerDemo/Child%20View%20Controllers/TagsVC.swift)\n- Growing row based on `UITextView`'s content in [NotesVC](https://github.com/malcommac/ScrollStackController/blob/master/ScrollStackControllerDemo/Child%20View%20Controllers/NotesVC.swift)\n- Growing row based on `UITableView`'s content in [PricingVC](https://github.com/malcommac/ScrollStackController/blob/master/ScrollStackControllerDemo/Child%20View%20Controllers/PricingVC.swift)\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"fixedrowsize\"/\u003e\n\n### Fixed Row Size\n\nIf your view controller has a fixed size you can just return it as follows:\n\n```swift\n\nclass GalleryVC: UIViewController, ScrollStackContainableController {\n\n    public func scrollStackRowSizeForAxis(_ axis: NSLayoutConstraint.Axis, row: ScrollStackRow, in stackView: ScrollStack) -\u003e ScrollStack.ControllerSize? {\n    \tswitch axis {\n    \tcase .horizontal:\n       \t  return .fixed(300)\n      \tcase .vertical:\n       \t  return .fixed(500)\n       }\n    }\n    \n}\n\n```\n\nIf your stack support single axis you can obivously avoid switch condition.\nWhen you will add this view controller in a scroll stack it will be sized as you requested (any height/width constraint already in place will be removed).\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"fittinglayoutrowsize\"/\u003e\n\n### Fitting Layout Row Size\n\nSometimes you may want to have the content view sized by fitting the contents of the view controller's view. In these cases you can use `. fitLayoutForAxis`.\n\nExample:\n\n```swift\npublic func scrollStackRowSizeForAxis(_ axis: NSLayoutConstraint.Axis, row: ScrollStackRow, in stackView: ScrollStack) -\u003e ScrollStack.ControllerSize? {\n\treturn .fitLayoutForAxis\n}\n```\n\n`ScrollStack` will use the `systemLayoutSizeFitting()` method on your view controller's view to get the best size to fit the content.\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"collapsiblerows\"/\u003e\n\n### Collapsible Rows\n\nSometimes you may want to create collapsible rows.\nThese row can have different heights depending of a variable.\n\nIn this case you just need to implement a `isExpanded: Bool` variable in your view controller and return a different height based on it.\n\n```swift\n\npublic class TagsVC: UIViewController, ScrollStackContainableController {\n\n    public var isExpanded = false\n    \n    public func scrollStackRowSizeForAxis(_ axis: NSLayoutConstraint.Axis, row: ScrollStackRow, in stackView: ScrollStack) -\u003e ScrollStack.ControllerSize? {\n        return (isExpanded == false ? .fixed(170) : .fixed(170 + collectionView.contentSize.height + 20))\n    }\n}\n```\n\nIn your main view controller you may call this:\n\n```swift\n\t // get the first row which manages this controller\n\t let tagsRow = stackView.firstRowForControllerOfType(TagsVC.self)\n\t // or if you have already the instance you can get the row directly\n\t // let tagsRow = stackView.rowForController(tagsVCInstance)\n\t \n\t let tagsVCInstance = (tagsRow.controller as! TagsVC)\n\t tagsVCInstance.isExpanded = !tagsVCInstance.isExpanded\n\t \n\t stackView.reloadRow(tagsRow, animated: true)\n```\n\nAnd your rows will perform a great animation to resize its content.\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"workingwithdynamicuicollectionviewuitableviewuitextview\"/\u003e\n\n### Working with dynamic UICollectionView/UITableView/UITextView\n\nThere are some special cases where you may need to resize the row according to the changing content in your view controller's view.\n\nConsider for example an `UIViewController` with a `UITableView` inside; you may want to show the entire table content's as it grown.\nIn this case you need to make some further changes:\n\n- You need to return `.fitLayoutForAxis`.\n- In your view controller's view you need to create a reference to the height constraint of your table.\n- You need to create a constraint from the table to the bottom safe area of your view (this will be used by AL to grow the size of the view).\n\nThen you must override the `updateViewConstraints()` to change the value of the table's height constraint to the right value.\n\nThis is the code:\n\n```swift\n\npublic class PricingVC: UIViewController, ScrollStackContainableController {\n    \n    public weak var delegate: PricingVCProtocol?\n    \n    @IBOutlet public var pricingTable: UITableView!\n    @IBOutlet public var pricingTableHeightConstraint: NSLayoutConstraint!\n    \n    public func scrollStackRowSizeForAxis(_ axis: NSLayoutConstraint.Axis, row: ScrollStackRow, in stackView: ScrollStack) -\u003e ScrollStack.ControllerSize? {\n        return .fitLayoutForAxis\n    }\n    \n    override public func updateViewConstraints() {\n        pricingTableHeightConstraint.constant = pricingTable.contentSize.height // the size of the table as the size of its content\n        view.height(constant: nil) // cancel any height constraint already in place in the view\n        super.updateViewConstraints()\n    }\n}\n```\n\nIn this way as you add new value to the table the size of the row in stack view will grown.\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"rowsseparator\"/\u003e\n\n### Rows Separator\n\nEach row managed by `ScrollStack` is of a subview class of type `ScrollStackRow`. It has a strong referenced to managed `UIViewController` but also have a subview on bottom called `ScrollStackSeparator`.\n\nYou can hide/show separators by using the following properties of the row:\n\n- `isSeparatorHidden`: to hide separator.\n- `separatorInsets`: to set the insets of the sepatator (by default is set to the same value used by `UITableView` instances)\n- `separatorView.color`: to change the color\n- `separatorView.thickness`: to se the thickness of the separator (1 by default).\n\nMoreover you can set these values directly on `ScrollStack` controller in order to have a default value for each new row.\n\n`ScrollStack` also have a property called `autoHideLastRowSeparator` to hide the last separator of the stack automatically.\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"lightweightplainuiview\"/\u003e\n\n### Using plain UIViews instead of view controllers\n\nSince version 1.3.x ScrollStack can also be used to layout plain `UIView` instances which not belong to a parent view controllers.  \nThis is especially useful when you don't have a complex logic in your views and you want to use ScrollStack to make custom layout and keep your code lightweight.\n\nUsing plain views is pretty easy; each row method supports both `UIView` or `UIViewController` as parameter.\n\nSince you are working with plain `UIView` instances in order to size it correctly you must set its `heightAnchor` or `widthAncor` (depending of your stack orientation) before adding it to the stack.\nAs for controllers, `ScrollStack` keeps a strong reference to the managed view which is added as `contentView` of the parent `ScrollStackRow` instance as it happens for `UIViewController`'s `.view` property.\n\nThis is a small example:\n\n```swift\nlet myCustomView = UIView(frame: .zero)\nmyCustomView.backgroundColor = .green\nmyCustomView.heightAnchor.constraint(equalToConstant: 300).isActive = true\nstackView.addRow(view: myCustomView)\n```\n\n\u003ca name=\"taponrows\"/\u003e\n\n### Tap On Rows\n\nBy default rows are not tappable but if you need to implement some sort of tap features like in `UITableView` you can add it by setting a default callback for `onTap` property on `ScrollStackRow` instances.\n\nFor example:\n\n```swift\nscrollStack.firstRow?.onTap = { row in\n\t// do something on tap\n}\n```\n\nOnce you can set a tap handler you can also provide highlight color for tap.\nTo do it you must implement `ScrollStackRowHighlightable` protocol in your row managed view controller.\n\nFor example:\n\n```swift\nclass GalleryVC: UIViewController, ScrollStackRowHighlightable {\n\n    public var isHighlightable: Bool {\n    \treturn true\n    }\n    \n    func setIsHighlighted(_ isHighlighted: Bool) {\n    \tself.view.backgroundColor = (isHighlighted ? .red : .white)\n    }\n\n}\n\n```\n\nTransition between highlights state will be animated automatically.\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"utilsmethods\"/\u003e\n\n### Get the row/controller\n\n**Get the (first) row which manage a specific view controller type**\nYou can get the first row which manage a specific view controller class using `firstRowForControllerOfType\u003cT: UIViewController\u003e(:) -\u003e ScrollStackRow?` function.\n\n```swift\nlet tagsVC = scrollStack.firstRowForControllerOfType(TagsVC.self) // TagsVC instance\n```\n\n**Get the row which manage a specific controller instance**\nTo get the row associated with a specific controller you can use `rowForController()` function:\n\n```swift\nlet row = scrollStack.rowForController(tagsVC) // ScrollStackRow\n```\n\n\u003ca name=\"setrowinsets\"/\u003e\n\n### Set Row Insets\n\nTo set an insets for a specific row you can use `setRowInsets()` function:\n\n```example\nlet newInsets: UIEdgeInsets = ...\nscrollStack.setRowInsets(index: 0, insets: newInsets)\n```\n\nYou can also use `setRowsInsets()` to set multiple rows.\n\nMoreover by setting `.rowInsets` in your `ScrollStack` class you can set a default insets value for new row added.\n\n\u003ca name=\"changescrollaxis\"/\u003e\n\n### Change ScrollStack scrolling axis\n\nIn order to change the axis of scroll for your `ScrollStack` instances you can set the `axis` property to `horizontal` or `vertical.\n\n\u003ca name=\"rowevents\"/\u003e\n\n### Subscribe to Row Events\n\nYou can listen when a row is removed or added into the stack view by subscribing the `onChangeRow` property.\n\n```swift\nscrollStackView.onChangeRow = { (row, isRemoved) in\n  if isRemoved {\n    print(\"Row at index \\(row.index) was removed\"\n  } else {\n    print(\"A new row is added at index: \\(row.index). It manages \\(type(of: row.controller))\")\n  }\n}\n```\n\nYou can also subscribe events for events about row visibility state changes by setting the `stackDelegate`. Your destination object must therefore conforms to the `ScrollStackControllerDelegate ` protocol:\n\nExample:\n\n```swift\nclass ViewController: ScrollStackController, ScrollStackControllerDelegate {\n\n    func viewDidLoad() {\n        super.viewDidLoad()\n        \n        self.scrollStack.stackDelegate = self\n    }\n\n    func scrollStackDidScroll(_ stackView: ScrollStack, offset: CGPoint) {\n        // Stack did scroll\n    }\n    \n      \n    func scrollStackDidEndScrollingAnimation(_ stackView: ScrollStack) {\n        // Scrolling animation has ended\n    }\n\n    func scrollStackRowDidBecomeVisible(_ stackView: ScrollStack, row: ScrollStackRow, index: Int, state: ScrollStack.RowVisibility) {\n        // Row did become partially or entirely visible.\n    }\n\n    func scrollStackRowDidBecomeHidden(_ stackView: ScrollStack, row: ScrollStackRow, index: Int, state: ScrollStack.RowVisibility) {\n        // Row did become partially or entirely invisible.\n    }\n\n    func scrollStackDidUpdateLayout(_ stackView: ScrollStack) {\n        // This function is called when layout is updated (added, removed, hide or show one or more rows).\n    }\n\n    func scrollStackContentSizeDidChange(_ stackView: ScrollStack, from oldValue: CGSize, to newValue: CGSize) {\n        // This function is called when content size of the stack did change (remove/add, hide/show rows).\n    }\n}\n```\n\n`ScrollStack.RowVisibility` is an enum with the following cases:\n\n- `partial`: row is partially visible.\n- `entire`: row is entirely visible.\n- `hidden`: row is invisible and hidden.\n- `offscreen`: row is not hidden but currently offscreen due to scroll position.\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"systemrequirements\"/\u003e\n\n### System Requirements\n\n- iOS 11+\n- Xcode 10+\n- Swift 5+\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"exampleapp\"/\u003e\n\n### Example App\n\n`ScrollStackController` comes with a demo application which show how easy you can create complex scrollable layoyut and some of the major features of the library.\n\nYou should look at it in order to implement your own layout, create dynamically sized rows and dispatch events.\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"installation\"/\u003e\n\n### Installation\n\n`ScrollStackController` can be installed with CocoaPods by adding pod 'ScrollStackController' to your Podfile.\n\n```ruby\npod 'ScrollStackController'\n```\n\nIt also supports `Swift Package Maneger` aka SPM in your `Package.swift`:\n\n```sh\nimport PackageDescription\n\n  let package = Package(name: \"YourPackage\",\n    dependencies: [\n      .Package(url: \"https://github.com/malcommac/ScrollStackController.git\", majorVersion: 0),\n    ]\n  )\n```\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"authorlicense\"/\u003e\n\n**Consider ❤️ [support the development](#support) of this library!**\n\n## Contributing\n\n- If you **need help** or you'd like to **ask a general question**, open an issue.\n- If you **found a bug**, open an issue.\n- If you **have a feature request**, open an issue.\n- If you **want to contribute**, submit a pull request.\n\n## Copyright \u0026 Acknowledgements\n\nScrollStackController is currently owned and maintained by Daniele Margutti.  \nYou can follow me on Twitter [@danielemargutti](http://twitter.com/danielemargutti).  \nMy web site is [https://www.danielemargutti.com](https://www.danielemargutti.com) \n\nThis software is licensed under [MIT License](LICENSE.md).\n\n***Follow me on:***  \n- 💼 [Linkedin](https://www.linkedin.com/in/danielemargutti/)  \n- 🐦 [Twitter](https://twitter.com/danielemargutti)\n\n","funding_links":["https://www.paypal.com/paypalme/danielemargutti/20","https://github.com/sponsors/malcommac"],"categories":["UI Component"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmalcommac%2FScrollStackController","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmalcommac%2FScrollStackController","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmalcommac%2FScrollStackController/lists"}