Git Product home page Git Product logo

magazinelayout's Introduction

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.

magazinelayout's People

Contributors

bryankeller avatar dfed avatar diegorozen avatar elfredpagan avatar elliot-vu avatar fruitcoder avatar jordanekay avatar nate-sentjens avatar ra1028 avatar rebeccachin avatar romanpodymov avatar snagappan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

magazinelayout's Issues

Respect safe area

Currently the layout only looks at the collection view's contentInset but ignores the safe area. The result is that the cells are inset to the right which caused them to be cutoff (on the right side). See

screenshot 2019-01-08 at 16 58 12

One way to solve this would be to look at the adjustedContentInset if available and base the calculations on that. It's also possible to extend the MagazineLayoutItemWidthMode to have different options as to whether the safe area should be respected or not (this could enable respecting the safe area on a section/item basis although I think that's rarely what you want).

does it support scale a UIImageView proportionally?

Hi,
I tried to add a custom cell with .xib.
show picture and text.
how can let imageview scale proportionally?
I' ve try to add ration to imageview, and use layoutIfNeeded()
but console log show waring
(
"<NSLayoutConstraint:0x60000359ed00 UIImageView:0x7fec1fe33890.width == 0.733333*UIImageView:0x7fec1fe33890.height (active)>",
"<NSLayoutConstraint:0x60000359ed50 H:[UIImageView:0x7fec1fe33890]-(10)-| (active, names: '|':UIView:0x7fec1fe31db0

Please let me know if I'm doing something wrong.

Inserted cell is overlayed on top of existing cells

Describe the bug
With quite simple setup(two sections - first one is empty, second has one cell with static height), after inserting an item into the collection view the inserted cell is overlayed over the existing one.

To Reproduce
Steps to reproduce the behavior:

  1. Set collection view's contentInsets to .zero
  2. Setup data source with one empty section(first) and another section with 1 item
  3. Insert an item into the first section

Expected behavior
Existing cell is pushed down by inserted cell.

Actual behaviour
Inserted cell is overlaid on top of existing cell.

Screenshots
magazinelayout_i MagazineLayout.zip nsert_issue

Smartphone (please complete the following information):

  • iPhone 8 Simulator(iOS 12), iPad(6th generation) Simulator(iOS 12), iPad (6th generation)(iOS 11)

Additional context
If collection view's contentInsets have any kind of horizontal margins(even UIEdgeInsets(top: 0, left: 1, bottom: 0, right: 1)) - it does seems to help. Not sure why this is happening, I modified example app to show the issue. MagazineLayout.zip

Crash when adding and deleting items using `RxDataSource`

Describe the bug
When using RxDataSource to add or delete items, the delegate of MagazineLayout will access the previous data source, causing the array to go out of bounds.
Translation: Google

To Reproduce
the code:

let animatedDataSource = RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<String, Int>>(configureCell: {_, _, _, _  in return UICollectionViewCell()})
let dataSource = PublishSubject<[AnimatableSectionModel<String, Int>]>()
dataSource.bind(to: collectionView.rx.items(dataSource: animatedDataSource)).disposed(by: disposeBag)
/// It's no problem
dataSource.onNext([.init(model: "1", items: [1,2,3]), .init(model: "2", items: [1,2,3,4])])
/// but...
/// Using `MagazineLayout` data source will out of bounds! !
dataSource.onNext([.init(model: "1", items: [1,2,3]), .init(model: "2", items: [1,2])])

Use RxCollectionViewSectionedReloadDataSource all is well

Expected behavior
I hope to use RxCollectionViewSectionedAnimatedDataSource also no problem

Screenshots
wS02TA.png

Smartphone (please complete the following information):

  • Device/Simulator: [All]
  • OS: [iOS13]
  • Version [1.6.0]

Additional context
Crash method
collectionView(_:layout:sizeModeForItemAt:)

Layout constraints are breaking when aspect ratio on UIImageView is set

Describe the bug
Well i have collection view cell which contains UIImageView which needs to have 16:9 aspect ratio and two labels below UIImageView. UIImageView needs to be 16:9 and labels below could have dynamic height. If i set aspect ratio on UIImageView 16:9 then i get in debug console this warning:
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60000274edf0 UIImageView:0x12a6287c0.height == 50 (active)>

Besides that when i fast scroll collection view to the bottom layout being totally messed up.
I tried also to put low priority for this aspect ratio but then i don't get properly layout(i don't see 2 labels below UIImageView) and when i scroll collection view then cells get bigger(too much).

