• Stars
    star
    3,232
  • Rank 13,273 (Top 0.3 %)
  • Language
    Swift
  • License
    Apache License 2.0
  • Created over 5 years ago
  • Updated 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A collection view layout capable of laying out views in vertically scrolling grids and lists.

MagazineLayout

A collection view layout capable of laying out views in vertically scrolling grids and lists.

Swift Package Manager compatible Carthage compatible Version License Platform Swift

Introduction

MagazineLayout is a UICollectionViewLayout subclass for laying out vertically scrolling grids and lists of items. Compared to UICollectionViewFlowLayout, MagazineLayout supports many additional features:

  • Item widths based on a fraction of the total available width
    • Full width for a list layout (similar to UITableView)
    • Half-width, third-width, etc. for a grid layout
  • Self-sizing in just the vertical dimension
  • Per-item self-sizing preferences (self-size and statically-size items anywhere in your collection view)
  • Self-sizing headers and footers
  • Hiding or showing headers and footers on a per-section basis
  • Pinned (sticky) headers and footers
  • Section backgrounds that can be hidden / visible on a per-section basis
  • Customizable insert and delete animations for items and supplementary views

Other features:

  • Specifying horizontal item spacing on a per-section basis
  • Specifying vertical row spacing on a per-section basis
  • Specifying section insets on a per-section basis
  • Specifying item insets on a per-section basis

These 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:

Homes Search Experiences Search Wish List Home
Homes Search Experiences Search Wish list Home
Plus Home Plus Home Tour Trips Trip Detail
Plus Home Plus Home Tour Trips Trip Detail

Table of Contents

Example App

An example app is available to showcase and enable you to test some of MagazineLayout's features. It can be found in ./Example/MagazineLayoutExample.xcworkspace.

Note: Make sure to use the .xcworkspace file, and not the .xcodeproj file, as the latter does not have access to MagazineLayout.framework.

Using the Example App

When 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.

Example App

If 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.

Reload Menu No Items
Reload Menu No Items

From this menu, you can also reset the app back to the original sample data.

Adding a new item

To add a new item, tap the add icon in the navigation bar.

Add Item Screen

From 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.

Item configuration options:

  • Section index (will create a new section if one does not exist for the specified index)
  • Item index (position in the specified section)
  • Item content / text to be displayed in the item (this will change how tall the item is if using a .dynamic height mode)
  • Color to use for the background of the item
  • Width mode (controls how wide the item should be in relation to the available width)
  • Height mode (controls self-sizing behavior)

Add Item Animation

Deleting an item

To delete an item, simple tap on the item in the collection view. The item will be deleted with an animation.

Delete Item Animation

Getting Started

Requirements

  • Deployment target iOS 10.0+, tvOS 10.0+
  • Swift 4+
  • Xcode 10+

Installation

Carthage

To install MagazineLayout using Carthage, add github "airbnb/MagazineLayout" to your Cartfile, then follow the integration tutorial here.

CocoaPods

To install MagazineLayout using CocoaPods, add pod 'MagazineLayout' to your Podfile, then follow the integration tutorial here.

Usage

Once you've integrated the MagazineLayout into your project, using it with a collection view is easy.

Setting up cells and headers

Due to shortcomings in UIKit, MagazineLayout requires its own UICollectionViewCell and UICollectionReusableView subclasses:

  • MagazineLayoutCollectionViewCell
  • MagazineLayoutCollectionReusableView

These 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.

Alternatively, you can copy the implementation of preferredLayoutAttributesFitting(_:) for use in your custom cell and reusable view types, without subclassing from the ones MagazineLayout provides.

Importing MagazineLayout

At the top of the file where you'd like to use MagazineLayout (likely a UIView or UIViewController subclass), import MagazineLayout.

import MagazineLayout 

Setting up the collection view

Create your UICollectionView instance, passing in a MagazineLayout instance for the layout parameter.

let layout = MagazineLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)

Make sure to add collectionView as a subview, then properly constrain it using Auto Layout or manually set its frame property.

view.addSubview(collectionView)

collectionView.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([
  collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
  collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
  collectionView.topAnchor.constraint(equalTo: view.topAnchor),
  collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])

Registering cells and supplementary views

Register your cell and reusable view types with your collection view.

collectionView.register(MyCustomCell.self, forCellWithReuseIdentifier: "MyCustomCellReuseIdentifier")

