[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-6977":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":17,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":21,"hasPages":21,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":24,"readmeContent":25,"aiSummary":26,"trendingCount":16,"starSnapshotCount":16,"syncStatus":27,"lastSyncTime":28,"discoverSource":29},6977,"MagazineLayout","airbnb\u002FMagazineLayout","airbnb","A collection view layout capable of laying out views in vertically scrolling grids and lists.","",null,"Swift",3370,223,56,12,0,1,4,29.05,"Apache License 2.0",false,"master",[],"2026-06-12 02:01:32","# MagazineLayout\nA collection view layout capable of laying out views in vertically scrolling grids and lists.\n\n[![Swift Package Manager compatible](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FSPM-compatible-4BC51D.svg?style=flat)](https:\u002F\u002Fgithub.com\u002Fapple\u002Fswift-package-manager)\n[![Carthage compatible](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FCarthage-compatible-4BC51D.svg?style=flat)](https:\u002F\u002Fgithub.com\u002FCarthage\u002FCarthage)\n[![Version](https:\u002F\u002Fimg.shields.io\u002Fcocoapods\u002Fv\u002FMagazineLayout.svg)](https:\u002F\u002Fcocoapods.org\u002Fpods\u002FMagazineLayout)\n[![License](https:\u002F\u002Fimg.shields.io\u002Fcocoapods\u002Fl\u002FMagazineLayout.svg)](https:\u002F\u002Fcocoapods.org\u002Fpods\u002FMagazineLayout)\n[![Platform](https:\u002F\u002Fimg.shields.io\u002Fcocoapods\u002Fp\u002FMagazineLayout.svg)](https:\u002F\u002Fcocoapods.org\u002Fpods\u002FMagazineLayout)\n![Swift](https:\u002F\u002Fgithub.com\u002Fairbnb\u002FMagazineLayout\u002Fworkflows\u002FSwift\u002Fbadge.svg)\n\n\n## Introduction\n`MagazineLayout` is a `UICollectionViewLayout` subclass for laying out vertically scrolling grids and lists of items. Compared to `UICollectionViewFlowLayout`, `MagazineLayout` supports many additional features:\n\n- Item widths based on a fraction of the total available width\n\t- Full width for a list layout (similar to `UITableView`)\n\t- Half-width, third-width, etc. for a grid layout\n- Self-sizing in just the vertical dimension\n- Per-item self-sizing preferences (self-size and statically-size items anywhere in your collection view)\n- Self-sizing headers and footers\n- Hiding or showing headers and footers on a per-section basis\n- Pinned (sticky) headers and footers\n- Section backgrounds that can be hidden \u002F visible on a per-section basis\n- Customizable insert and delete animations for items and supplementary views\n\nOther features:\n- Specifying horizontal item spacing on a per-section basis\n- Specifying vertical row spacing on a per-section basis\n- Specifying section insets on a per-section basis\n- Specifying item insets on a per-section basis\n\nThese capabilities have allowed us to build a wide variety of screens in the Airbnb app, many of which are among our highest-traffic screens. Here are just a few examples of screens laid out using `MagazineLayout`:\n\n| Homes Search | Experiences Search | Wish List | Home  |\n| --- | --- | --- | --- |\n| ![Homes Search](Docs\u002FImages\u002FSearchHomes.png) | ![Experiences Search](Docs\u002FImages\u002FSearchExperiences.png)  | ![Wish list](Docs\u002FImages\u002FWishList.png) | ![Home](Docs\u002FImages\u002FHomePDP.png) |\n\n| Plus Home | Plus Home Tour | Trips | Trip Detail |\n| --- | --- | --- | --- |\n|![Plus Home](Docs\u002FImages\u002FPlusHomePDP.png) | ![Plus Home Tour](Docs\u002FImages\u002FPlusHomeTour.png) | ![Trips](Docs\u002FImages\u002FTrips.png) |  ![Trip Detail](Docs\u002FImages\u002FTripDetail.png) |\n\n## Table of Contents\n- [Example App](#example-app)\n\t- [Using the Example App](#using-the-example-app)\n\t\t- [Adding a new item](#adding-a-new-item)\n\t\t- [Deleting an item](#deleting-an-item)\n- [Getting Started](#getting-started)\n\t- [Requirements](#requirements)\n\t- [Installation](#installation)\n\t\t- [Carthage](#carthage)\n\t\t- [CocoaPods](#cocoapods)\n\t- [Usage](#usage)\n\t\t- [Setting up cells and headers](#setting-up-cells-and-headers)\n\t\t- [Importing MagazineLayout](#importing-magazinelayout)\n\t\t- [Setting up the collection view](#setting-up-the-collection-view)\n\t\t- [Registering cells and supplementary views](#registering-cells-and-supplementary-views)\n\t\t- [Setting the data source](#setting-the-data-source)\n\t\t- [Configuring the delegate](#configuring-the-delegate)\n* [Contributions](#contributions)\n* [Maintainers](#maintainers)\n* [Contributors](#contributors)\n* [License](#license)\n\n## Example App\nAn example app is available to showcase and enable you to test some of `MagazineLayout`'s features. It can be found in `.\u002FExample\u002FMagazineLayoutExample.xcworkspace`. \n\nNote: Make sure to use the `.xcworkspace` file, and not the `.xcodeproj` file, as the latter does not have access to `MagazineLayout.framework`.\n\n### Using the Example App\nWhen you first open the example app, you'll see many items and sections pre-populated. Most items are configured to self-size based on the text they're displaying.\n\n![Example App](Docs\u002FImages\u002FExampleApp.png)\n\nIf you'd like to get rid of the sample content and start with a blank collection view, you can tap the reload icon in the navigation bar.\n\n| Reload Menu | No Items |\n| --- | --- |\n| ![Reload Menu](Docs\u002FImages\u002FExampleAppReloadMenu.png) | ![No Items](Docs\u002FImages\u002FExampleAppBlank.png) |\n\nFrom this menu, you can also reset the app back to the original sample data.\n\n#### Adding a new item\nTo add a new item, tap the add icon in the navigation bar.\n\n![Add Item Screen](Docs\u002FImages\u002FExampleAppAddItem.png)\n\nFrom the add screen, you can configure a new item to insert into the `UICollectionView`. The item will be inserted with an animation once you tap the done button in the navigation bar.\n\nItem configuration options:\n\n- Section index (will create a new section if one does not exist for the specified index)\n- Item index (position in the specified section)\n- Item content \u002F text to be displayed in the item (this will change how tall the item is if using a `.dynamic` height mode)\n- Color to use for the background of the item\n- Width mode (controls how wide the item should be in relation to the available width)\n- Height mode (controls self-sizing behavior)\n\n![Add Item Animation](Docs\u002FImages\u002FExampleAppInsertAnimation.gif)\n\n#### Deleting an item\nTo delete an item, simple tap on the item in the collection view. The item will be deleted with an animation.\n\n![Delete Item Animation](Docs\u002FImages\u002FExampleAppDeleteAnimation.gif)\n\n## Getting Started\n\n### Requirements\n- Deployment target iOS 10.0+, tvOS 10.0+\n- Swift 4+\n- Xcode 10+\n\n### Installation\n#### Carthage\nTo install `MagazineLayout` using [Carthage](https:\u002F\u002Fgithub.com\u002FCarthage\u002FCarthage), add\n`github \"airbnb\u002FMagazineLayout\"` to your Cartfile, then follow the integration tutorial [here](https:\u002F\u002Fgithub.com\u002FCarthage\u002FCarthage#if-youre-building-for-ios-tvos-or-watchos).\n\n#### CocoaPods\nTo install `MagazineLayout` using [CocoaPods](http:\u002F\u002Fcocoapods.org), add\n`pod 'MagazineLayout'` to your Podfile, then follow the integration tutorial [here](https:\u002F\u002Fguides.cocoapods.org\u002Fusing\u002Fusing-cocoapods.html).\n\n\n### Usage\nOnce you've integrated the `MagazineLayout` into your project, using it with a collection view is easy.\n\n#### Setting up cells and headers\n[Due to shortcomings in `UIKit`](MagazineLayout\u002FPublic\u002FViews\u002FMagazineLayoutCollectionViewCell.swift#L34-L39), `MagazineLayout` requires its own `UICollectionViewCell` and `UICollectionReusableView` subclasses:\n-  `MagazineLayoutCollectionViewCell`\n- `MagazineLayoutCollectionReusableView`\n\nThese two types enable cells and supplementary views to self-size correctly when using `MagazineLayout`. **Make sure that the custom cell and reusable view types in your app subclass from `MagazineLayoutCollectionViewCell` and `MagazineLayoutCollectionReusableView`, respectively.** \n\nAlternatively, you can copy the implementation of `preferredLayoutAttributesFitting(_:)`\nfor use in your custom cell and reusable view types, without subclassing from the ones `MagazineLayout` provides.\n\n#### Importing MagazineLayout\nAt the top of the file where you'd like to use `MagazineLayout` (likely a `UIView` or `UIViewController` subclass), import `MagazineLayout`.\n```swift\nimport MagazineLayout \n```\n\n#### Setting up the collection view\nCreate your `UICollectionView` instance, passing in a `MagazineLayout` instance for the layout parameter.\n```swift\nlet layout = MagazineLayout()\nlet collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)\n\n```\n\nMake sure to add  `collectionView` as a subview, then properly constrain it using Auto Layout or manually set its `frame` property.\n\n```swift\nview.addSubview(collectionView)\n\ncollectionView.translatesAutoresizingMaskIntoConstraints = false\n\nNSLayoutConstraint.activate([\n  collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),\n  collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),\n  collectionView.topAnchor.constraint(equalTo: view.topAnchor),\n  collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),\n])\n```\n\n#### Registering cells and supplementary views\nRegister your cell and reusable view types with your collection view.\n```swift\ncollectionView.register(MyCustomCell.self, forCellWithReuseIdentifier: \"MyCustomCellReuseIdentifier\")\n\n\u002F\u002F Only necessary if you want section headers\ncollectionView.register(MyCustomHeader.self, forSupplementaryViewOfKind: MagazineLayout.SupplementaryViewKind.sectionHeader, withReuseIdentifier: \"MyCustomHeaderReuseIdentifier\")\n\n\u002F\u002F Only necessary if you want section footers\ncollectionView.register(MyCustomFooter.self, forSupplementaryViewOfKind: MagazineLayout.SupplementaryViewKind.sectionFooter, withReuseIdentifier: \"MyCustomFooterReuseIdentifier\")\n\n\u002F\u002F Only necessary if you want section backgrounds\ncollectionView.register(MyCustomBackground.self, forSupplementaryViewOfKind: MagazineLayout.SupplementaryViewKind.sectionBackground, withReuseIdentifier: \"MyCustomBackgroundReuseIdentifier\")\n```\n\nBecause cells, headers, and footers can self-size (backgrounds do not self-size), in this example, `MyCustomCell`, `MyCustomHeader`, and `MyCustomFooter` **must** have the correct implementation of `preferredLayoutAttributesFitting(_:)`. See [Setting up cells and headers](#setting-up-cells-and-headers).\n\n#### Setting the data source\nNow that you've registered your view types with your collection view, it's time to wire up the data source. Like with any collection view integration, your data source needs to conform to `UICollectionViewDataSource`. If the same object that owns your collection view is also your data source, you can simply do this:\n```swift\ncollectionView.dataSource = self\n```\n\n#### Configuring the delegate\nLastly, it's time to configure the layout to suit your needs. Like with `UICollectionViewFlowLayout` and `UICollectionViewDelegateFlowLayout`, `MagazineLayout` configured its layout through its `UICollectionViewDelegateMagazineLayout`. \n\nTo start configuring `MagazineLayout`, set your collection view's `delegate` property to an object conforming to `UICollectionViewDelegateMagazineLayout`. If the same object that owns your collection view is also your delegate, you can simply do this:\n```swift\ncollectionView.delegate = self\n```\n\nHere's an example delegate implementation:\n\n```swift\nextension ViewController: UICollectionViewDelegateMagazineLayout {\n\n  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeModeForItemAt indexPath: IndexPath) -> MagazineLayoutItemSizeMode {\n    let widthMode = MagazineLayoutItemWidthMode.halfWidth\n    let heightMode = MagazineLayoutItemHeightMode.dynamic\n    return MagazineLayoutItemSizeMode(widthMode: widthMode, heightMode: heightMode)\n  }\n\n  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, visibilityModeForHeaderInSectionAtIndex index: Int) -> MagazineLayoutSupplementaryViewVisibilityMode {\n    return .visible(heightMode: .dynamic, pinToVisibleBounds: true)\n  }\n\n  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, visibilityModeForFooterInSectionAtIndex index: Int) -> MagazineLayoutSupplementaryViewVisibilityMode {\n    return .visible(heightMode: .dynamic, pinToVisibleBounds: false)\n  }\n\n  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, visibilityModeForBackgroundInSectionAtIndex index: Int) -> MagazineLayoutBackgroundVisibilityMode {\n    return .hidden\n  }\n\n  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, horizontalSpacingForItemsInSectionAtIndex index: Int) -> CGFloat {\n    return  12\n  }\n\n  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, verticalSpacingForElementsInSectionAtIndex index: Int) -> CGFloat {\n    return  12\n  }\n  \n  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetsForSectionAtIndex index: Int) -> UIEdgeInsets {\n    return UIEdgeInsets(top: 0, left: 8, bottom: 24, right: 8)\n  }\n\n  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetsForItemsInSectionAtIndex index: Int) -> UIEdgeInsets {\n    return UIEdgeInsets(top: 24, left: 0, bottom: 24, right: 0)\n  }\n  \n}\n```\n\nIf you've followed the steps above, you should have a working `UICollectionView` using `MagazineLayout`! If you'd like to work with a pre-made example, check out the included example project, and [the instructions for using it](#example-app).\n\n\n## Contributions\n`MagazineLayout` welcomes both fixes, improvements, and feature additions. If you'd like to contribute, open a pull request with a detailed description of your changes. \n\nAs a rule of thumb, if you're proposing an API breaking change or a change to existing functionality, consider proposing it by opening an issue, rather than a pull request; we'll use the issue as a public forum for discussing whether the proposal makes sense or not.\n\n## Maintainers\nBryan Keller\n- https:\u002F\u002Fgithub.com\u002Fbryankeller\n- [https:\u002F\u002Fx.com\u002Fblk19_](https:\u002F\u002Fx.com\u002Fblk19_)\n\nBryn Bodayle\n- https:\u002F\u002Fgithub.com\u002Fbrynbodayle\n- https:\u002F\u002Fx.com\u002Fbrynbodayle\n\nIf you or your company has found `MagazineLayout` to be useful, let us know!\n\n## Contributors\n`MagazineLayout` would not have been possible without the contributions and support from several of my colleagues at Airbnb. Bryn Bodayle, in particular, has reviewed every PR since `MagazineLayout`'s inception, as well as helped talk through and solve countless tricky `UICollectionView` and `UIKit` issues.\n\nI'd also like to thank the following people, who have all helped pave the way for `MagazineLayout` to be successful:\n- Laura Skelton\n- Eric Horacek\n- Tyler Hedrick\n- Michael Bachand\n- Xiao Pan\n- Yong Li\n- Luke Hiesterman\n- Jordan Harband\n\n## License\n\n`MagazineLayout` is released under the Apache License 2.0. See [LICENSE](LICENSE) for details.\n","MagazineLayout 是一个用于垂直滚动网格和列表布局的集合视图布局工具。它基于 Swift 语言开发，支持多种高级布局特性，如根据总宽度的比例设定项目宽度、仅垂直方向上的自适应大小调整、单个项目级别的自适应设置偏好、可隐藏或显示的头部和尾部、以及可定制的插入与删除动画等。这些功能使得 MagazineLayout 成为构建复杂且美观界面的理想选择，尤其适用于需要高度自定义和动态内容展示的应用场景，比如旅游预订应用中的搜索结果页、个人收藏列表或是详细信息页面。此外，该项目还提供了通过 Swift Package Manager 和 Carthage 进行集成的支持，方便开发者快速上手使用。",2,"2026-06-11 03:09:56","top_language"]