Git Product home page Git Product logo

zenangst / blueprints Goto Github PK

View Code? Open in Web Editor NEW
990.0 22.0 56.0 77.31 MB

:cyclone: Blueprints - A framework that is meant to make your life easier when working with collection view flow layouts.

License: Other

Swift 98.12% Ruby 0.25% Shell 0.05% C 0.09% Objective-C 1.50%
swift ios uicollectionview uicollectionviewlayout layout flowlayout uicollectionview-cell uicollectionview-animation tvos macos

blueprints's Introduction

Blueprints logo Blueprints Preview

CI Status Version Carthage Compatible Code Coverage License Platform Swift

Description

Blueprints Icon

Blueprints is a collection of flow layouts that is meant to make your life easier when working with collection view flow layouts. It comes with two built-in layouts that are highly flexible and easy to configure at the call-site. They support properties like items per row and items per column; this will calculate the layout attributes needed for fitting the number of views that you want to appear on the screen.

The framework also provides a good base for your custom implementations. By extending the core blueprint layout, you get built-in support for animations and layout attribute caching. The bundled default animator supports animations that look very similar to what you get from a vanilla table view. If you want to provide your collection view animator, no problem; you can inject an animator of your choosing when initializing the layout.

Features

  • ๐Ÿญ Animation support
  • ๐Ÿคณ๐Ÿป Optimized for performance
  • ๐Ÿ“ Built-in vertical and horizontal layouts
  • ๐Ÿ“ฐ Supports header and footers
  • ๐Ÿ– Supports sticky headers and footers
  • ๐ŸŒˆ Built-in mosiac layout
  • ๐Ÿ’ฆ Built-in vertical layout that supports waterfall
  • ๐Ÿ“ฑ iOS support
  • ๐Ÿ’ป macOS support
  • ๐Ÿ“บ tvOS support
  • ๐Ÿฆ– Objective-C support

Supporting the project

If you want to support the development of this framework, you can do so by becoming a sponsor. โค๏ธ

Preview

iOS macOS
iOS PReview macOS Preview

How do items per row work?

If you specify how many items per row that you want to appear in a vertical layout, the width of the layout attribute will be calculated for you taking the section inset, item spacing into account to make sure that all views fit your design. For example, if you set that you want two items per row, then two views will appear on the same row side by side. If you want to create a table view layout, you would simply set the items per row value to be one. You can use this variable for horizontal layouts as well, but instead of creating a new row, the value is used to create a width to cover the desired area. If you want the width to span across the entire container then simply set it to one, if you want to create a carousel layout with hinting, setting a value like 1.1 will render at least one complete item and give a visual hint to the user that another view is available if the scroll horizontally.

How do items per column work?

Items per column are explicitly for horizontal layouts and are used to decide how many items that should be shown on screen but using a vertical axis. If you set it to two, it will display two views, one above and one below and then continue to build the rest of the views horizontally, following the same pattern.

How does item sizes work?

It works just like a regular flow layout, but with a twist. If you want to provide a static size using the regular item size, you are free to do so. As mentioned above, you can also provide the number of views that you want visible on the screen based on the container views width. To provide dynamic sizing, you can make your collection view delegate conform to UICollectionViewDelegateFlowLayout or NSCollectionViewDelegateFlowLayout. That way you can compute the values based on the data coming from the data source etc. Worth noting is that using itemsPerRow takes precedence over the other alternatives.

Dynamic sizing preview

Usage

Vertical layout

let blueprintLayout = VerticalBlueprintLayout(
  itemsPerRow: 1.0,
  height: 50,
  minimumInteritemSpacing: 10,
  minimumLineSpacing: 10,
  sectionInset: EdgeInsets(top: 10, left: 10, bottom: 10, right: 10),
  stickyHeaders: true,
  stickyFooters: false
)
let collectionView = UICollectionView(frame: .zero,
                                      collectionViewLayout: blueprintLayout)

Horizontal layout

let blueprintLayout = HorizontalBlueprintLayout(
  itemsPerRow: 1.0,
  itemsPerColumn: 2,
  height: 50,
  minimumInteritemSpacing: 10,
  minimumLineSpacing: 10,
  sectionInset: EdgeInsets(top: 10, left: 10, bottom: 10, right: 10),
  stickyHeaders: true,
  stickyFooters: true
)
let collectionView = UICollectionView(frame: .zero,
                                      collectionViewLayout: blueprintLayout)

Mosaic layout

