{"id":18389294,"url":"https://github.com/rightpoint/stackable","last_synced_at":"2025-09-13T08:49:50.705Z","repository":{"id":45575460,"uuid":"285060848","full_name":"Rightpoint/Stackable","owner":"Rightpoint","description":"Supercharged UIStackViews for Swift","archived":false,"fork":false,"pushed_at":"2021-12-07T18:16:35.000Z","size":948,"stargazers_count":21,"open_issues_count":6,"forks_count":2,"subscribers_count":4,"default_branch":"develop","last_synced_at":"2025-08-24T23:27:25.806Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/Rightpoint.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}},"created_at":"2020-08-04T18:09:42.000Z","updated_at":"2025-01-02T06:29:30.000Z","dependencies_parsed_at":"2022-09-21T20:42:12.259Z","dependency_job_id":null,"html_url":"https://github.com/Rightpoint/Stackable","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/Rightpoint/Stackable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rightpoint%2FStackable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rightpoint%2FStackable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rightpoint%2FStackable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rightpoint%2FStackable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Rightpoint","download_url":"https://codeload.github.com/Rightpoint/Stackable/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rightpoint%2FStackable/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274941962,"owners_count":25378205,"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","status":"online","status_checked_at":"2025-09-13T02:00:10.085Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-11-06T01:42:26.759Z","updated_at":"2025-09-13T08:49:50.677Z","avatar_url":"https://github.com/Rightpoint.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Stackable\n\n\u003cimg src=\"https://github.com/Rightpoint/Stackable/blob/develop/docs/LogoRender.png\" height=\"300\"\u003e\n\n**Stackable** is a delightful and declarative set of utilities for `UIStackView`. It is designed to make your layout code easier to write, read, and communicate with your designer.\n\n[![Version](https://img.shields.io/cocoapods/v/RPStackable.svg?style=flat)](https://cocoapods.org/pods/RPStackable)\n[![License](https://img.shields.io/cocoapods/l/RPStackable.svg?style=flat)](https://cocoapods.org/pods/RPStackable)\n[![Platform](https://img.shields.io/cocoapods/p/RPStackable.svg?style=flat)](https://cocoapods.org/pods/RPStackable)\n\nStackable aims to bridge the gap between the way designers articulate layout and the way developers express that layout in code.\n\n```swift\nstack.stackable.add([\n    logo,\n    30,\n    \"Example Views\",\n    10,\n    cells\n        .outset(to: view)\n        .margins(alignedWith: stack),\n    UIStackView.stackable.hairlines(around: cells)\n        .outset(to: view),\n    20...,\n    \"Copyright Rightpoint\",\n])\n```\n\n\u003cimg src=\"https://github.com/Rightpoint/Stackable/blob/develop/docs/MenuExampleRender.png\" height=\"600\"\u003e\n\n## Views\n**Stackable** includes built-in support for all `UIView` subclasses.\n\nAdd them using `stackView.stackable.add(_ stackables: [Stackable])`\n\n```swift\nstack.stackable.add([\n    imageView,\n    label,\n    button,\n])\n```\n\nAdditionally, you can reference `UIViewController` directly to include its view.\n\n```swift\nstack.stackable.add([\n    viewController,\n    button,\n])\n```\n\nThere is also support for:\n  - `String`\n  - `NSAttributedString`\n  - `UIImage`\n\n```swift\nstack.stackable.add([\n    \"String\",\n    NSAttributedString(string: \"Attributed String\"),\n    UIImage(named: \"example\"),\n])\n```\n\n#### Alignment\nViews can be expressively manipulated to adjust their alignment in a `UIStackView`.\n\n```swift\nlet stack = UIStackView()\nstack.axis = .vertical\nstack.stackable.add([\n    imageView\n      .inset(by: .init(top: 20, left: 20, bottom: 20, right: 20), //imageView padded on all sides 20px\n    \n    label\n      .inset(by: .init(top: 0, left: -20, bottom: 0, right: -20) //inset can also be negative to move outside of bounds of `stack`\n      .aligned(.right), // aligned right (transforms are composable)\n      \n    button\n      .aligned(.centerX), // button aligned center horizontally\n      \n    hairline\n      .outset(to: view), // hairline pinned to horizontal edges of `view`\n      \n    cells\n      .outset(to:  view) // cells horizontal edges pinned to horizontal edges of `view`\n      .margins(alignedWith: view), // cell layout margins updated to line up with view.layoutMarginsGuide\n])\n```\n\n## Spaces\n\nSpaces can be added using a number literal or `StackableSpaceItem`. Add them alongside your views.\n\n#### Fixed Spaces\n```swift\nstack.stackable.add([\n    20, //constant space\n    viewA,\n    20, //custom spacing after `viewA`, will be removed if viewA is hidden.\n    viewB,\n    12.5, //floating point works too!\n    viewC,\n    UIStackView.stackable.constantSpace(20), //Constant space, not dependent on visibility of `viewC`\n    viewD,\n    UIStackView.stackable.space(after: viewD, 20), //Explicit custom space dependent on visibility of viewD. Equivalent to just using `20` here.\n    viewE,\n    UIStackView.stackable.space(before: viewF, 20), //Custom space before viewF. Dependent on the visibility of `viewF`\n    viewF,\n])\n```\n\n#### Flexible Spaces\n```swift\nstack.stackable.add([\n    viewA,\n    20...30, // Flexible space, at least 20, at most 30, inclusive. Flexible spaces do not track view visibility.\n    viewB,\n    20..., // Flexible space. At least 20, no maximum.\n    viewC,\n    ...15.5, // Flexible space. At least 0, at most 15.5, floating point works too!\n    viewD,\n    UIStackView.stackable.flexibleSpace, // flexible space, at least 0, no maximum. Equivalent to `0...`\n    viewE,\n    UIStackView.stackable.flexibleSpace(.atLeast(20)), // flexible space, at least 20, no maximum. equivalent to `20...`\n    viewF,\n    UIStackView.stackable.flexibleSpace(.atMost(20)), // flexible space. at least 0, at most 20, equivalent to `...20`\n    viewG,\n    UIStackView.stackable.flexibleSpace(.range(10...20)), // flexible space, at least 10, at most 20, equivalent to `10...20`\n])\n```\n\n#### Advanced Spaces\n```swift\nlet cells: [UIView] = ...\nstack.stackable.add([\n    cells,\n    UIStackView.stackable.space(afterGroup: cells, 20), // A space that is dependent on at least one view in `cells` to be visible.\n])\n```\n\n## Hairlines\n\n```swift\nstack.stackable.add([\n    viewA,\n    UIStackView.stackable.hairline, // Simple hairline, extended to edges of StackView. \n    viewB,\n    UIStackView.stackable.hairline(after: viewB), // hairline that mirrors visibility of `viewB`\n    viewC,\n    UIStackView.stackable.hairline(between: viewC, viewD), // hairline that is dependent on both `viewC` and `viewD` being visible.\n    viewD,\n    UIStackView.stackable.hairline(before: viewE), // hairline before `viewE`, mirrors visibility of `viewE`\n    viewE,\n    viewF,\n    UIStackView.stackable.hairlines(around: viewF), // hairline added above and below viewF. Mirrors visibility of `viewF`\n])\n```\n\n#### Hairlines and Groups\n```swift\nlet cells: [UIView] = ...\n\nstack.stackable.add([\n    cellsA,\n    UIStackView.stackable.hairlines(between: cellsA), // Hairlines between any visibile members of `cellsA`\n\n    cellsB,\n    UIStackView.stackable.hairlines(after: cellsB), // Hairlines after all visible members of `cellsB`\n\n    cellsC,\n    UIStackView.stackable.hairlines(around: cellsC), // Hairlines above and below all visible members of `cellsC`\n])\n```\n\n#### Hairline Alignment\n```swift\nstack.stackable.add([\n    viewA,\n    UIStackView.hairline\n        .inset(by: .init(top: 0, left: 10, bottom: 0, right: 10) // hairline inset horizontally by 10\n      \n    viewB,\n    UIStackView.hairline\n        .inset(by: .init(top: 0, left: -10, bottom: 0, right: -10) // hairline outset horizontally by 10\n\n    viewC,\n    UIStackView.hairline\n        .outset(to: view) // hairline constrained to edges of some ancestor (horizontal edges for a vertical stack)\n])\n```\n\n#### Hairline Appearance\n\nHairline appearance can be customized on a per hairline or group basis:\n```swift\nstack.stackable.add([\n  viewA,\n  UIStackView.hairline\n      .color(.lightGray)\n      .thickness(1),\n    \n  cells,\n  UIStackView.hairlines(around: cells)\n      .color(.red)\n      .thickness(10)\n])\n```\n\nIt can also be customized on a per `UIStackView` basis:\n```swift\nlet stack = UIStackView()\nstack.stackable.hairlineColor = .lightGray\nstack.stackable.hairlineThickness = 1\n```\n\nIt can also be customized with a global default:\n```swift\nUIStackView.stackable.hairlineColor = .lightGray\nUIStackView.stackable.hairlineThickness = 1\n```\n\nIf more customiztion is required, you may provide a `HairlineProvider` to vend the `UIView` that should be used as a hairline:\n```swift\nlet stack = UIStackView()\nstack.stackable.hairlineProvider = { stackView in\n    let customHairline = UIView()\n    //...\n    return customHairline\n}\n\n// global default provider\nUIStackView.stackable.hairlineProvider = { stackView in\n    let customHairline = UIView()\n    //...\n    return customHairline\n}\n```\n\n## Example\n\nTo run the example project, clone the repo, and run `pod install` from the Example directory first.\n\n## Requirements\n\n* iOS 12.0+\n\n## Installation\n\nStackable is available as a Swift Package. You can also install it through \n[CocoaPods](https://cocoapods.org) by adding the following line to your Podfile:\n\n```ruby\npod 'RPStackable'\n```\n\n## Author\n\n\u003cjclark@rightpoint.com\u003e\n\n## License\n\nStackable is available under the MIT license. See the LICENSE file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frightpoint%2Fstackable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frightpoint%2Fstackable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frightpoint%2Fstackable/lists"}