Giter Site home page Giter Site logo

mischa-hildebrand / alignedcollectionviewflowlayout Goto Github PK

View Code? Open in Web Editor NEW
1.2K 9.0 200.0 445 KB

A collection view layout that gives you control over the horizontal and vertical alignment of the cells.

License: MIT License

Swift 91.99% Ruby 8.01%
ios uicollectionview uicollectionviewflowlayout uicollectionviewlayout alignment swift uikit

alignedcollectionviewflowlayout's Introduction

AlignedCollectionViewFlowLayout

Version CocoaPods License Platform

A collection view layout that gives you control over the horizontal and vertical alignment of the cells. You can use it to align the cells like words in a left- or right-aligned text and you can specify how the cells are vertically aligned within their rows.

Other than that, the layout behaves exactly like Apple's UICollectionViewFlowLayout. (It's a subclass.)

ℹ️ Important:

AlignedCollectionViewFlowLayout was developed with a "tag view" in mind, i.e. a collection view that displays a limited number of items with a relatively simple layout. It works perfectly for this use case. While it also does its job for a large number of items and more complex cell layouts scrolling might become laggy in this case. This is due to the fact the layout needs to recursively obtain layout attributes from its superclass and cannot be avoided. If you experience unacceptable lagginess please consider other alternatives.

Available Alignment Options

You can use any combination of horizontal and vertical alignment to achieve your desired layout.

Horizontal Alignment:

  • horizontalAlignment = .left

Example layout for horizontalAlignment = .left

  • horizontalAlignment = .right

Example layout for horizontalAlignment = .right

  • horizontalAlignment = .justified

Example layout for horizontalAlignment = .justified

Vertical Alignment:

  • verticalAlignment = .top

Example layout for verticalAlignment = .top

  • verticalAlignment = .center

Example layout for verticalAlignment = .center

  • verticalAlignment = .bottom

Example layout for verticalAlignment = .bottom

Installation

With CocoaPods:

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

pod "AlignedCollectionViewFlowLayout"

Manual installation:

Just add the file AlignedCollectionViewFlowLayout.swift to your Xcode project and you're ready to go.

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

Usage

Setup in Interface Builder

  1. You have a collection view in Interface Builder and setup its data source appropriately. Run the app and make sure everything works as expected (except the cell alignment).

  2. In the Document Outline, select the collection view layout object.

    Screenshot of the Flow Layout object in Interface Builder

  3. In the Identity Inspector, set the layout object's custom class to AlignedCollectionViewFlowLayout.

    Screenshot: How to set a custom class for the layout object in Interface Builder

  4. Add and customize the following code to your view controller's viewDidLoad() method:

    let alignedFlowLayout = collectionView?.collectionViewLayout as? AlignedCollectionViewFlowLayout
    alignedFlowLayout?.horizontalAlignment = .left
    alignedFlowLayout?.verticalAlignment = .top

    If you omit any of the last two lines the default alignment will be used (horizontally justified, vertically centered).

    💡 Pro Tip: Instead of type-casting the layout as shown above you can also drag an outlet from the collection view layout object to your view controller.

Setup in code

  1. Create a new AlignedCollectionViewFlowLayout object and specify the alignment you want:

    let alignedFlowLayout = AlignedCollectionViewFlowLayout(horizontalAlignment: .left, verticalAlignment: .top)
  2. Either create a new collection view object and and initialize it with alignedFlowLayout:

    let collectionView = UICollectionView(frame: bounds, collectionViewLayout: alignedFlowLayout)

    or assign alignedFlowLayout to the collectionViewLayout property of an existing collection view:

    yourExistingCollectionView.collectionViewLayout = alignedFlowLayout
  3. Implement your collection view's data source.

  4. Run the app.


Additional configuration

For the left and right alignment AlignedCollectionViewFlowLayout distributes the cells horizontally with a constant spacing which is the same for all rows. You can control the spacing with the minimumInteritemSpacing property.

alignedFlowLayout.minimumInteritemSpacing = 10

Despite its name (which originates from its superclass UICollectionViewFlowLayout) this property doesn't describe a minimum spacing but the exact spacing between the cells.

The vertical spacing between the lines works exactly as in UICollectionViewFlowLayout:

alignedFlowLayout.minimumLineSpacing = 10

Enjoy! 😎

Author

Mischa Hildebrand, [email protected]

Twitter Follow GitHub followers

License

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

alignedcollectionviewflowlayout's People

Contributors

bpollman avatar e-sung avatar jeehut avatar mischa-hildebrand avatar omarpedraza 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

alignedcollectionviewflowlayout's Issues

Top Alignment Bug

Hi,

I have a collectionView with vertical flow layout that contains the header and shows two cells in a row, the following code will return incorrect y pos in one of the cell:

Line 303:

 switch verticalAlignment {
         case .top:
            let minY = layoutAttributes.reduce(CGFloat.greatestFiniteMagnitude) { min($0, $1.frame.minY) }
            return AlignmentAxis(alignment: .top, position: minY)

I found out the issue is caused by the UICollectionElementKindSectionHeader contains in the layoutAttributes array and affected the result of minY.

Here is my quick fix:

let filteredLayoutAttributes = layoutAttributes.filter { layoutAttribute in
       return layoutAttribute.representedElementCategory == .cell
}
            
let minY = filteredLayoutAttributes.reduce(CGFloat.greatestFiniteMagnitude) { min($0, $1.frame.minY) }
return AlignmentAxis(alignment: .top, position: minY)

Pod updated to 1.1.1?

When will the pod be updated? The pod still has the issue of the init being "internal" and therefore inaccessible to users of the pod.

Crash on Iphone X (iOS 11.2)

I have implemnted AlignedCollectionViewFlowLayout on my app.

let leftAlignLayout = AlignedCollectionViewFlowLayout(horizontalAlignment: .left, verticalAlignment: .top)
leftAlignLayout.estimatedItemSize = CGSize(width: 1, height: 1)

// Margin
leftAlignLayout.minimumInteritemSpacing = 6
leftAlignLayout.minimumLineSpacing = 6
leftAlignLayout.sectionInset = UIEdgeInsets(top: 6, left: 6, bottom: 6, right: 6)

collectionView.collectionViewLayout = leftAlignLayout

It crash on iPhone X. On iphone 6, 6s and other it works normally.
Please help me.
screen shot 2018-04-06 at 11 03 22 pm

New Release For Right To Left?

Right to left support is very important can you do another release so that feature is accessible directly without specifying sha via cocoapods?

High CPU usage on fast scrolling on bigger lists?

Loving the library! So useful, its suprising that UICollectionView doesnt support this kind of functionality by default.

Just been testing it myself by using the AlignedCollectionViewFlowLayout example to test the performance. Only thing is, if I have a list of items around 500 long, I notice that the CPU usage jumps to up to 80% if you can scroll fast enough. This will happen regardless of the arrays size but is more difficult to replicate because you cant scroll as fast.

Is there anything I can do t improve the performance? This is using the iPhone Simulator on iOS 10.3. I know that the iOS Simulator isnt very good for judging performance so maybe its just that.

Add Natural horizontal alignment.

Given that for some layouts the desired alignment of the cells may actually depend upon how the cells themselves align their content with respect to the leading and trailing edges or depending upon their labels' text alignment, it makes sense to be directionally aware in this library.

左对齐,内容过长超出屏幕外。

init(horizontalAlignment:.left,verticalAlignment:.top)

估计项目尺寸=CGSize(宽度:100,高度:40)

cell 内容过长,超出屏幕外

应该处理成 ... 省略模式

ask for help

let layout = AlignedCollectionViewFlowLayout(horizontalAlignment: .left, verticalAlignment: .top)
layout.minimumLineSpacing = 10
layout.minimumInteritemSpacing = 10
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 20, right: 10)
layout.estimatedItemSize = CGSize(width: 100, height: 40)

When the content is too long for the screen,"cell" display error; I want to process as ellipsis style "xxxx...xxxx"

Multi-line labels break the layout

If you enter a longer string, even in your example project, in one of the cells, the whole layout breaks and none of the cells are loaded. Is there a way to provide support for multi-line labels which would resize the cell dynamically not only by width as it does now but by height also?

Any way to keep spacing the same and increase item sizes to justify?

Thank you for creating this great layout and sharing it with the world! ++ for a very nice readme.

I would like to create a justified layout just like the default settings provide, except instead of increasing the spacing between items, it would increase the width of the cells in that row equally to fill the available width. So the amount of spacing between the items remains consistent and the cells themselves are dynamically sized to fill the space. Would that be possible using this layout?

So given this current layout
simulator screen shot - iphone x - 2018-11-01 at 13 12 33

I'm envisioning it would obtain the same amount of spacing in row 4 for rows 1-3 by increasing the width of the cells in each row until that same spacing is obtained.

Justify last row

With the justify horizontal alignment, would it be possible to justify this row as well so that the last cell is aligned on the right with all the rows above it?

Sometimes items are not rendered

Hello @mischa-hildebrand,

Thank you very much for your very useful layout 👍

I could spot an issue: sometimes cells would not appear on screen.

The screenshot below displays various setups for a custom "InlineStackView" based on your layout. The "Various Heights" item displays a collection view with pills of various font sizes. For some arrangement of font sizes, one item is not displayed.

Capture d’écran 2021-06-23 à 11 59 17

Center Horizontal Alignment