To Reproduce
Steps to reproduce the behaviour:

  1. Create UICollectionViewCell which inherits MagazineLayoutCollectionViewCell.
  2. Create UIImageView and two labels below UIImageView.
  3. Set 16:9 aspect ratio on UIImageView.
  4. Setup constraints between 2x UILabel and UIImageView vertically.

Expected behavior
Don't get breaking layout constraints when set aspect ratio 16:9 on UIImageView and fast scrolling should not mess up layout.

Smartphone (please complete the following information):

  • Device/Simulator: Apple TV 4K
  • OS: tvOS

Header and Footer are not visible during the section insertion animation

Header and Footer are not visible during the section insertion animation, they appear only when the animation is over

To Reproduce
Steps to reproduce the behavior:

  1. Go to example app
  2. Click on delete all content using the refresh button
  3. Insert an item. It will also cause the section insertion
  4. See error

Expected behavior
Header and footer appear simultaneously with the item.

Screenshots
header:footer bug

Smartphone (please complete the following information):

  • Device/Simulator: Both
  • OS: tested in 12-14 ios version

Podspec swift version must be updated.

Hi Dears,
The framework's swift version has been updated to 5.0. however, the swift version in pospec needs to be updated to the latest swift version.

s.swift_version = '4.0'

the pod install might face the error where Podfile has specified swift version support.
supports_swift_versions '>= 5.0'

[!] Unable to determine Swift version for the following pods:

- `MagazineLayout` does not specify a Swift version (`4.0`) that is satisfied by any of targets integrating it.

please update the swift version in pod spec file.

Self-sizing mode cause scroll jumping

Smartphone:

  • Device/Simulator: [iPhone6]
  • OS: [iOS 12]
  • For self-sizing

Scroll to the last indexPath, rotate device and begin to scroll to top. Scroll will start to jump.

Reason:
Imagine we have 1000 cell (1000 item). We are at the last cell. On invalidateLayout UICollectionViewLayout will calculate only last for example 10-20 cells and other 980 cells height will be same as estimatedSize.height. So contentSize.height will be less. While we start to scroll top layout will start calculate new visible cells size, contentSize.height will grow and scroll starts to jump. -> This problem is encountered on UICollectionViewFlowLayout too. I think because calculating 1000 cells size is bad for performance (for 60 fps). And it may be also 10.000 cells. And calculating all cells to define true size of contentSize is bad solution.

Because of you caching cell sizes, jumping is not huge than in UICollectionViewFlowLayout

untitled480trim

Hi-res: https://youtu.be/LgQehZKhp1Q

Section insets

Is your feature request related to a problem? Please describe.
UICollectionViewFlowLayout have frustrated me quite a few times with it's way to specify sections insets, which are not section insets but rather item insets. I guess item insets in MagazineLayout and their behaviour is modelled after UICollectionViewFlowLayout.
So my issue is - how do I specify section insets for a section? In a way that header and footer would also respect them.

Describe the solution you'd like
I'd suggest adding a notion of sectionInsets to MagazineLayout sections could help this

Describe alternatives you've considered
Alternative way would be to add extra space at the top to section header(or the bottom of section footer) to make it appear as there is spacing between sections, but that appears more like a hack rather than a solution.

Nib based cells dynamic sizing issues

Describe the bug
When using a nib based cell, dynamic heights behave erratically

To Reproduce
Steps to reproduce the behavior:

  1. See https://github.com/diegorozen/MagazineLayout/tree/nib_based_cells_bug, just the same sample app changed to use nibs
  2. Initial cells height doesn't get constrained. The layout recovers after scrolling

