{"id":30473903,"url":"https://github.com/danielinoa/pin","last_synced_at":"2025-08-24T09:51:08.067Z","repository":{"id":37506880,"uuid":"326630174","full_name":"danielinoa/Pin","owner":"danielinoa","description":"A declarative wrapper around NSLayoutConstraint and NSLayoutAnchor to simplify the assembly of AutoLayout constraints.","archived":false,"fork":false,"pushed_at":"2023-06-07T07:31:05.000Z","size":63,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-23T21:19:01.829Z","etag":null,"topics":["constraints","layout","swift"],"latest_commit_sha":null,"homepage":"https://github.com/danielinoa/Pin","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielinoa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-01-04T09:20:54.000Z","updated_at":"2024-03-27T10:14:57.000Z","dependencies_parsed_at":"2023-01-17T15:01:03.315Z","dependency_job_id":null,"html_url":"https://github.com/danielinoa/Pin","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/danielinoa/Pin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielinoa%2FPin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielinoa%2FPin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielinoa%2FPin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielinoa%2FPin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielinoa","download_url":"https://codeload.github.com/danielinoa/Pin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielinoa%2FPin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271839248,"owners_count":24831732,"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-08-24T02:00:11.135Z","response_time":111,"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":["constraints","layout","swift"],"created_at":"2025-08-24T09:50:54.573Z","updated_at":"2025-08-24T09:51:08.051Z","avatar_url":"https://github.com/danielinoa.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=center\u003ePin\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://user-images.githubusercontent.com/972877/143309795-78be2e80-05bd-4871-8a0c-2dcf4eb7cd4b.jpg\" width=\"200\" max-width=\"90%\" alt=“Pin” /\u003e\n\u003c/p\u003e\n\n# Introduction\n\n### What\n**Pin** is a thin wrapper around `NSLayoutConstraint` and `NSLayoutAnchor` to streamline the assembly of `AutoLayout` constraints.\n\n### Why\nBuilding rule-based layouts has been made easier with the introduction of `NSLayoutConstraint` and later `NSLayoutAnchor`. However, there is still room to further simplify commonly used operations without the verbosity and tedious setup of existing solutions. `Pin` addresses this with an intuitive and declarative API that leverages the builder pattern.\n\n### How\n`Pin` works by assembling a tree of nodes containing a view and its associated constraints, and once activated at the root it will start resolving constraints in a depth-first manner. If stored, a tree can also be deactivated. Deactivation reverts all constraints and removes any artifacts added in the activation process.\n\n# Usage\n\n## Installation\n\nTo install using Swift Package Manager, add this to the dependencies section in your `Package.swift` file:\n\n```swift\n.package(url: \"https://github.com/danielinoa/Pin.git\", .branch(\"main\"))\n```\n\n## Integration\n\nThe primary API exposed by **Pin** is the `Pinnable` protocol, which is conformed to by `UIView` and exposes functions to layout a view.\n\nThe most used functions in this library are `add()`, `pin(to:)`, and `activate()`. \n\n- `add()` contains a view within a parent using a containment strategy. The default is `UIView.addSubview`.\n- `pin(to:)` constrains a view within a parent using a any number of attributes (`leading`, `top`, `centerX`, `width`, etc).\n- `activate()` actives the tree of constraints.\n\nTo further illustrate `Pin`'s API let's go over some common operations we may leverage when creating a layout.\n\nTo start let's import the dependencies.\n```swift\nimport Pin\nimport UIKit\n```\n\n### Pinning to parent's edges\n\n```swift\nlet parent = UIView()\nlet child = UIView()\nparent.add {\n    child.pin(to: .leading, .trailing, .top, .bottom) \n    // or \n    child.pin(to: .edges)\n}\n.activate()\n``` \n\n### Centering within parent\n\n```swift\nlet parent = UIView()\nlet child = UIView()\nparent.add {\n    child.pin(to: .centerX, centerY) \n    // or \n    child.pin(to: .center)\n}\n.activate()\n``` \n\n### Sizing\n\n```swift\nlet parent = UIView()\nlet child = UIView()\nparent.add {\n    child\n        .size(height: 100)\n        .pin(to: .width)\n}\n.activate()\n```\n\n### Containing a view within a view within a view ♾\n\n```swift\nlet parent = UIView()\nlet child = UIView()\nlet grandChild = UIView()\nparent.add {\n    child\n        .size(height: 100)\n        .pin(to: .width)\n        .add {\n            grandChild\n                .pin(to: .size, .center)\n        }\n}\n.activate()\n```\n\n### Adding views using a custom containment strategy\n\n```swift\nextension Pinnable where Self: UIStackView {\n\n    /// Adds the specified Pinnable's view as an arranged-subview under this `UIStackView`.\n    public func stack(_ pinnables: Pinnable...) -\u003e Pinnable {\n        add(pinnables).contain(using: {\n            self.addArrangedSubview($0.view)\n        })\n    }\n}\n\nlet stackView = UIStackView()\nlet child = UIView()\nstackView.stack(\n    child.size(height: 100)\n)\n.activate()\n```\n\n### Extending `Pinnable` with custom operators\n\nAll operators (`pin()`, `center()`, `size()`, etc) in `Pin` are defined as extensions of the [`Pinnable`](https://github.com/danielinoa/Pin/blob/main/Sources/Pin/Core/Pinnable.swift) protocol.\n\nThe [`center()`](https://github.com/danielinoa/Pin/blob/main/Sources/Pin/Extensions/Resolvables/Pinnable%2BCenter.swift) operator, backed by the [`Center`](https://github.com/danielinoa/Pin/blob/main/Sources/Pin/Extensions/Resolvables/Pinnable%2BCenter.swift) class, is an example of how `Pinnable` can be extended to create layouts not possible with the basic `pin(to:)`. While `pin(to: .center)` centers a view within a parent view, [`center(between:and:)`](https://github.com/danielinoa/Pin/blob/main/Sources/Pin/Extensions/Resolvables/Pinnable%2BCenter.swift) centers a view within any two anchors regardless of where in the view hierarchy those anchors reside.\n\nOperators can be built on top of existing operators, or can be backed by types that conform to either [`SuperResolvable`](https://github.com/danielinoa/Pin/blob/main/Sources/Pin/Core/SuperResolvable.swift) or [`SelfResolvable`](https://github.com/danielinoa/Pin/blob/main/Sources/Pin/Core/SelfResolvable.swift).\n\n- A `SuperResolvable` represents the future assembly and activation of a constraint (or set of constraints) for a view that needs its designated superview to satisfy its layout requirements. [`Center`](https://github.com/danielinoa/Pin/blob/main/Sources/Pin/Extensions/Resolvables/Pinnable%2BCenter.swift) and [`Pin`](https://github.com/danielinoa/Pin/blob/main/Sources/Pin/Extensions/Resolvables/Pinnable%2BPin.swift) are examples of concrete `SuperResolvable`s.\n\n- A `SelfResolvable` represents the future assembly and activation of a constraint (or set of constraints) for a view that satisfy its layout requirements without a superview. `NSLayoutConstraint` has implicit conformance to `SelfResolvable`. \nThe [`size(width:height:)`](https://github.com/danielinoa/Pin/blob/main/Sources/Pin/Extensions/Resolvables/Pinnable%2BSize.swift) operator is a good application of `SelfResolvable` given that size constraints do not require a parent view to be satisfied.\n\n# Contributing\n\nFeel free to open an issue if you have questions about how to use `Pin`, discovered a bug, or want to improve the implementation or interface.\n\n# Credits\n\n`Pin` is primarily the work of [Daniel Inoa](https://github.com/danielinoa).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielinoa%2Fpin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielinoa%2Fpin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielinoa%2Fpin/lists"}