This is a comprehensive and most useful framework. Many thanks! It would be handy to also include a horizontal alignment of centre ala [https://github.com/Coeur/CollectionViewCenteredFlowLayout]

Vertical Aligment Bug

    fileprivate func layoutAttributes(forItemsInLineWith layoutAttributes: UICollectionViewLayoutAttributes) -> [UICollectionViewLayoutAttributes] {
        guard let lineWidth = contentWidth else {
            return [layoutAttributes]
        }
        var lineFrame = layoutAttributes.frame
        lineFrame.origin.x = sectionInset.left
        lineFrame.size.width = lineWidth
        
        // Need to be add
        // bug fix for super.layoutAttributesForElements(in:)
        // if there are three row (Row1, Row2, Row3), and rows step by step closly, such as
        // Row1: (x: 0, y: 0, width: 375, 100)
        // Row2: (x: 0, y: 100, width: 375, height: 100)
        // Row3: (x: 0, y: 200, width: 375, height: 100)
        // then super.layoutAttributesForElements() will reture 3 values(Row1, Row2, Row3), that is incorrect
        if lineFrame.height > 2 {
            lineFrame.origin.y += 1
            lineFrame.size.height -= 2
        }
        
        return super.layoutAttributesForElements(in: lineFrame) ?? []
    }

Error updating width

I'm using alignedFlowLayout this way:

alignedFlowLayout.horizontalAlignment = .left
alignedFlowLayout.verticalAlignment = .top

After XCode updates to 10.0 (but still using Swift 4.0) flow layout requires to update manually after collection view reloaded:

alignedFlowLayout.invalidateLayout()

If there is an any other way for correct using or please fix an issue
Thanx a lot!

Crash on iPad for iOS 15

I'm getting a crash force unwrapping a nil value testing on iOS 15 Beta but on iPad only. iPhone seems to work fine. The crash occurs here

// It's okay to force-unwrap here because we pass a non-empty array.
return verticalAlignmentAxisForLine(with: layoutAttributesInLine)!

the cause being on iPad
super.layoutAttributesForElements called by layoutAttributes(forItemsInLineWith: currentLayoutAttributes) returns nil (and therefore an empty array) whereas on iPhone this returns the layout attributes of the cell. The verticalAlignmentAxisForLine func returns nil if the attributes array is empty therefore causing the force unwrap to crash.

guard let firstAttribute = layoutAttributes.first else {
  return nil
}

Whilst this issue might be fixed in a subsequent beta release I think it exposes a valid issue with the force unwrap. Perhaps a default alignment should be returned by verticalAlignmentAxisForLine instead of nil?

guard let firstAttribute = layoutAttributes.first else {
  return AlignmentAxis(alignment: verticalAlignment, position: 0)
}

Multiple Section Header Overlapping Cells

I am having a hard time trying to add header sections along with your awesome AlignedcollectionViewFlowLayout, but my attempts are failing.

What I did so far was adding layoutAttributesForSupplementaryView:ofKind:at to your .swift file and modifying your setFrame function to process UICollectionElementKindSectionHeader. I also added a public var for header's height: headerHeight

override open func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        
        guard let attributes = super.layoutAttributesForSupplementaryView(ofKind: elementKind, at: indexPath)?.copy() as? UICollectionViewLayoutAttributes else {
            print("nill called attributes")
            return nil
        }
        print("first cell frame = \(layoutAttributesForItem(at: indexPath)!.frame)")
        let yPos:CGFloat = layoutAttributesForItem(at: indexPath)!.frame.origin.y - headerHeight
        attributes.frame = CGRect(x: 0.0, y: yPos, width: (collectionView?.frame.width)!, height: headerHeight)
        
        return attributes
    }

and

/// Sets the frame for the passed layout attributes object by calling the `layoutAttributesForItem(at:)` function.
    private func setFrame(forLayoutAttributes layoutAttributes: UICollectionViewLayoutAttributes) {
        if layoutAttributes.representedElementCategory == .cell { // Do not modify header views etc.
            let indexPath = layoutAttributes.indexPath
            if let newFrame = layoutAttributesForItem(at: indexPath)?.frame {
                layoutAttributes.frame = newFrame
            }
        } else if layoutAttributes.representedElementCategory == .supplementaryView {
            if layoutAttributes.representedElementKind == UICollectionElementKindSectionHeader {
                if let newFrame = layoutAttributesForSupplementaryView(ofKind: UICollectionElementKindSectionHeader, at: layoutAttributes.indexPath)?.frame {
                    layoutAttributes.frame = newFrame
                }
            }
        }
    }

My implementation seems to work right, but when I segue to my ViewController where I am implementing your custom flow layout, viewForSupplementaryElementOfKind is called too soon before the correct attributes are calculated. I know this because I am triggering a call to viewForSupplementaryElementOfKind by reloadItems(at:) inside collectionView:didSelectItemAt

(Edit: I added the following block to my viewDidAppear to work around this delay

UIView.performWithoutAnimation {
            filterCollection.reloadItems(at: [IndexPath(row: patterns.count - 1, section: 2)])
        }

)

I've also printed frame values for first cell in section as well as the header frame. It seems that my attributes are calculated 4 times and the viewForSupplementaryElementOfKind function takes values from the 3rd call

Edit:
see attachment for screens after segueing to VC, log from console about first cell in section and header saved frame, and screen of collectionView after triggering didSelectItemAt or reloadingItems in viewDidAppear

I'd really appreciate it if you'd help me to avoid making extra call to reloadItems(at:)

log after segue to vc
after segue to vc
after click at item at index

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.