Additional context
The issue can be somewhat mitigated by adding a setNeedsLayout() + LayoutIfNeeded() before MagazineLayoutCollectionViewCell.swift Line 59. I still see eventual glitches after rotating several times. Am I missing something in terms of configuration?

How to add a custom cell with .xib?

Hi, thanks for this nice release but I have the following problem:
I tried to add a custom cell with .xib design file and I cannot get it to work.
If I try to add views programatically like the example, it works like a charm.
Is it possible to add cells like this or just programatically? Should I do something different from the example project?
I can see that my custom cell is rendering, but empty, none of the designed subviews are getting rendered.

Please let me know if I'm doing something wrong.

Thanks!

Scrolling to item when using dynamic cell height

Describe the bug
UICollectionView.scrollToItem(at:at:animated:) doesn't scroll to the correct position when using dynamic cell height. When using static cell height, it scrolls to the correct position. Sample project here.

To Reproduce
Steps to reproduce the behavior:

  1. Setup collection view with one cell type
  2. Call collectionView.scrollToItem(at: indexPath, at: .centeredVertically, animated: animated)
  3. Cell at indexPath is not centered vertically, but ends up somewhere in the top half of the collection view. See the attached screenshot where Cell 27 should be centered vertically.

Expected behavior
Cell is centered vertically in the collection view.

Screenshots
screenshot

Smartphone:

  • iPhone 8 (device), iPhone 11 Pro (simulator)
  • iOS 13.3.1

Add support for width modes other than 1/n

Currently, all widths are represented as a fraction 1/n of the width. How would you suggest to add support for something like |-- 2/3 --|-- 1/3 --| : Redefining fractionalWidth to use a floating point number, or adding a 'numerator' associated value to that enum?

Thanks!

reload data layout been change

Hi there,
I got a bug: when reload data, layout has been change, it should be same after reload data

wechatimg77
wechatimg78
wechatimg79

I coded with example project by change the cell to different type by code:
struct ItemInfo {

let type:ItemType
let sizeMode: MagazineLayoutItemSizeMode
let text: String
let attributeString: NSMutableAttributedString
let color: UIColor

}

func set(_ itemInfo: ItemInfo) {
//
label.textColor = .white
label.numberOfLines = 0
contentView.addSubview(label)

image.translatesAutoresizingMaskIntoConstraints = false
label.translatesAutoresizingMaskIntoConstraints = false

contentView.addSubview(image)

if itemInfo.type == .type1{
    NSLayoutConstraint.activate([
        image.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0),
        //button.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -4),
        image.widthAnchor.constraint(equalToConstant: 50),
        image.heightAnchor.constraint(equalToConstant: 50),
        image.topAnchor.constraint(equalTo: contentView.topAnchor),
        //button.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
        ])
    
    NSLayoutConstraint.activate([
        label.leadingAnchor.constraint(equalTo: image.trailingAnchor, constant: 4),
        label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0),
        label.topAnchor.constraint(equalTo: contentView.topAnchor),
        label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
        ])
    
}else{
    NSLayoutConstraint.activate([
        //image.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0),
        image.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -4),
        image.widthAnchor.constraint(equalToConstant: 50),
        image.heightAnchor.constraint(equalToConstant: 50),
        image.topAnchor.constraint(equalTo: contentView.topAnchor),
        //button.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
        ])
    
    NSLayoutConstraint.activate([
        label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 4),
        label.trailingAnchor.constraint(equalTo: image.leadingAnchor, constant: 0),
        label.topAnchor.constraint(equalTo: contentView.topAnchor),
        label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
        ])
    
}
//

label.text = itemInfo.text
label.attributedText = itemInfo.attributeString
contentView.backgroundColor = itemInfo.color

}

ContentOffset is adjusted incorrectly when delete cells

Scroll collection view to the bottom, then delete some cells, the behavior is unexpected. The animation of deleting cell is great, but the contentOffset is adjusted without any animation, the collection view just jumps.

