{"id":21318379,"url":"https://github.com/mezhevikin/pin","last_synced_at":"2025-07-12T02:33:25.573Z","repository":{"id":62724645,"uuid":"560715786","full_name":"mezhevikin/Pin","owner":"mezhevikin","description":"📌 A tiny library that makes working with AutoLayout easier. Only 200 lines of code.","archived":false,"fork":false,"pushed_at":"2022-11-04T08:08:13.000Z","size":30,"stargazers_count":9,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-03-14T23:47:11.360Z","etag":null,"topics":["anchor","autolayout","chainable","constraints","dsl","extension","frame","ios","layout","nslayoutanchor","nslayoutconstraint","pinlayout","purelayout","snapkit","stevia","sugar","swift","uiview"],"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/mezhevikin.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":"2022-11-02T05:14:26.000Z","updated_at":"2023-04-23T11:37:25.000Z","dependencies_parsed_at":"2022-11-05T06:00:42.239Z","dependency_job_id":null,"html_url":"https://github.com/mezhevikin/Pin","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mezhevikin%2FPin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mezhevikin%2FPin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mezhevikin%2FPin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mezhevikin%2FPin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mezhevikin","download_url":"https://codeload.github.com/mezhevikin/Pin/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225787454,"owners_count":17524108,"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":["anchor","autolayout","chainable","constraints","dsl","extension","frame","ios","layout","nslayoutanchor","nslayoutconstraint","pinlayout","purelayout","snapkit","stevia","sugar","swift","uiview"],"created_at":"2024-11-21T19:12:34.742Z","updated_at":"2024-11-21T19:12:35.465Z","avatar_url":"https://github.com/mezhevikin.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pin\n\n📌 A tiny library that makes working with AutoLayout easier.\n\nA library for those who don't want to use big libraries like SnapKit, but the standard NSLayoutConstraint creation seems too verbose.\n\n### Example with Pin 😸\n\n```swift\nimport UIKit\nimport Pin\n\nclass CurrencyCell: UITableViewCell {\n    \n    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {\n        super.init(style: style, reuseIdentifier: reuseIdentifier)\n        \n        contentView.addSubview(bodyView)\n        bodyView.addSubviews([flagLabel, titleLabel, codeLabel])\n        \n        Pin.activate([\n            bodyView.pin.horizontally(offset: 15).vertically(),\n            flagLabel.pin.start().size(36).centerY(),\n            titleLabel.pin.after(flagLabel, offset: 15).vertically(),\n            codeLabel.pin.after(titleLabel, offset: 15).end().vertically()\n        ])\n    }\n    \n    let bodyView = UIView()\n    let flagLabel = UILabel()\n    let titleLabel = UILabel()\n    let codeLabel = UILabel() \n    \n}\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://user-images.githubusercontent.com/973364/199716786-3ba59b9e-1efa-4241-80e2-0fc54dfaf9c1.jpg\" width=\"320\"\u003e\n\u003c/p\u003e\n\n[Original code](https://github.com/mezhevikin/PinExample/blob/master/PinExample/CurrencyCell.swift)\n\n### Example without Pin 🙀\n\n```swift\nimport UIKit\n\nclass CurrencyCell: UITableViewCell {\n    \n    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {\n        super.init(style: style, reuseIdentifier: reuseIdentifier)\n        \n        bodyView.translatesAutoresizingMaskIntoConstraints = false\n        flagLabel.translatesAutoresizingMaskIntoConstraints = false\n        titleLabel.translatesAutoresizingMaskIntoConstraints = false\n        codeLabel.translatesAutoresizingMaskIntoConstraints = false\n        \n        contentView.addSubview(bodyView)\n        bodyView.addSubview(flagLabel)\n        bodyView.addSubview(titleLabel)\n        bodyView.addSubview(codeLabel)\n        \n        NSLayoutConstraint.activate([\n            bodyView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15),\n            bodyView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15),\n            bodyView.topAnchor.constraint(equalTo: contentView.topAnchor),\n            bodyView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),\n            \n            flagLabel.leadingAnchor.constraint(equalTo: bodyView.leadingAnchor),\n            flagLabel.centerYAnchor.constraint(equalTo: bodyView.centerYAnchor),\n            flagLabel.widthAnchor.constraint(equalToConstant: 36),\n            flagLabel.heightAnchor.constraint(equalToConstant: 36),\n            \n            titleLabel.leadingAnchor.constraint(equalTo: flagLabel.trailingAnchor, constant: 15),\n            titleLabel.topAnchor.constraint(equalTo: bodyView.topAnchor),\n            titleLabel.bottomAnchor.constraint(equalTo: bodyView.bottomAnchor),\n            \n            codeLabel.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: 15),\n            codeLabel.trailingAnchor.constraint(equalTo: bodyView.trailingAnchor),\n            codeLabel.topAnchor.constraint(equalTo: bodyView.topAnchor),\n            codeLabel.bottomAnchor.constraint(equalTo: bodyView.bottomAnchor)\n        ])\n    }\n    \n    let bodyView = UIView()\n    let flagLabel = UILabel()\n    let titleLabel = UILabel()\n    let codeLabel = UILabel() \n    \n}\n```\n### Methods\n\n```swift\n// titleLabel.leading == titleLabel.superview.leading\n// Use addSubview(titleLabel) before it\ntitleLabel.pin.start()\n\n// titleLabel.leading == iconView.leading\ntitleLabel.pin.start(iconView)\n\n// titleLabel.leading == iconView.leading + 10\ntitleLabel.pin.start(iconView, offset: 10)\n\n// titleLabel.leading == titleLabel.superview.safeAreaLayoutGuide.leading\ntitleLabel.pin.start(safe: true)\n\n// titleLabel.trailing == titleLabel.superview.trailing - 10\ntitleLabel.pin.end(offset: -10)\n\n// titleLabel.top == titleLabel.superview.top\ntitleLabel.pin.top()\n\n// titleLabel.bottom == titleLabel.superview.bottom\ntitleLabel.pin.bottom()\n\n// titleLabel.leading == titleLabel.superview.leading + 10\n// titleLabel.trailing == titleLabel.superview.trailing - 10\ntitleLabel.pin.horizontally(offset: 10)\n\n// titleLabel.top == titleLabel.superview.top\n// titleLabel.bottom == titleLabel.superview.bottom\ntitleLabel.pin.vertically()\n\n// contentView.leading == contentView.superview.leading + 10\n// contentView.trailing == contentView.superview.trailing - 10\n// contentView.top == contentView.superview.top + 10\n// contentView.bottom == contentView.superview.bottom - 10\ncontentView.all(offset: 10)\n\n// titleLabel.leading == iconView.trailing + 10\ntitleLabel.pin.after(iconView, offset: 10)\n\n// iconView.trailing == titleLabel.leading - 10\niconView.pin.before(titleLabel, offset: -10)\n\n// titleLabel.top == navigationBar.bottom + 10\ntitleLabel.pin.below(navigationBar, offset: 10)\n\n// titleLabel.width == 200\ntitleLabel.pin.width(200)\n\n// titleLabel.height == 20\ntitleLabel.pin.height(20)\n\n// codeLabel.width == titleLabel.width\ncodeLabel.pin.width(titleLabel)\n\n// codeLabel.height == titleLabel.height\ncodeLabel.pin.height(titleLabel)\n\n// codeLabel.width \u003c= titleLabel.width\ncodeLabel.pin.add(\n    attr: .width,\n    relation: .lessThanOrEqual,\n    to: titleLabel,\n    attr: .width\n)\n```\n\n### Activate and deactivate\n\n```swift\nlet top = titleLabel.pin.top()\ntop.activate()\ntop.deactivate()\n```\n\nActivation array of constraints is more efficient than activating each constraint individually.\n\n```swift\nPin.activate([\n    bodyView.pin.horizontally(offset: 15).vertically(),\n    flagLabel.pin.start().size(36).centerY(),\n    titleLabel.pin.after(flagLabel, offset: 15).vertically(),\n    codeLabel.pin.after(titleLabel, offset: 15).end().vertically()\n])\n```\n\n### Safe area\n\n```swift\noverride func viewDidLoad() {\n    super.viewDidLoad()\n    view.addSubView(toolBar)\n    toolBar.pin\n        .start(safe: true)\n        .end(safe: true)\n        .top(safe: true)\n        .height(55)\n        .activate()\n}\n```\n\n### Body\n\n```swift\n// Add body (bodyView fill view)\nview.addBody(bodyView)\n// Add body with safe area\nview.addBody(bodyView, safe: true)\n// Add body with insets 15\nview.addBody(bodyView, insets: .all(15))\n// Add body with horizontal insets 15\nview.addBody(bodyView, insets: .horizontal(15))\n```\n\n### Priority\n\n```swift\n// Set priority for each constraint\ntitleLabel.pin\n    .start().priority(.defaultHeight)\n    .end().priority(.defaultLow)\n\n// Set priority for all constraints\ntitleLabel.pin.start().end().priorityForAll(.defaultHeight)\n```\n\n### Access to NSLayoutConstraint\n\n```swift\nlet start = titleLabel.pin.start().constraints.last\nstart.constant = 30\nstart.isActive = true\n```\n\n### Extensions\n\nYou can add own extensions:\n\n```swift\nextension Pin {\n    \n    public func horizontallyBetween(\n        _ first: UIView,\n        _ second: UIView,\n        offset: CGFloat = 0\n    ) -\u003e Self {\n        self.after(first, offset: offset)\n            .before(second, offset: -offset)\n    }\n    \n    public func verticallyBetween(\n        _ first: UIView,\n        _ second: UIView,\n        offset: CGFloat = 0\n    ) -\u003e Self {\n        self.add(attr: .top, to: first, attr: .bottom, constant: offset)\n            .add(attr: .bottom, to: second, attr: .top, constant: -offset)\n    }\n    \n}\n```\n\n### Right to left languages\n\nMethods `start(), end(), after(), before()` support rtl languages by default. If you want to force direction then use:\n\n```swift\nsemanticContentAttribute = .forceLeftToRight\n```\n\n### Swift Package Manager\n\n```\nhttps://github.com/mezhevikin/Pin.git\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmezhevikin%2Fpin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmezhevikin%2Fpin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmezhevikin%2Fpin/lists"}