let mosaicLayout = VerticalMosaicBlueprintLayout(
  patternHeight: 400,
  minimumInteritemSpacing: 2,
  minimumLineSpacing: 2,
  sectionInset: EdgeInsets(top: 2, left: 2, bottom: 2, right: 2),
  patterns: [
    MosaicPattern(alignment: .left, direction: .vertical, amount: 2, multiplier: 0.6),
    MosaicPattern(alignment: .left, direction: .horizontal, amount: 2, multiplier: 0.33),
    MosaicPattern(alignment: .left, direction: .vertical, amount: 1, multiplier: 0.5),
    MosaicPattern(alignment: .left, direction: .vertical, amount: 1, multiplier: 0.5)
  ])
let collectionView = UICollectionView(frame: .zero,
                                      collectionViewLayout: mosaicLayout)

Installation

Blueprints is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'Blueprints'

Blueprints is also available through Carthage. To install just write into your Cartfile:

github "zenangst/Blueprints"

Blueprints can also be installed manually. Just download and drop Sources folders in your project.

Author(s)

Contributing

We would love you to contribute to Blueprints, check the CONTRIBUTING file for more info.

License

Blueprints is available under the MIT license. See the LICENSE file for more info.

blueprints's People

Contributors

christoff-1992 avatar legoless avatar zenangst 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

blueprints's Issues

[Example] Horizontal Layout headers and footers UILabel exceed maximum graphic context buffer.

Opening this issue for the visibility of others as it's something to keep in mind.

When using the horizontal layout with headers and footers, when not set to stick, they will take the full width of the section.

If you have for example the following layout (see below), where the UILabel is constrained to the edges of the view. It's possible for the UILabel to exceed the graphics context buffer. If this occurs the text of the UILabel will not be rendered.

screenshot 2019-02-14 at 15 51 11

This is most noticeable on the simulator or on lower end devices and may not always occur on physical devices if there is enough memory.

This can be easily worked around but will vary on the use case, for example with the above instead of pining the edges to the view we can simply centre the label horizontally. The original constraints have also been adjusted so that they can not exceed the width of the frame. e.g. label.leading >= Safe Area.leading + 10.

screenshot 2019-02-14 at 15 55 24

Incorrect collectionView size after setting layout in viewWillTransition.

I am trying to update the collectionView layout after rotating the device.

I programatically calculate the number of items that are required per row based on the size of the cell and the available width of the view. However this causes the collection view width and height to behave very oddly.

The width becomes too large and the height becomes too small...

Is there a preferred approach to take? Here a sample of when I set the new layout.

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)

        coordinator.animate(alongsideTransition: nil) { [weak self] _ in
            guard let layout = self?.offlineLoginCollectionViewLayout(forWidth: size.width) else {
                Logger.log(message: "Failed to create layout", event: .verbose)
                return
            }
            self?.collectionView?.collectionViewLayout.invalidateLayout()
            self?.collectionView?.setCollectionViewLayout(layout, animated: true)
        }
    }

What I am trying to achieve is the following,

The number of items are calculated based off the size of the item / the size of the available view width adding .1 to hint that other items are available. However on rotation to landscape I would like to recalculate this and show the update so that more items are visible to the user.

Xcode 10, swfit 4.2

hi when i try to build from xcode i have this error

Type 'UICollectionView' has no member 'UICollectionElementKindSectionHeader'

xcode 10
swift 4.2

OSX - Scrollbar overlaps with solid background (based on users general preferences)

If the user has the system preferences for show scroll bars set to either 'Always' or 'Automatically based on mouse or trackpad' where the mouse/trackpad doesn't support gestures, the scrollbar style used has a solid background by default.

This means that the cells are partially covered sometimes.

Example - 'Always' or device without gesture support when using 'Automatic':

screenshot 2019-01-17 at 13 49 23

Example - 'When scrolling only':

screenshot 2019-01-17 at 13 49 34

It would be nice to ensure that the collection view is not covered by the scrollbar so that content is not hidden.

strange warning

hi i read this warning in the dabug area

Snapshotting a view that has not been rendered results in an empty snapshot. Ensure your view has been rendered at least once before snapshotting or snapshot after screen updates.

Changing the dataSource can cause the cachedItems layoutAttributes to become nil, when the updated datasource has items for section at index.

I have observed the following behaviour when updating the dataSource and updating the collectionViewLayout. (As different cells are been used and they have a different height between the datasources)

  1. CollectionView with segmented control that changes the datasource.
  2. The first datasource has items, the second datasource has no items, the third datasource has items.
  3. When switching to the empty datasource the cached items will become nil as there are no items in the section.
  4. When switching to a datasource with items after this, the cached items correctly updates initially but then somewhere along the chain it invalidates the cached items returning them to the nil state. I am not sure what is causing this at the moment as the blueprint layout correctly sets this and super is always called before the cached items have been set.
  5. When layoutAttibutesForItem(at indexPath: IndexPath) is called, as the cached items have been invalidated, an index out of bounds exception is caused.