Screenshots
If applicable, add screenshots to help explain your problem.
image

Smartphone (please complete the following information):

  • Simulator: iPhone 11
  • iOS 13

Size calculated with default height when collection is reloaded.

Describe the bug
Given a collection of items using .dynamic height, calling reloadData() causes cells to be re-sized using the default height of 150 and not their actual height.

To Reproduce
Steps to reproduce the behavior:

  1. Download project:
    MagazineLayout.zip Or visit Gist: ViewController.swift and add into a project
  2. Once running, scroll to the bottom and lick on an item
  3. See the scroll offset jump

Expected behavior
No change in scroll offset

Screenshots
N/A - otherwise layout is good.

Smartphone (please complete the following information):

  • Device/Simulator: Device and Simulator
  • OS: 9+
  • Version v1.4.5 and possibly earlier

Additional context
This does not happen when providing a static height.

Collection view flickering on bounce

Describe the bug
When you're at the top of the collection view and you continue scroll up, the collection view flickers during the bounce. It only happens on the first bounce after the collection view is loaded. If you scroll down and back up again everything looks fine.

To Reproduce
Steps to reproduce the behavior:

  1. Download sample project: https://www.dropbox.com/s/iuj8pd0sz9p5pb0/MagazineLayoutIssue.zip?dl=0
  2. Build & Run
  3. Navigate to first tab
  4. Scroll to top
  5. Scroll past the top to invoke bounce
  6. Release to return to yOffset 0

Expected behavior
A smooth animation of the collection view back to yOffset 0

Screenshots
Bounce Glitch

Smartphone (please complete the following information):

  • Device/Simulator: iPhone 8 simulator, iPhone XS Max
  • OS: iOS 12.2
  • Version MagazineLayout 1.2.4

Animating header resize

What's the appropriate way to animate the resize of a header?

I'm currently trying using:

UIView.animate(withDuration: 0.3) {
  collectionView?.collectionViewLayout.invalidateLayout()
}

Add support for items order by zIndex

UICollectionViewLayout supports the ability to order its elements (cells, supplementary views & decoration views) not only by their IndexPath, so items with small IndexPath will be in the collectionView subviews' stack in front of item with bigger IndexPath. This is required, for example, if you want to have an animation on a cell's content which will hover above all other cells.

It'd be great if MagazineLayout will support changing item's zIndex.

I can implement it & make a pull request. I think that the best place to get this data is part of the delegate call for 'MagazineLayoutItemSizeMode' (maybe we need to change its name to MagazineLayoutItemSizeAndOrderMode)

Add Swift PM support ๐Ÿ“ฆ

Is your feature request related to a problem? Please describe.
When setting up a new project I wanted to add MagazineLayout via the new Swift PM integration in Xcode 11. This was not possible because this repository lacks a Package.swift.

https://github.com/airbnb/MagazineLayout has no Package.swift manifest for version 1.5.0

Because of this I had to use Cocoapods/Carthage.

Describe the solution you'd like
I'd like for MagazineLayout to have a Package.swift file so that Xcode picks it up as a Swift package.

Alignment bug on simultaneous insertion and refresh

Describe the bug
An attempt to make two simultaneous changes in collection view causes UI glitches

To Reproduce
Add the next piece of code to the performBatchUpdates

          let itemInfo1 = ItemInfo(
              sizeMode: state.sizeMode,
              text: "Test test test test test test",
              color: state.color)
          self?.dataSource.removeItem(atItemIndex: 3, inSectionAtIndex: 0)
          self?.dataSource.insert(itemInfo1, atItemIndex: 3, inSectionAtIndex: 0)
          self?.collectionView.reloadItems(at: [IndexPath(item: 3, section: 0)])

Run the project and insert an item
You will see the effect that is described in the video. Cells will start animation from the wrong position and finish it in the wrong place

Expected behavior
All the items will reorder and update according to their positions

Screenshots
https://www.dropbox.com/s/rzfa5g8qbiamlcm/Missalignmets.mov?dl=0
Screenshot 2020-03-27 at 00 58 11
Smartphone (please complete the following information):

  • Device/Simulator: iPhone 11/XR (Simulator)
  • OS: iOS12/13