// Only necessary if you want section headers
collectionView.register(MyCustomHeader.self, forSupplementaryViewOfKind: MagazineLayout.SupplementaryViewKind.sectionHeader, withReuseIdentifier: "MyCustomHeaderReuseIdentifier")

// Only necessary if you want section footers
collectionView.register(MyCustomFooter.self, forSupplementaryViewOfKind: MagazineLayout.SupplementaryViewKind.sectionFooter, withReuseIdentifier: "MyCustomFooterReuseIdentifier")

// Only necessary if you want section backgrounds
collectionView.register(MyCustomBackground.self, forSupplementaryViewOfKind: MagazineLayout.SupplementaryViewKind.sectionBackground, withReuseIdentifier: "MyCustomBackgroundReuseIdentifier")

Because 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 the data source

Now 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:

collectionView.dataSource = self

Configuring the delegate

Lastly, it's time to configure the layout to suit your needs. Like with UICollectionViewFlowLayout and UICollectionViewDelegateFlowLayout, MagazineLayout configured its layout through its UICollectionViewDelegateMagazineLayout.

To 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:

collectionView.delegate = self

Here's an example delegate implementation:

extension ViewController: UICollectionViewDelegateMagazineLayout {

  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeModeForItemAt indexPath: IndexPath) -> MagazineLayoutItemSizeMode {
    let widthMode = MagazineLayoutItemWidthMode.halfWidth
    let heightMode = MagazineLayoutItemHeightMode.dynamic
    return MagazineLayoutItemSizeMode(widthMode: widthMode, heightMode: heightMode)
  }

  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, visibilityModeForHeaderInSectionAtIndex index: Int) -> MagazineLayoutSupplementaryViewVisibilityMode {
    return .visible(heightMode: .dynamic, pinToVisibleBounds: true)
  }

  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, visibilityModeForFooterInSectionAtIndex index: Int) -> MagazineLayoutSupplementaryViewVisibilityMode {
    return .visible(heightMode: .dynamic, pinToVisibleBounds: false)
  }

  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, visibilityModeForBackgroundInSectionAtIndex index: Int) -> MagazineLayoutBackgroundVisibilityMode {
    return .hidden
  }

  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, horizontalSpacingForItemsInSectionAtIndex index: Int) -> CGFloat {
    return  12
  }

  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, verticalSpacingForElementsInSectionAtIndex index: Int) -> CGFloat {
    return  12
  }
  
  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetsForSectionAtIndex index: Int) -> UIEdgeInsets {
    return UIEdgeInsets(top: 0, left: 8, bottom: 24, right: 8)
  }

  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetsForItemsInSectionAtIndex index: Int) -> UIEdgeInsets {
    return UIEdgeInsets(top: 24, left: 0, bottom: 24, right: 0)
  }
  
}

If 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.

Contributions

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.

As 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.

Maintainers

Bryan Keller

Bryn Bodayle

If you or your company has found MagazineLayout to be useful, let us know!

Contributors

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.

I'd also like to thank the following people, who have all helped pave the way for MagazineLayout to be successful:

  • Laura Skelton
  • Eric Horacek
  • Tyler Hedrick
  • Michael Bachand
  • Xiao Pan
  • Yong Li
  • Luke Hiesterman
  • Jordan Harband

License

MagazineLayout is released under the Apache License 2.0. See LICENSE for details.

More Repositories

1

javascript

JavaScript Style Guide
JavaScript
141,845
star
2

lottie-android

Render After Effects animations natively on Android and iOS, Web, and React Native
Java
34,600
star
3

lottie-web

Render After Effects animations natively on Web, Android and iOS, and React Native. http://airbnb.io/lottie/
JavaScript
29,564
star
4

lottie-ios

An iOS library to natively render After Effects vector animations
Swift
24,897
star
5

visx

🐯 visx | visualization components
TypeScript
18,609
star
6

react-sketchapp

render React components to Sketch ⚛️💎
TypeScript
14,951
star
7

react-dates

An easily internationalizable, mobile-friendly datepicker library for the web
JavaScript
11,630
star
8

epoxy

Epoxy is an Android library for building complex screens in a RecyclerView
Java
8,426
star
9

css

A mostly reasonable approach to CSS and Sass.
6,869
star
10

hypernova

A service for server-side rendering your JavaScript views
JavaScript
5,824
star
11

mavericks

Mavericks: Android on Autopilot
Kotlin
5,741
star
12