implementing a safe check for the index resolves the issues, however I am not sure what's causing the cached items to become nil in the first place.

override open func layoutAttributesForItem(at indexPath: IndexPath) -> LayoutAttributes? {
    let compare: (LayoutAttributes) -> Bool
    #if os(macOS)
      compare = { indexPath > $0.indexPath! }
    #else
      compare = { indexPath > $0.indexPath }
    #endif

    guard cachedItems[safe:indexPath.section] != nil else {
        return nil
    }

    let result = binarySearch.findElement(in: cachedItems[indexPath.section],
                                          less: compare,
                                          match: { indexPath == $0.indexPath })
    return result
  }
subscript (safe index: Int) -> Element? {
        return index < count ? self[index] : nil
    }

I will add an example asap and will continue to hunt down what may be causing the cached items to become nil.

Performance

Hey! Does this framework have any performance specifications? How many cells we can use/fetch at once? Thanks!

Sticky headers are not working

Hey at the moment if I implement the blueprints layout with sticky headers they do not stick to the top of the view.

Here is an example project: Example

Bug/Negative footer item index

Due to the offset used when obtaining the last footer reference it's possible for the requested item to be negative e.g -1. In this scenario we should return the footerReferenceHeight as there will be no footer to be displayed.

Improve public API for builtin layouts

I think it would be nice to improve the public API's for the built-in layouts.
The user (developer) should never have to declare variables that are not used, it simply obscures the public having them there. For example, you don't need to assign an itemSize to a mosaic layout because the size is calculated based on the variables. This kind of clean up would be a good improvement to Blueprints.

OSX example doesn't compile

Compile OSX framework is Ok
Compile OSX example is ok but problem is link
the message error are:

Undefined symbols for architecture x86_64:
"___llvm_profile_runtime", referenced from:
___llvm_profile_runtime_user in BlueprintLayoutAnimator.o
___llvm_profile_runtime_user in BlueprintLayoutAnimation.o
___llvm_profile_runtime_user in TypeAlias.o
___llvm_profile_runtime_user in BlueprintLayoutAnimationType.o
(maybe you meant: ___llvm_profile_runtime_user)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

compiled with XCode 10.2.1

Incorrect "Waterfall" layout order

Hey! Thank you for the great framework!
I tried demo project and noticed a vertical layout bug.
For instance: CollectionView contains multiple cells and two cells with index 31 and 32 which have the same Y, but the cell with index 31 is located on the right, not on the left.

Please have a look on the screenshots.
Screen Shot 2021-03-26 at 8 14 21 PM
Screen Shot 2021-03-26 at 8 15 02 PM
Screen Shot 2021-03-26 at 8 15 18 PM

Dynamic height for HorizontalBlueprintLayout

With UICollectionViewFlowLayout I can set the height dynamic which adjust itself according to the underlying content by setting height in estimatedItemSize to 1 and then overriding systemLayoutSizeFitting in cell and setting height again to 1. This makes the height auto-adjusted according to its content. In Blueprints, it seems to be overriding it and I am no longer able to make it dynamic. I am using single row layout with multiple items.

Recommended approach to dynamic height using UICollectionViewDelegateFlowLayout

Hey just wondering what the recommended approach is to using items per row with dynamic height, do we have to load a dummy cell to calculate the height and return this in the sizeForItemAt?

Likewise to support self sizing headers, at the moment the headerReferenceSize is been used to set the height. As a result the delegate method for referenceSizeForHeaderInSection is not called. Is this a limitation of the layout?

[SUGGESTION] AutoCenter flow

Hello ๐Ÿ‘‹ , first of all thanks for this project!

I'd like to know if it would be possible to auto-compute the sectionInset based on the layout (and with self cell size calculation) ๐Ÿ˜„ .

Regards

Dynamic heights for headers and footers breaks cell positioning.

When using dynamic heights for headers and footers the height of the collection view is incorrect. The last item will be overlapped by the footer due to the footers dynamic size not been taken into account. This had been fine in the past and may be related to some of the refactoring between #131 and master.

Unable to scroll past all items in the collection view. Affects both horizontal and vertical layout when using dynamic heights.

I know changes haven't been made to the horizontal layout to fully support dynamic heights however I thought it worth mentioning here that both are affected in slightly different ways.

Vertical:
If the previous columns last item has a greater height than the last item in the last column, then the scrollview bounds are pinning against the last item. This stops the user from scrolling which in turn cuts off the collection view cell.

screenshot 2018-12-13 at 13 16 50

Horizontal:

When scrolling around the cells disappear and re-appear, this is only an issue if items per rows is been used alongside items per column. However this may not be the intended use for a Horizontal layout and will probably be addressed in #43