Additional context
You did a fantastic job.

Collection View Header

Is your feature request related to a problem? Please describe.
A lot of collection views nowadays requires a cell to be used as "header" of a collection view when we display detail information of a previous list of items.
An example in airbnb would be a home.

HomePDP
I have been working in a custom layout based on MagazineLayout for adding more powerful features to that top view. I think the most useful is to allow it to resize to a given compact size when scrolling and fix the view to the top when bouncing (making increase the height).

Describe the solution you'd like
Add a new type of supplementary view as a main header and customize its behaviour while scrolling

Examples:
This examples come from a real app I made myself
COMPACT SIZE -> Header bar size
IMG_1872

COMPACT SIZE -> 0 (Background resize)
IMG_1869

BOUNCING
IMG_1873

I would like to participate in the implementation if it's possible.

Why MagazineLayoutCollectionViewLayoutAttributes is `final`?

The problem
I use UICollectionView with custom layout in my app. I wanted to migrate to MagazineLayout. But I use custom UICollectionViewLayoutAttributes, so I need to somehow extend MagazineLayoutCollectionViewLayoutAttributes to use with my cells.
But since MagazineLayoutCollectionViewLayoutAttributes is final - I can't just subclass it.

The solution I'd like
Make it non-final

Alternatives I've considered
a) Make MagazineLayout non-final, and let subclasses provide type of UICollectionViewLayoutAttributes which conforms to protocol:

protocol IMagazineLayoutCollectionViewLayoutAttributes {
    public var shouldVerticallySelfSize: Bool { get set}
}

Make MagazineLayoutCollectionViewLayoutAttributes conform this protocol :

extension MagazineLayoutCollectionViewLayoutAttributes: IMagazineLayoutCollectionViewLayoutAttributes {}

b) Use objc runtime to extend MagazineLayoutCollectionViewLayoutAttributes
c) Add property into MagazineLayoutCollectionViewLayoutAttributes (some key-value collection), to hold additional parameters

Footer support

Layout currently supports headers, would be nice to add footer support as well. It, of course, can be emulated by appending an extra cell at the bottom of a section, but footers are much more convenient to work with.
Footer layout should be quite similar to headers', is there any obstacle that prevents them from being supported?

Waterfall

It is possible to create a waterfall layout using MagazineLayout? I have cells with different height, but they're being accommodated in the same Y-AXIS.

iOS 9 support

Is there a chance that the library get iOS 9 support? There seems to be some iOS 9-specific comments in the code, so there was certainly an intention to make it work there. What was the obstacle there?

Feature Request/Discussion - Pinned headers across sections

I just want to say upfront thanks so much for open sourcing this library. They say naming variables and caching data is the hardest thing in computer science, but I'd argue dealing with collection view layouts on iOS has made me pull out more hair over the years than anything else I can think of.

I have one feature request, and it's something I've been messing with over the last few days but this is your library, and I want to make sure that everyone here is happy with the idea/approach.

Is your feature request related to a problem? Please describe.
Section headers can be pinned to the top of the screen like table view headers can. This is fantastic! And more than enough in most circumstances. However, I have a use case where, instead of the section header scrolling off screen when we get to the next section, I want it to stay stuck until I encounter the next pinned section.

So let's say section 0 and section 5 are pinned. I want the header in section 0 to stay on screen while scrolling through sections 0 until 4, only scrolling off once we reach section 5.

Describe the solution you'd like
Currently in MagazineLayoutHeaderVisibilityMode there's a boolean pinToVisibleBounds property. I propose changing this to be an enum to be something like -

public enum MagazineLayoutPinningMode {
    case none
    case sectionVisibleBounds
    case visibleBounds
}

As mentioned in the opening, naming things are hard, so I'm not married to any of these. But essentially, none is the same is the current pinToVisibleBounds being set to false, sectionVisibleBounds is the current truebehaviour, and visibleBounds would be this new case I'm talking about.