knowledge-repo

A next-generation curated knowledge sharing platform for data scientists and other technical professions.
Python
5,432
star
13

ts-migrate

A tool to help migrate JavaScript code quickly and conveniently to TypeScript
TypeScript
5,307
star
14

aerosolve

A machine learning package built for humans.
Scala
4,790
star
15

DeepLinkDispatch

A simple, annotation-based library for making deep link handling better on Android
Java
4,356
star
16

lottie

Lottie documentation for http://airbnb.io/lottie.
HTML
4,289
star
17

ruby

Ruby Style Guide
Ruby
3,711
star
18

polyglot.js

Give your JavaScript the ability to speak many languages.
JavaScript
3,644
star
19

native-navigation

Native navigation library for React Native applications
Java
3,127
star
20

streamalert

StreamAlert is a serverless, realtime data analysis framework which empowers you to ingest, analyze, and alert on data from any environment, using datasources and alerting logic you define.
Python
2,825
star
21

infinity

UITableViews for the web (DEPRECATED)
JavaScript
2,809
star
22

airpal

Web UI for PrestoDB.
Java
2,760
star
23

HorizonCalendar

A declarative, performant, iOS calendar UI component that supports use cases ranging from simple date pickers all the way up to fully-featured calendar apps.
Swift
2,656
star
24

swift

Airbnb's Swift Style Guide
Markdown
2,239
star
25

synapse

A transparent service discovery framework for connecting an SOA
Ruby
2,067
star
26

Showkase

🔦 Showkase is an annotation-processor based Android library that helps you organize, discover, search and visualize Jetpack Compose UI elements
Kotlin
2,018
star
27

paris

Define and apply styles to Android views programmatically
Kotlin
1,894
star
28

AirMapView

A view abstraction to provide a map user interface with various underlying map providers
Java
1,861
star
29

react-with-styles

Use CSS-in-JavaScript with themes for React without being tightly coupled to one implementation
JavaScript
1,697
star
30

rheostat

Rheostat is a www, mobile, and accessible slider component built with React
JavaScript
1,692
star
31

binaryalert

BinaryAlert: Serverless, Real-time & Retroactive Malware Detection.
Python
1,382
star
32

epoxy-ios

Epoxy is a suite of declarative UI APIs for building UIKit applications in Swift
Swift
1,142
star
33

nerve

A service registration daemon that performs health checks; companion to airbnb/synapse
Ruby
942
star
34

okreplay

📼 Record and replay OkHttp network interaction in your tests.
Groovy
775
star
35

RxGroups

Easily group RxJava Observables together and tie them to your Android Activity lifecycle
Java
693
star
36

prop-types

Custom React PropType validators that we use at Airbnb.
JavaScript
672
star
37

react-outside-click-handler

OutsideClickHandler component for React.
JavaScript
603
star
38

ResilientDecoding

This package makes your Decodable types resilient to decoding errors and allows you to inspect those errors.
Swift
580
star
39

babel-plugin-dynamic-import-node

Babel plugin to transpile import() to a deferred require(), for node
JavaScript
575
star
40

kafkat

KafkaT-ool
Ruby
504
star
41

babel-plugin-dynamic-import-webpack

Babel plugin to transpile import() to require.ensure, for Webpack
JavaScript
500
star
42

chronon

Chronon is a data platform for serving for AI/ML applications.
Scala
479
star
43

babel-plugin-inline-react-svg

A babel plugin that optimizes and inlines SVGs for your React Components.
JavaScript
474
star
44

lunar

🌗 React toolkit and design language for Airbnb open source and internal projects.
TypeScript
461
star
45

BuckSample

An example app showing how Buck can be used to build a simple iOS app.
Objective-C
460
star
46

SpinalTap

Change Data Capture (CDC) service
Java
428
star
47

artificial-adversary

🗣️ Tool to generate adversarial text examples and test machine learning models against them
Python
390
star
48

dynein

Airbnb's Open-source Distributed Delayed Job Queueing System
Java
383
star
49

hammerspace

Off-heap large object storage
Ruby
364
star
50

trebuchet

Trebuchet launches features at people
Ruby
313
star
51

reair

ReAir is a collection of easy-to-use tools for replicating tables and partitions between Hive data warehouses.
Java
278
star
52

zonify

a command line tool for generating DNS records from EC2 instances
Ruby
270
star
53

ottr

Serverless Public Key Infrastructure Framework
Python
266
star
54