Horizontal Layout - layout attributes for supplementary items changed without invalidating the layout.

If headers and footers are been used the horizontal layout will crash when been set.

2018-12-14 11:33:41.254874+0000 Example-iOS[2682:570726] *** Assertion failure in -[UICollectionViewData validateLayoutInRect:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore/UIKit-3698.93.8/UICollectionViewData.m:459
2018-12-14 11:33:41.256220+0000 Example-iOS[2682:570726] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'layout attributes for supplementary item at index path (<NSIndexPath: 0x281b59b00> {length = 2, path = 0 - 0}) changed from <UICollectionViewLayoutAttributes: 0x104700dd0> index path: (<NSIndexPath: 0x281b582e0> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 375 61);  to <UICollectionViewLayoutAttributes: 0x104704650> index path: (<NSIndexPath: 0x281b59b00> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 1835 61);  without invalidating the layout'
*** First throw call stack:
(0x1c05fbea0 0x1bf7cda40 0x1c0511c1c 0x1c0fff140 0x1ecfbb2ac 0x1ecfba814 0x1ecfbce30 0x1ecf8f594 0x1ecf8eaa8 0x1ecf8e668 0x102f03db0 0x102ee9c80 0x1edb528b4 0x1edb52ed0 0x102f03b68 0x102f0d0f4 0x102f0d044 0x102f0d154 0x1ed6ba768 0x1ece8a6ec 0x1ece8a61c 0x1ed6ba768 0x1ed1486d0 0x1ed1489f0 0x1ed1479f0 0x1ed6f418c 0x1ed6f53f0 0x1ed6d46ec 0x1ed7a057c 0x1ed7a2f74 0x1ed79ba64 0x1c058c1cc 0x1c058c14c 0x1c058ba30 0x1c05868fc 0x1c05861cc 0x1c27fd584 0x1ed6b9054 0x102ed8288 0x1c0046bb4)
libc++abi.dylib: terminating with uncaught exception of type NSException

Index path invalidated during code execution

This is an extension of #40 #42, when the layout has been changed I have observed occasionally that the indexPath is invalidated. The previous fix makes assumptions that this will be available and it probably should be but hey ho..

screenshot 2018-12-14 at 10 06 13

Multiple Sections?

Is there a way to use this with multiple sections? When I set the layout, it crashes and I get this error:

Thread 1: Fatal error: Index out of range

It traces back to the VerticalBlueprintLayout.swift's class prepare() method:

        if section == layoutAttributes.count {
          layoutAttributes.append([layoutAttribute])
        } else {
          layoutAttributes[section].append(layoutAttribute)
        }

The objective was to make the 2nd and 3rd sections of the collection view use the VerticalBlueprintLayout while keeping the 1st section customized.

fixed width in horizontal scrolling

Is it possible to fix the width to constant value in HorizontalBlueprintLayout. I have tried using estimatedItemSize and width constraint on contentView but its getting override by the library.

Update demo project for iOS 13.

With the changes in iOS 13 the demo project does not look as great as it could. It just needs some small tweaks here and there.

macOS Example project - Implement dynamic height for cells

Need to implement an example for dynamic height for the macOS Example, at the point of asking for the size in sizeForItemAt*, the collection view has not been loaded into the view so we can not make use of makeItem(withIdentifier*.

We will need to load a dummy cell and set the calculated width like the iOS example does.

NSCollectionView does not update the content size correctly after animating layout changes

When setting a new layout the contentSize doesn't correctly update, this can be seen in the macOS example project when switching between the horizontal layout -> mosaic layout. As the previous content size has a larger width than the updated layout. Resizing the window makes the collection view update correctly but I am not sure at this time what is causing this.

This may also be an issue on iOS but as the collections views content size doesn't change like it does on macOS it's not noticeable.

Mosaic Layout

I'm having troubles understanding how the Mosaic Layout works...

I don't understand why the first "block" isn't aligned on the right.

simulator screen shot - iphone 8 - 2018-09-13 at 18 51 27

let mosaicLayout = VerticalMosaicBlueprintLayout(
  itemSize: CGSize.init(width: 50, height: 400),
  minimumInteritemSpacing: 15,
  minimumLineSpacing: 15,
  sectionInset: EdgeInsets(top: 15, left: 15, bottom: 15, right: 15),
  patterns: [
    MosaicPattern(alignment: .left, direction: .vertical, amount: 2, multiplier: 0.5),
    MosaicPattern(alignment: .left, direction: .horizontal, amount: 2, multiplier: 0.33),
    MosaicPattern(alignment: .left, direction: .vertical, amount: 1, multiplier: 0.5),
    MosaicPattern(alignment: .right, direction: .vertical, amount: 2, multiplier: 0.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.