Describe alternatives you've considered
One alternative could be to instead just allow the ability to create a header (similar to table view headers, not section headers) which could have pinning behaviour on it. In some ways this might be preferable as it doesn't muddy the implementation of section headers (which already behave as you'd probably expect based on how they work on table view headers), and doesn't break existing API. But this is also a little less flexible.

Additional context
This has come about because I'm working on an app which has a component based API (I'm guessing Airbnb has something similar if they've gone to the trouble of building this library) where all content is driven by the server. Each component is part of its own section, and I want video players to be able to stick around on screen. The option of putting everything in one section and only having one sticky header was investigated but in the end this made things even more tricky to deal with.

Delegate not being triggered

Describe the bug
I created my CollectionView from the storyboard, connected to the Controller, added to the delegate; registered the MagazineLayoutCollectionViewCell, but delegate functions are not getting triggered.

`layoutAttributesForItem(at:)` crashes when given an empty index path

Describe the bug
layoutAttributesForItem(at:) crashes when given an empty index path

To Reproduce
Steps to reproduce the behavior:

  1. Call layout.layoutAttributesForItem(at: IndexPath())

Expected behavior
Expected it to be treated as IndexPath(item: 0, section: 0), mirroring the UICollectionViewFlowLayout behavior
Edit: just found a difference. IndexPath() returns the first header, while IndexPath(item: 0, section: 0) returns the first cell

Smartphone (please complete the following information):

  • Device/Simulator: iPad Mini 2nd gen
  • OS: iOS 13.3.1
  • Version [e.g. 22]: 1.6.0

Additional context
Note that although common usage (especially with UICollectionViews) is that all IndexPaths have 2 items, they actually can have any number of items. row, item, and section are just convenience accessors/initializers that assume there are 2 items in the IndexPath, which may not be the case

This was in our existing (non-MagazineLayout) code, used to scroll to top

iOS 12 Compatibility?

It appears there's a few issues with iOS 12.

One of which being a crash which may be related to #77.
Also some trouble with headers especially when pinning to bounds.
I can provide more details here, however should this library have support for iOS12?

Refresh Control Causes Sticky Header to Stay at Incorrect Position

Describe the bug
If a refreshcontrol is added to the collectionView, pulling to refresh will cause the sticky header to pin to incorrect position, instead of top of the screen

To Reproduce
Steps to reproduce the behavior:

  1. Go to MagazineLayoutExample
  2. Go to ViewController.swift
  3. Inside viewDidLoad(), add collectionView.refreshControl = .init()
  4. Run the project and pull to refresh, then scroll down
  5. You will see the same behavior as shown in the attached gif

Expected behavior
The sticky header is expected to pin to the top even when the refresh control is active

Screenshots
fdg

Smartphone

  • Simulator: iPhone 8 Plus
  • OS: 13.2.2
  • Version: Latest (1.5.2)

SPM support

Describe the solution you'd like
SPM support

Additional context
Moving forward SPM support is preferable

Swift 5.1 support.

Describe the solution you'd like
Supporting Swift 5.1 so that module stability can be achieved.

Additional context
The current MagazineLayout target is still using Swift 4. This means every time Xcode updates with a newer swift version, we'll have to rebuild MagazineLayout. Updating to Swift 5.1 will make use of the module stability to allow us rebuild only when we really want to.

Add support for sticky section header

Is your feature request related to a problem? Please describe.
Just like UICollectoinViewFlowLayout, when scrolling the collectionView, the section headers will scroll beyond the visible area while the items in this section are still visible. Is it better for MagazineLayout to support this sticky section header just like the UITableView.

Describe the solution you'd like
Is it possible to add a notion of enableStickySectionHeader to MagazineLayout or add a new delegate method shouldStickyHeaderInSectionAtIndex to control which section should has this ability.

Thanks

Collection view with UIRefrshControl flashing

Describe the bug
I've found weird flickering/flashing animation when collection view is updating using refresh control. Also this issue can be reproduced only on OS 13 and with dynamic height of "sizeModeForItemAt".