omniduct

A toolkit providing a uniform interface for connecting to and extracting data from a wide variety of (potentially remote) data stores (including HDFS, Hive, Presto, MySQL, etc).
Python
249
star
55

hypernova-react

React bindings for Hypernova.
JavaScript
248
star
56

smartstack-cookbook

The chef recipes for running and testing Airbnb's SmartStack
Ruby
244
star
57

interferon

Signaling you about infrastructure or application issues
Ruby
239
star
58

prop-types-exact

For use with React PropTypes. Will error on any prop not explicitly specified.
JavaScript
237
star
59

backpack

A pack of UI components for Backbone projects. Grab your backpack and enjoy the Views.
HTML
223
star
60

babel-preset-airbnb

A babel preset for transforming your JavaScript for Airbnb
JavaScript
222
star
61

goji-js

React ❤️ Mini Program
TypeScript
213
star
62

react-with-direction

Components to provide and consume RTL or LTR direction in React
JavaScript
192
star
63

stemcell

Airbnb's EC2 instance creation and bootstrapping tool
Ruby
185
star
64

hypernova-ruby

Ruby client for Hypernova.
Ruby
141
star
65

kafka-statsd-metrics2

Send Kafka Metrics to StatsD.
Java
135
star
66

optica

A tool for keeping track of nodes in your infrastructure
Ruby
134
star
67

sparsam

Fast Thrift Bindings for Ruby
C++
125
star
68

js-shims

JS language shims used by Airbnb.
JavaScript
123
star
69

browser-shims

Browser and JS shims used by Airbnb.
JavaScript
118
star
70

bossbat

Stupid simple distributed job scheduling in node, backed by redis.
JavaScript
118
star
71

nimbus

Centralized CLI for JavaScript and TypeScript developer tools.
TypeScript
118
star
72

lottie-spm

Swift Package Manager support for Lottie, an iOS library to natively render After Effects vector animations
Ruby
106
star
73

twitter-commons-sample

A sample REST service based on Twitter Commons
Java
103
star
74

is-touch-device

Is the current JS environment a touch device?
JavaScript
90
star
75

rudolph

A serverless sync server for Santa, built on AWS
Go
73
star
76

hypernova-node

node.js client for Hypernova
JavaScript
73
star
77

plog

Fire-and-forget UDP logging service with custom Netty pipelines & extensive monitoring
Java
72
star
78

cloud-maker

Building castles in the sky
Ruby
67
star
79

react-create-hoc

Create a React Higher-Order Component (HOC) following best practices.
JavaScript
66
star
80

vulnture

Python
65
star
81

react-native-push-notification

React Native Local and Remote Notifications
Java
64
star
82

deline

An ES6 template tag that strips unwanted newlines from strings.
JavaScript
63
star
83

react-with-styles-interface-react-native

Interface to use react-with-styles with React Native
JavaScript
63
star
84

sputnik

Scala
61
star
85

mocha-wrap

Fluent pluggable interface for easily wrapping `describe` and `it` blocks in Mocha tests.
JavaScript
54
star
86

react-with-styles-interface-aphrodite

Interface to use react-with-styles with Aphrodite
JavaScript
54
star
87

eslint-plugin-react-with-styles

ESLint plugin for react-with-styles
JavaScript
49
star
88

sssp

Software distribution by way of S3 signed URLs
Haskell
47
star
89

alerts

An example alerts repo, for use with airbnb/interferon.
Ruby
46
star
90

apple-tv-auth

Example application to demonstrate how to build Apple TV style authentication.
Ruby
44
star
91

airbnb-spark-thrift

A library for loadling Thrift data into Spark SQL
Scala
43
star
92

jest-wrap

Fluent pluggable interface for easily wrapping `describe` and `it` blocks in Jest tests.
JavaScript
39
star
93

billow

Query AWS data without API credentials. Don't wait for a response.
Java
38
star
94

gosal

A Sal client written in Go
Go
36
star
95

backbone.baseview

DEPRECATED: A simple base view class for Backbone.View
JavaScript
34
star
96

anotherlens

News Deeply X Airbnb.Design - Another Lens
HTML
33
star
97

eslint-plugin-miniprogram

TypeScript
33
star
98

react-component-variations

JavaScript
33
star
99

react-with-styles-interface-css

📃 CSS interface for react-with-styles
JavaScript
31
star
100

puppet-munki

Puppet
29
star