To Reproduce
Steps to reproduce the behavior:

  1. Add collection view with MagazineLayout().
  2. Subscribe on UICollectionViewDelegateMagazineLayout
  3. Select "fullWidth(respectsHorizontalInsets: true)", "heightMode: .dynamicAndStretchToTallestItemInRow" for the sizeModeForItemAt
  4. Create MagazineLayoutCollectionViewCell
  5. Add Refresh control to the collection view
  6. Add target to refresh control with collectionView.reloadData()
  7. Pull to refresh and see some collectionView content is fleshing

Screenshots
If applicable, add screenshots to help explain your problem.

Smartphone (please complete the following information):

  • Device/Simulator: Any simulator or device
  • OS: Can be reproduced only for iOS 13
  • Version of the MaganizeLayout 1.5.2

Additional context
Here is the test project with everything that needed to reproduce this bug:
https://drive.google.com/file/d/1gIHftMHvhpGY4IZgqpM3cUxHtcVnU-Lh/view?usp=sharing
or modified MagazineLayout example:
https://drive.google.com/file/d/1mp9rF7f_X6BWdQRjos18JptzfPrUcjVf/view?usp=sharing

Here is the video with bug:
https://drive.google.com/file/d/1gHRjcuSZz9k7pGqZqxr9GAjF4MC_2-DR/view?usp=sharing

Could not dequeue a view of kind --- Delegate mixing Header, Footer and Background

Describe the bug
I'm trying to implement a background view for section; I created a Background class, but when I set BG to .visible; it tries to dequeue header view as the background.
Here is my code:

    collectionView.register(
      UINib.init(nibName: "ItemExploreViewCell", bundle: Bundle.main),
      forCellWithReuseIdentifier: "ItemExploreViewCell")
    
    collectionView.register(
      UINib.init(nibName: "UserProfileView", bundle: Bundle.main),
      forSupplementaryViewOfKind:  MagazineLayout.SupplementaryViewKind.sectionHeader,
      withReuseIdentifier: "UserProfileView")

    collectionView.register(
      Background.self,
      forSupplementaryViewOfKind: MagazineLayout.SupplementaryViewKind.sectionBackground,
      withReuseIdentifier: Background.description())

Expected behavior
Dequeue my Background class as BG, not my header.

Additional context
Console output:
could not dequeue a view of kind: MagazineLayoutSupplementaryViewKindSectionBackground with identifier UserProfileView

Expose MagazineLayout.Default enum as public

While I was building an RxSwift extension for MagazineLayout (which can be discussed in a different topic), I needed to provide sensible defaults for the cases when a forwarding delegate was not defined. Those defaults have already being defined by the lib in MagazineLayout.Default, but they are not visible outside the library.

Since that enum doesn't expose any internal implementation details, it would be helpful to mark it as public

Thanks,
Diego

Reloading collectionview does not maintain content offset

Describe the bug
When reloadData() is called, the collectionView sometimes doesn't preserve the scrolling position, causing the collectionView to jump abruptly

To Reproduce
Steps to reproduce the behavior:

  1. Open and run the stock example project (workspacce) under MagazineLayoutExample
  2. Please see the attached gif to reproduce this issue
    Note: this doesn't occur every time, you need to adjust scrolling position before reloading the data if you cannot reproduce it at the beginning

Expected behavior
The screen should maintain its position after reloading data

Screenshots

Untitled

Smartphone (please complete the following information):

  • Simulator: iPhone 11 Pro Max
  • OS: iOS 13.0
  • Version: current

Additional context
Not sure if this could be a bug in UICollectionView rather than MagazineLayout, please advise.

UIKit layout issues with square collection cells

Describe the bug
I have a collection view that has a fractional width relative to the View Controller width. 0.4 in Compact size class and 0.3 in Regular. I want my cells to be square in the collection view.

My cell is a Xib with a container view, pinned to the edges of the cell, with 1:1 aspect ratio constraint.

Setup code

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
                        sizeModeForItemAt indexPath: IndexPath) -> MagazineLayoutItemSizeMode {
        switch collectionView.traitCollection.horizontalSizeClass {
        case .compact:
            return MagazineLayoutItemSizeMode(widthMode: .fullWidth(respectsHorizontalInsets: true),
                                              heightMode: .dynamic)
        case .regular:
            return MagazineLayoutItemSizeMode(widthMode: .halfWidth, heightMode: .dynamicAndStretchToTallestItemInRow)
        case .unspecified:
            preconditionFailure()
        @unknown default: //swiftlint:disable:this switch_default_case
            preconditionFailure()
        }
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
                        insetsForSectionAtIndex index: Int) -> UIEdgeInsets {
        UIEdgeInsets(...) //non-zero
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
                        verticalSpacingForElementsInSectionAtIndex index: Int) -> CGFloat {
        10
    }

Expected behavior
Cells are square in the collection view

Actual behavior

UIKit reports unsatisfiable constraints:

(
    "<NSLayoutConstraint:0x600003317570 UIView:0x7fe406f6a510.width == UIView:0x7fe406f6a510.height   (active)>",
    "<NSLayoutConstraint:0x600003315d60 UILayoutGuide:0x6000029bd340'UIViewSafeAreaLayoutGuide'.bottom == UIView:0x7fe406f6a510.bottom + 3   (active)>",
    "<NSLayoutConstraint:0x600003331bd0 UILayoutGuide:0x6000029bd340'UIViewSafeAreaLayoutGuide'.trailing == UIView:0x7fe406f6a510.trailing + 3   (active)>",
    "<NSLayoutConstraint:0x600003333250 UIView:0x7fe406f6a510.leading == UILayoutGuide:0x6000029bd340'UIViewSafeAreaLayoutGuide'.leading + 3   (active)>",
    "<NSLayoutConstraint:0x600003333890 UIView:0x7fe406f6a510.top == UILayoutGuide:0x6000029bd340'UIViewSafeAreaLayoutGuide'.top + 3   (active)>",
    "<NSLayoutConstraint:0x600003377480 'UIView-Encapsulated-Layout-Height' SquareCollectionCell.height == 150   (active, names: SquareCollectionCell:0x7fe406f6a100 )>",
    "<NSLayoutConstraint:0x600003376080 'UIView-Encapsulated-Layout-Width' SquareCollectionCell.width == 202   (active, names: SquareCollectionCell:0x7fe406f6a100 )>",
    "<NSLayoutConstraint:0x600003321180 'UIViewSafeAreaLayoutGuide-bottom' V:[UILayoutGuide:0x6000029bd340'UIViewSafeAreaLayoutGuide']-(0)-|   (active, names: SquareCollectionCell:0x7fe406f6a100, '|':SquareCollectionCell:0x7fe406f6a100 )>",
    "<NSLayoutConstraint:0x60000331b9d0 'UIViewSafeAreaLayoutGuide-left' H:|-(0)-[UILayoutGuide:0x6000029bd340'UIViewSafeAreaLayoutGuide'](LTR)   (active, names: SquareCollectionCell:0x7fe406f6a100, '|':SquareCollectionCell:0x7fe406f6a100 )>",
    "<NSLayoutConstraint:0x600003333b10 'UIViewSafeAreaLayoutGuide-right' H:[UILayoutGuide:0x6000029bd340'UIViewSafeAreaLayoutGuide']-(0)-|(LTR)   (active, names: SquareCollectionCell:0x7fe406f6a100, '|':SquareCollectionCell:0x7fe406f6a100 )>",
    "<NSLayoutConstraint:0x6000033143c0 'UIViewSafeAreaLayoutGuide-top' V:|-(0)-[UILayoutGuide:0x6000029bd340'UIViewSafeAreaLayoutGuide']   (active, names: SquareCollectionCell:0x7fe406f6a100, '|':SquareCollectionCell:0x7fe406f6a100 )>"
)

Environment:

  • Device/Simulator: iPad Pro 11 inch
  • OS: 13.3
  • SDK: 1.5.5

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.