From 2a19d7c7cc9823512ddacf06e03e3da90e1d47fc Mon Sep 17 00:00:00 2001 From: Viscmad Date: Mon, 13 Nov 2017 11:14:15 +0530 Subject: [PATCH 1/6] Create CardsCollectionViewVerticleLayout.swift --- CardsCollectionViewVerticleLayout.swift | 155 ++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 CardsCollectionViewVerticleLayout.swift diff --git a/CardsCollectionViewVerticleLayout.swift b/CardsCollectionViewVerticleLayout.swift new file mode 100644 index 0000000..32dbf50 --- /dev/null +++ b/CardsCollectionViewVerticleLayout.swift @@ -0,0 +1,155 @@ +import UIKit + +open class CardsCollectionViewLayout: UICollectionViewLayout { + + // MARK: - Layout configuration + + //MARK: Size attribute + //Using code : + // attributes.size = CGSize( + // width: (collectionView.bounds.width - ((collectionView.bounds.width * 2)/100)) , + // height: (collectionView.bounds.height - ((collectionView.bounds.height * 4)/100))) + //Instead Calling + // public var itemSize: CGSize = CGSize(width: 200, height: 300) { + // didSet{ + // invalidateLayout() + // } + // } + + public var spacing: CGFloat = 10.0 { + didSet{ + invalidateLayout() + } + } + + public var maximumVisibleItems: Int = 2 { + didSet{ + invalidateLayout() + } + } + + // MARK: UICollectionViewLayout + + override open var collectionView: UICollectionView { + return super.collectionView! + } + + override open var collectionViewContentSize: CGSize { + let itemsCount = CGFloat(collectionView.numberOfItems(inSection: 0)) + + return CGSize(width: collectionView.bounds.width, + height: collectionView.bounds.height * itemsCount) + } + + override open func prepare() { + super.prepare() + assert(collectionView.numberOfSections == 1, "Multiple sections aren't supported!") + } + + override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { + let totalItemsCount = collectionView.numberOfItems(inSection: 0) + + let minVisibleIndex = max(Int(collectionView.contentOffset.x) / Int(collectionView.bounds.width), 0) + let maxVisibleIndex = min(minVisibleIndex + maximumVisibleItems, totalItemsCount) + + let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) + + //Converted from contentOffset.x to contentOffset.y + //and from collectionView.bounds.widt to collectionView.bounds.heigh + let deltaOffset = Int(collectionView.contentOffset.y) % Int( collectionView.bounds.height) + let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.height + + let visibleIndices = minVisibleIndex.. UICollectionViewLayoutAttributes? { + let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) + let minVisibleIndex = Int(collectionView.contentOffset.x) / Int(collectionView.bounds.width) + + //Converted from contentOffset.x to contentOffset.y + //and from collectionView.bounds.widt to collectionView.bounds.heigh + let deltaOffset = Int(collectionView.contentOffset.y) % Int(collectionView.bounds.height) + let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.height + + return computeLayoutAttributesForItem(indexPath: indexPath, + minVisibleIndex: minVisibleIndex, + contentCenterX: contentCenterX, + deltaOffset: CGFloat(deltaOffset), + percentageDeltaOffset: percentageDeltaOffset) + } + + override open func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { + return true + } +} + + +// MARK: - Layout computations +fileprivate extension CardsCollectionViewLayout { + + private func scale(at index: Int) -> CGFloat { + let translatedCoefficient = CGFloat(index) - CGFloat(self.maximumVisibleItems) / 2 + return CGFloat(pow(0.95, translatedCoefficient)) + } + + private func transform(atCurrentVisibleIndex visibleIndex: Int, percentageOffset: CGFloat) -> CGAffineTransform { + var rawScale = visibleIndex < maximumVisibleItems ? scale(at: visibleIndex) : 1.0 + + if visibleIndex != 0 { + let previousScale = scale(at: visibleIndex - 1) + let delta = (previousScale - rawScale) * percentageOffset + rawScale += delta + } + return CGAffineTransform(scaleX: rawScale, y: rawScale) + } + + fileprivate func computeLayoutAttributesForItem(indexPath: IndexPath, + minVisibleIndex: Int, + contentCenterX: CGFloat, + deltaOffset: CGFloat, + percentageDeltaOffset: CGFloat) -> UICollectionViewLayoutAttributes { + let attributes = UICollectionViewLayoutAttributes(forCellWith:indexPath) + let visibleIndex = indexPath.row - minVisibleIndex + + //Using this to fill up screen with card with fixed % of margin + attributes.size = CGSize( + width: (collectionView.bounds.width - ((collectionView.bounds.width * 2)/100)) , + height: (collectionView.bounds.height - ((collectionView.bounds.height * 4)/100))) + + let midY = self.collectionView.bounds.midY + attributes.center = CGPoint(x: contentCenterX + spacing * CGFloat(visibleIndex), + y: midY + spacing * CGFloat(visibleIndex)) + attributes.zIndex = maximumVisibleItems - visibleIndex + + attributes.transform = transform(atCurrentVisibleIndex: visibleIndex, + percentageOffset: percentageDeltaOffset) + switch visibleIndex { + case 0: + attributes.center.y -= deltaOffset + break + case 1.. Date: Mon, 13 Nov 2017 15:05:51 +0530 Subject: [PATCH 2/6] Update CardsCollectionViewVerticleLayout.swift *Renamed to VerticalCollectionViewLayout *Fixed issue with scrolling to 1st index (line 58 changed) --- CardsCollectionViewVerticleLayout.swift | 82 +++++++++++++------------ 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/CardsCollectionViewVerticleLayout.swift b/CardsCollectionViewVerticleLayout.swift index 32dbf50..9bdd26b 100644 --- a/CardsCollectionViewVerticleLayout.swift +++ b/CardsCollectionViewVerticleLayout.swift @@ -1,9 +1,9 @@ import UIKit - -open class CardsCollectionViewLayout: UICollectionViewLayout { - + +open class VerticalCollectionViewLayout: UICollectionViewLayout { + // MARK: - Layout configuration - + //MARK: Size attribute //Using code : // attributes.size = CGSize( @@ -15,52 +15,58 @@ open class CardsCollectionViewLayout: UICollectionViewLayout { // invalidateLayout() // } // } - - public var spacing: CGFloat = 10.0 { + + public var spacing: CGFloat = 0.0 { didSet{ invalidateLayout() } } - + public var maximumVisibleItems: Int = 2 { didSet{ invalidateLayout() } } - + // MARK: UICollectionViewLayout - + override open var collectionView: UICollectionView { return super.collectionView! } - + override open var collectionViewContentSize: CGSize { let itemsCount = CGFloat(collectionView.numberOfItems(inSection: 0)) - + + //Converted from + //CGSize(width: collectionView.bounds.width, + //height: collectionView.bounds.height * itemsCount) + //to return CGSize(width: collectionView.bounds.width, height: collectionView.bounds.height * itemsCount) } - + override open func prepare() { super.prepare() assert(collectionView.numberOfSections == 1, "Multiple sections aren't supported!") } - + override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { let totalItemsCount = collectionView.numberOfItems(inSection: 0) - - let minVisibleIndex = max(Int(collectionView.contentOffset.x) / Int(collectionView.bounds.width), 0) + + //Converted from contentOffset.x to contentOffset.y + //and from collectionView.bounds.widt to collectionView.bounds.heigh + let minVisibleIndex = max(Int(collectionView.contentOffset.y) / Int(collectionView.bounds.height), 0) let maxVisibleIndex = min(minVisibleIndex + maximumVisibleItems, totalItemsCount) - + let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) - + //Converted from contentOffset.x to contentOffset.y //and from collectionView.bounds.widt to collectionView.bounds.heigh let deltaOffset = Int(collectionView.contentOffset.y) % Int( collectionView.bounds.height) let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.height - + let visibleIndices = minVisibleIndex.. UICollectionViewLayoutAttributes? { let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) let minVisibleIndex = Int(collectionView.contentOffset.x) / Int(collectionView.bounds.width) - + //Converted from contentOffset.x to contentOffset.y //and from collectionView.bounds.widt to collectionView.bounds.heigh let deltaOffset = Int(collectionView.contentOffset.y) % Int(collectionView.bounds.height) let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.height - + return computeLayoutAttributesForItem(indexPath: indexPath, minVisibleIndex: minVisibleIndex, contentCenterX: contentCenterX, deltaOffset: CGFloat(deltaOffset), percentageDeltaOffset: percentageDeltaOffset) } - + override open func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { return true } } - - + + // MARK: - Layout computations -fileprivate extension CardsCollectionViewLayout { - +fileprivate extension VerticalCollectionViewLayout { + private func scale(at index: Int) -> CGFloat { let translatedCoefficient = CGFloat(index) - CGFloat(self.maximumVisibleItems) / 2 return CGFloat(pow(0.95, translatedCoefficient)) } - + private func transform(atCurrentVisibleIndex visibleIndex: Int, percentageOffset: CGFloat) -> CGAffineTransform { var rawScale = visibleIndex < maximumVisibleItems ? scale(at: visibleIndex) : 1.0 - + if visibleIndex != 0 { let previousScale = scale(at: visibleIndex - 1) let delta = (previousScale - rawScale) * percentageOffset @@ -113,7 +119,7 @@ fileprivate extension CardsCollectionViewLayout { } return CGAffineTransform(scaleX: rawScale, y: rawScale) } - + fileprivate func computeLayoutAttributesForItem(indexPath: IndexPath, minVisibleIndex: Int, contentCenterX: CGFloat, @@ -124,16 +130,16 @@ fileprivate extension CardsCollectionViewLayout { //Using this to fill up screen with card with fixed % of margin attributes.size = CGSize( - width: (collectionView.bounds.width - ((collectionView.bounds.width * 2)/100)) , - height: (collectionView.bounds.height - ((collectionView.bounds.height * 4)/100))) - + width: (collectionView.bounds.width - ((collectionView.bounds.width * 8)/100)) , + height: (collectionView.bounds.height - ((collectionView.bounds.height * 8)/100))) + let midY = self.collectionView.bounds.midY attributes.center = CGPoint(x: contentCenterX + spacing * CGFloat(visibleIndex), y: midY + spacing * CGFloat(visibleIndex)) attributes.zIndex = maximumVisibleItems - visibleIndex - - attributes.transform = transform(atCurrentVisibleIndex: visibleIndex, - percentageOffset: percentageDeltaOffset) + +// attributes.transform = transform(atCurrentVisibleIndex: visibleIndex, +// percentageOffset: percentageDeltaOffset) switch visibleIndex { case 0: attributes.center.y -= deltaOffset @@ -141,7 +147,7 @@ fileprivate extension CardsCollectionViewLayout { case 1.. Date: Wed, 3 Jan 2018 21:38:09 +0300 Subject: [PATCH 3/6] Remove redundant comments, change appearance --- CardsCollectionViewVerticleLayout.swift | 57 ++++++------------- .../CardsExample.xcodeproj/project.pbxproj | 8 +++ .../CardsExample/ViewController.swift | 1 + 3 files changed, 26 insertions(+), 40 deletions(-) diff --git a/CardsCollectionViewVerticleLayout.swift b/CardsCollectionViewVerticleLayout.swift index 9bdd26b..711db02 100644 --- a/CardsCollectionViewVerticleLayout.swift +++ b/CardsCollectionViewVerticleLayout.swift @@ -4,25 +4,19 @@ open class VerticalCollectionViewLayout: UICollectionViewLayout { // MARK: - Layout configuration - //MARK: Size attribute - //Using code : - // attributes.size = CGSize( - // width: (collectionView.bounds.width - ((collectionView.bounds.width * 2)/100)) , - // height: (collectionView.bounds.height - ((collectionView.bounds.height * 4)/100))) - //Instead Calling - // public var itemSize: CGSize = CGSize(width: 200, height: 300) { - // didSet{ - // invalidateLayout() - // } - // } + public var itemSize: CGSize = CGSize(width: 200, height: 300) { + didSet{ + invalidateLayout() + } + } - public var spacing: CGFloat = 0.0 { + public var spacing: CGPoint = CGPoint(x: 10.0, y: 20.0) { didSet{ invalidateLayout() } } - public var maximumVisibleItems: Int = 2 { + public var maximumVisibleItems: Int = 4 { didSet{ invalidateLayout() } @@ -36,11 +30,6 @@ open class VerticalCollectionViewLayout: UICollectionViewLayout { override open var collectionViewContentSize: CGSize { let itemsCount = CGFloat(collectionView.numberOfItems(inSection: 0)) - - //Converted from - //CGSize(width: collectionView.bounds.width, - //height: collectionView.bounds.height * itemsCount) - //to return CGSize(width: collectionView.bounds.width, height: collectionView.bounds.height * itemsCount) } @@ -52,19 +41,13 @@ open class VerticalCollectionViewLayout: UICollectionViewLayout { override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { let totalItemsCount = collectionView.numberOfItems(inSection: 0) - - //Converted from contentOffset.x to contentOffset.y - //and from collectionView.bounds.widt to collectionView.bounds.heigh + let minVisibleIndex = max(Int(collectionView.contentOffset.y) / Int(collectionView.bounds.height), 0) let maxVisibleIndex = min(minVisibleIndex + maximumVisibleItems, totalItemsCount) let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) - - //Converted from contentOffset.x to contentOffset.y - //and from collectionView.bounds.widt to collectionView.bounds.heigh - let deltaOffset = Int(collectionView.contentOffset.y) % Int( collectionView.bounds.height) + let deltaOffset = Int(collectionView.contentOffset.y) % Int(collectionView.bounds.height) let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.height - let visibleIndices = minVisibleIndex.. UICollectionViewLayoutAttributes? { let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) let minVisibleIndex = Int(collectionView.contentOffset.x) / Int(collectionView.bounds.width) - - //Converted from contentOffset.x to contentOffset.y - //and from collectionView.bounds.widt to collectionView.bounds.heigh let deltaOffset = Int(collectionView.contentOffset.y) % Int(collectionView.bounds.height) let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.height - return computeLayoutAttributesForItem(indexPath: indexPath, minVisibleIndex: minVisibleIndex, contentCenterX: contentCenterX, @@ -128,25 +107,23 @@ fileprivate extension VerticalCollectionViewLayout { let attributes = UICollectionViewLayoutAttributes(forCellWith:indexPath) let visibleIndex = indexPath.row - minVisibleIndex - //Using this to fill up screen with card with fixed % of margin - attributes.size = CGSize( - width: (collectionView.bounds.width - ((collectionView.bounds.width * 8)/100)) , - height: (collectionView.bounds.height - ((collectionView.bounds.height * 8)/100))) + attributes.size = self.itemSize let midY = self.collectionView.bounds.midY - attributes.center = CGPoint(x: contentCenterX + spacing * CGFloat(visibleIndex), - y: midY + spacing * CGFloat(visibleIndex)) + let midX = self.collectionView.bounds.midX + attributes.center = CGPoint(x: midX + spacing.x * CGFloat(visibleIndex), + y: midY + spacing.y * CGFloat(visibleIndex)) attributes.zIndex = maximumVisibleItems - visibleIndex -// attributes.transform = transform(atCurrentVisibleIndex: visibleIndex, -// percentageOffset: percentageDeltaOffset) + attributes.transform = transform(atCurrentVisibleIndex: visibleIndex, + percentageOffset: percentageDeltaOffset) switch visibleIndex { case 0: attributes.center.y -= deltaOffset break case 1.. Date: Wed, 3 Jan 2018 22:22:48 +0300 Subject: [PATCH 4/6] Renamed classes, ident horizontal layout with 4 spaces --- CardsCollectionViewHorizontalLayout.swift | 149 ++++++++++++++++++ CardsCollectionViewLayout.swift | 148 ----------------- CardsCollectionViewVerticleLayout.swift | 15 +- .../CardsExample.xcodeproj/project.pbxproj | 8 +- .../CardsExample/ViewController.swift | 2 +- 5 files changed, 167 insertions(+), 155 deletions(-) create mode 100644 CardsCollectionViewHorizontalLayout.swift delete mode 100644 CardsCollectionViewLayout.swift diff --git a/CardsCollectionViewHorizontalLayout.swift b/CardsCollectionViewHorizontalLayout.swift new file mode 100644 index 0000000..0b55105 --- /dev/null +++ b/CardsCollectionViewHorizontalLayout.swift @@ -0,0 +1,149 @@ +// +// CardsCollectionViewHorizontalLayout.swift +// CardsLayout +// +// Created by Filipp Fediakov on 18.08.17. +// Copyright © 2017 filletofish. All rights reserved. +// + +import UIKit + +open class CardsCollectionViewHorizontalLayout: UICollectionViewLayout { + + // MARK: - Layout configuration + + public var itemSize: CGSize = CGSize(width: 200, height: 300) { + didSet { + invalidateLayout() + } + } + + public var spacing: CGFloat = 10.0 { + didSet { + invalidateLayout() + } + } + + public var maximumVisibleItems: Int = 4 { + didSet { + invalidateLayout() + } + } + + // MARK: UICollectionViewLayout + + override open var collectionView: UICollectionView { + return super.collectionView! + } + + override open var collectionViewContentSize: CGSize { + let itemsCount = CGFloat(collectionView.numberOfItems(inSection: 0)) + return CGSize(width: collectionView.bounds.width * itemsCount, + height: collectionView.bounds.height) + } + + override open func prepare() { + super.prepare() + assert(collectionView.numberOfSections == 1, "Multiple sections aren't supported!") + } + + override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { + let totalItemsCount = collectionView.numberOfItems(inSection: 0) + + let minVisibleIndex = max(Int(collectionView.contentOffset.x) / Int(collectionView.bounds.width), 0) + let maxVisibleIndex = min(minVisibleIndex + maximumVisibleItems, totalItemsCount) + + let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) + + let deltaOffset = Int(collectionView.contentOffset.x) % Int(collectionView.bounds.width) + + let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.width + + let visibleIndices = minVisibleIndex.. UICollectionViewLayoutAttributes? { + let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) + let minVisibleIndex = Int(collectionView.contentOffset.x) / Int(collectionView.bounds.width) + let deltaOffset = Int(collectionView.contentOffset.x) % Int(collectionView.bounds.width) + let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.width + return computeLayoutAttributesForItem(indexPath: indexPath, + minVisibleIndex: minVisibleIndex, + contentCenterX: contentCenterX, + deltaOffset: CGFloat(deltaOffset), + percentageDeltaOffset: percentageDeltaOffset) + } + + override open func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { + return true + } +} + + +// MARK: - Layout computations + +fileprivate extension CardsCollectionViewHorizontalLayout { + + private func scale(at index: Int) -> CGFloat { + let translatedCoefficient = CGFloat(index) - CGFloat(self.maximumVisibleItems) / 2 + return CGFloat(pow(0.95, translatedCoefficient)) + } + + private func transform(atCurrentVisibleIndex visibleIndex: Int, percentageOffset: CGFloat) -> CGAffineTransform { + var rawScale = visibleIndex < maximumVisibleItems ? scale(at: visibleIndex) : 1.0 + + if visibleIndex != 0 { + let previousScale = scale(at: visibleIndex - 1) + let delta = (previousScale - rawScale) * percentageOffset + rawScale += delta + } + return CGAffineTransform(scaleX: rawScale, y: rawScale) + } + + fileprivate func computeLayoutAttributesForItem(indexPath: IndexPath, + minVisibleIndex: Int, + contentCenterX: CGFloat, + deltaOffset: CGFloat, + percentageDeltaOffset: CGFloat) -> UICollectionViewLayoutAttributes { + let attributes = UICollectionViewLayoutAttributes(forCellWith:indexPath) + let visibleIndex = indexPath.row - minVisibleIndex + attributes.size = itemSize + let midY = self.collectionView.bounds.midY + attributes.center = CGPoint(x: contentCenterX + spacing * CGFloat(visibleIndex), + y: midY + spacing * CGFloat(visibleIndex)) + attributes.zIndex = maximumVisibleItems - visibleIndex + + attributes.transform = transform(atCurrentVisibleIndex: visibleIndex, + percentageOffset: percentageDeltaOffset) + switch visibleIndex { + case 0: + attributes.center.x -= deltaOffset + break + case 1.. [UICollectionViewLayoutAttributes]? { - let totalItemsCount = collectionView.numberOfItems(inSection: 0) - - let minVisibleIndex = max(Int(collectionView.contentOffset.x) / Int(collectionView.bounds.width), 0) - let maxVisibleIndex = min(minVisibleIndex + maximumVisibleItems, totalItemsCount) - - let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) - - let deltaOffset = Int(collectionView.contentOffset.x) % Int(collectionView.bounds.width) - - let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.width - - let visibleIndices = minVisibleIndex.. UICollectionViewLayoutAttributes? { - let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) - let minVisibleIndex = Int(collectionView.contentOffset.x) / Int(collectionView.bounds.width) - let deltaOffset = Int(collectionView.contentOffset.x) % Int(collectionView.bounds.width) - let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.width - return computeLayoutAttributesForItem(indexPath: indexPath, - minVisibleIndex: minVisibleIndex, - contentCenterX: contentCenterX, - deltaOffset: CGFloat(deltaOffset), - percentageDeltaOffset: percentageDeltaOffset) - } - - override open func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { - return true - } -} - - -// MARK: - Layout computations - -fileprivate extension CardsCollectionViewLayout { - - private func scale(at index: Int) -> CGFloat { - let translatedCoefficient = CGFloat(index) - CGFloat(self.maximumVisibleItems) / 2 - return CGFloat(pow(0.95, translatedCoefficient)) - } - - private func transform(atCurrentVisibleIndex visibleIndex: Int, percentageOffset: CGFloat) -> CGAffineTransform { - var rawScale = visibleIndex < maximumVisibleItems ? scale(at: visibleIndex) : 1.0 - - if visibleIndex != 0 { - let previousScale = scale(at: visibleIndex - 1) - let delta = (previousScale - rawScale) * percentageOffset - rawScale += delta - } - return CGAffineTransform(scaleX: rawScale, y: rawScale) - } - - fileprivate func computeLayoutAttributesForItem(indexPath: IndexPath, - minVisibleIndex: Int, - contentCenterX: CGFloat, - deltaOffset: CGFloat, - percentageDeltaOffset: CGFloat) -> UICollectionViewLayoutAttributes { - let attributes = UICollectionViewLayoutAttributes(forCellWith:indexPath) - let visibleIndex = indexPath.row - minVisibleIndex - attributes.size = itemSize - let midY = self.collectionView.bounds.midY - attributes.center = CGPoint(x: contentCenterX + spacing * CGFloat(visibleIndex), - y: midY + spacing * CGFloat(visibleIndex)) - attributes.zIndex = maximumVisibleItems - visibleIndex - - attributes.transform = transform(atCurrentVisibleIndex: visibleIndex, - percentageOffset: percentageDeltaOffset) - switch visibleIndex { - case 0: - attributes.center.x -= deltaOffset - break - case 1.. UICollectionViewLayoutAttributes? { + let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) let minVisibleIndex = Int(collectionView.contentOffset.x) / Int(collectionView.bounds.width) let deltaOffset = Int(collectionView.contentOffset.y) % Int(collectionView.bounds.height) let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.height + print("Called layoutAttributesForItem IP = \(indexPath), delta = \(deltaOffset), percentage = \(percentageDeltaOffset)%") return computeLayoutAttributesForItem(indexPath: indexPath, minVisibleIndex: minVisibleIndex, contentCenterX: contentCenterX, @@ -81,7 +92,7 @@ open class VerticalCollectionViewLayout: UICollectionViewLayout { // MARK: - Layout computations -fileprivate extension VerticalCollectionViewLayout { +fileprivate extension CardsCollectionViewVerticleLayout { private func scale(at index: Int) -> CGFloat { let translatedCoefficient = CGFloat(index) - CGFloat(self.maximumVisibleItems) / 2 diff --git a/CardsExample/CardsExample.xcodeproj/project.pbxproj b/CardsExample/CardsExample.xcodeproj/project.pbxproj index 07dd5a9..f7cede8 100644 --- a/CardsExample/CardsExample.xcodeproj/project.pbxproj +++ b/CardsExample/CardsExample.xcodeproj/project.pbxproj @@ -12,7 +12,7 @@ 365EC0BD1FABCC9200583152 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 365EC0BB1FABCC9200583152 /* Main.storyboard */; }; 365EC0BF1FABCC9200583152 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 365EC0BE1FABCC9200583152 /* Assets.xcassets */; }; 365EC0C21FABCC9200583152 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 365EC0C01FABCC9200583152 /* LaunchScreen.storyboard */; }; - 365EC0CA1FABCD1500583152 /* CardsCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 365EC0C91FABCD1400583152 /* CardsCollectionViewLayout.swift */; }; + 365EC0CA1FABCD1500583152 /* CardsCollectionViewHorizontalLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 365EC0C91FABCD1400583152 /* CardsCollectionViewHorizontalLayout.swift */; }; 36B5506A1FCE28EB005BBDD5 /* CardsCollectionViewVerticleLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B550691FCE28EB005BBDD5 /* CardsCollectionViewVerticleLayout.swift */; }; /* End PBXBuildFile section */ @@ -24,7 +24,7 @@ 365EC0BE1FABCC9200583152 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 365EC0C11FABCC9200583152 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 365EC0C31FABCC9200583152 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 365EC0C91FABCD1400583152 /* CardsCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CardsCollectionViewLayout.swift; path = ../../CardsCollectionViewLayout.swift; sourceTree = ""; }; + 365EC0C91FABCD1400583152 /* CardsCollectionViewHorizontalLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CardsCollectionViewHorizontalLayout.swift; path = ../../CardsCollectionViewHorizontalLayout.swift; sourceTree = ""; }; 36B550691FCE28EB005BBDD5 /* CardsCollectionViewVerticleLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CardsCollectionViewVerticleLayout.swift; path = ../../CardsCollectionViewVerticleLayout.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -59,7 +59,7 @@ isa = PBXGroup; children = ( 36B550691FCE28EB005BBDD5 /* CardsCollectionViewVerticleLayout.swift */, - 365EC0C91FABCD1400583152 /* CardsCollectionViewLayout.swift */, + 365EC0C91FABCD1400583152 /* CardsCollectionViewHorizontalLayout.swift */, 365EC0B71FABCC9200583152 /* AppDelegate.swift */, 365EC0B91FABCC9200583152 /* ViewController.swift */, 365EC0BB1FABCC9200583152 /* Main.storyboard */, @@ -143,7 +143,7 @@ buildActionMask = 2147483647; files = ( 365EC0BA1FABCC9200583152 /* ViewController.swift in Sources */, - 365EC0CA1FABCD1500583152 /* CardsCollectionViewLayout.swift in Sources */, + 365EC0CA1FABCD1500583152 /* CardsCollectionViewHorizontalLayout.swift in Sources */, 36B5506A1FCE28EB005BBDD5 /* CardsCollectionViewVerticleLayout.swift in Sources */, 365EC0B81FABCC9200583152 /* AppDelegate.swift in Sources */, ); diff --git a/CardsExample/CardsExample/ViewController.swift b/CardsExample/CardsExample/ViewController.swift index 540b048..5f8c3a0 100644 --- a/CardsExample/CardsExample/ViewController.swift +++ b/CardsExample/CardsExample/ViewController.swift @@ -14,7 +14,7 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection override func viewDidLoad() { super.viewDidLoad() - collectionView.collectionViewLayout = CardsCollectionViewLayout() + collectionView.collectionViewLayout = CardsCollectionViewHorizontalLayout() collectionView.dataSource = self collectionView.delegate = self collectionView.isPagingEnabled = true From 3f56b5c791e79a695f5dbb7666a5cf14bc6b237e Mon Sep 17 00:00:00 2001 From: Filipp Fediakov Date: Wed, 3 Jan 2018 22:54:53 +0300 Subject: [PATCH 5/6] Refactor code between two layouts --- CardsCollectionViewHorizontalLayout.swift | 133 ++++++++++------------ CardsCollectionViewVerticleLayout.swift | 93 +++++---------- 2 files changed, 88 insertions(+), 138 deletions(-) diff --git a/CardsCollectionViewHorizontalLayout.swift b/CardsCollectionViewHorizontalLayout.swift index 0b55105..def49d2 100644 --- a/CardsCollectionViewHorizontalLayout.swift +++ b/CardsCollectionViewHorizontalLayout.swift @@ -2,15 +2,23 @@ // CardsCollectionViewHorizontalLayout.swift // CardsLayout // -// Created by Filipp Fediakov on 18.08.17. -// Copyright © 2017 filletofish. All rights reserved. +// Created by Filipp Fediakov on 03.01.18. +// Copyright © 2018 filletofish. All rights reserved. // import UIKit -open class CardsCollectionViewHorizontalLayout: UICollectionViewLayout { - // MARK: - Layout configuration +protocol CardsCollectionViewLayout { + var itemSize: CGSize { get set } + var spacing: CGPoint { get set } + var maximumVisibleItems: Int { get set } +} + + +open class CardsCollectionViewHorizontalLayout: UICollectionViewLayout, CardsCollectionViewLayout { + + // MARK: Layout configuration public var itemSize: CGSize = CGSize(width: 200, height: 300) { didSet { @@ -18,7 +26,7 @@ open class CardsCollectionViewHorizontalLayout: UICollectionViewLayout { } } - public var spacing: CGFloat = 10.0 { + public var spacing: CGPoint = CGPoint(x: 10.0, y: 10.0) { didSet { invalidateLayout() } @@ -30,7 +38,7 @@ open class CardsCollectionViewHorizontalLayout: UICollectionViewLayout { } } - // MARK: UICollectionViewLayout + // MARK: - UICollectionViewLayout override open var collectionView: UICollectionView { return super.collectionView! @@ -49,101 +57,84 @@ open class CardsCollectionViewHorizontalLayout: UICollectionViewLayout { override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { let totalItemsCount = collectionView.numberOfItems(inSection: 0) - let minVisibleIndex = max(Int(collectionView.contentOffset.x) / Int(collectionView.bounds.width), 0) let maxVisibleIndex = min(minVisibleIndex + maximumVisibleItems, totalItemsCount) - - let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) - - let deltaOffset = Int(collectionView.contentOffset.x) % Int(collectionView.bounds.width) - - let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.width - let visibleIndices = minVisibleIndex.. UICollectionViewLayoutAttributes? { - let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) let minVisibleIndex = Int(collectionView.contentOffset.x) / Int(collectionView.bounds.width) - let deltaOffset = Int(collectionView.contentOffset.x) % Int(collectionView.bounds.width) + let deltaOffset = CGFloat(Int(collectionView.contentOffset.x) % Int(collectionView.bounds.width)) let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.width - return computeLayoutAttributesForItem(indexPath: indexPath, - minVisibleIndex: minVisibleIndex, - contentCenterX: contentCenterX, - deltaOffset: CGFloat(deltaOffset), - percentageDeltaOffset: percentageDeltaOffset) - } - - override open func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { - return true - } -} - - -// MARK: - Layout computations - -fileprivate extension CardsCollectionViewHorizontalLayout { - - private func scale(at index: Int) -> CGFloat { - let translatedCoefficient = CGFloat(index) - CGFloat(self.maximumVisibleItems) / 2 - return CGFloat(pow(0.95, translatedCoefficient)) - } - - private func transform(atCurrentVisibleIndex visibleIndex: Int, percentageOffset: CGFloat) -> CGAffineTransform { - var rawScale = visibleIndex < maximumVisibleItems ? scale(at: visibleIndex) : 1.0 - - if visibleIndex != 0 { - let previousScale = scale(at: visibleIndex - 1) - let delta = (previousScale - rawScale) * percentageOffset - rawScale += delta - } - return CGAffineTransform(scaleX: rawScale, y: rawScale) - } + let visibleIndex = indexPath.row - minVisibleIndex - fileprivate func computeLayoutAttributesForItem(indexPath: IndexPath, - minVisibleIndex: Int, - contentCenterX: CGFloat, - deltaOffset: CGFloat, - percentageDeltaOffset: CGFloat) -> UICollectionViewLayoutAttributes { let attributes = UICollectionViewLayoutAttributes(forCellWith:indexPath) - let visibleIndex = indexPath.row - minVisibleIndex attributes.size = itemSize let midY = self.collectionView.bounds.midY - attributes.center = CGPoint(x: contentCenterX + spacing * CGFloat(visibleIndex), - y: midY + spacing * CGFloat(visibleIndex)) + let midX = self.collectionView.bounds.midX + attributes.center = CGPoint(x: midX + spacing.x * CGFloat(visibleIndex), + y: midY + spacing.y * CGFloat(visibleIndex)) attributes.zIndex = maximumVisibleItems - visibleIndex + attributes.transform = scaleTransform(forVisibleIndex: visibleIndex, + percentageOffset: percentageDeltaOffset) + - attributes.transform = transform(atCurrentVisibleIndex: visibleIndex, - percentageOffset: percentageDeltaOffset) switch visibleIndex { case 0: attributes.center.x -= deltaOffset + attributes.isHidden = false break case 1.. Bool { + return true + } } + +extension CardsCollectionViewLayout { + private func scale(at index: Int) -> CGFloat { + let translatedCoefficient = CGFloat(index) - CGFloat(self.maximumVisibleItems) / 2 + return CGFloat(pow(0.95, translatedCoefficient)) + } + + func scaleTransform(forVisibleIndex visibleIndex: Int, percentageOffset: CGFloat) -> CGAffineTransform { + guard visibleIndex != 0 else { + let scaleAtZeroIndex = scale(at: visibleIndex) + return CGAffineTransform(scaleX: scaleAtZeroIndex, y: scaleAtZeroIndex) + } + + guard visibleIndex < maximumVisibleItems else { + return CGAffineTransform.identity + } + + var rawScale = scale(at: visibleIndex) + let previousScale = scale(at: visibleIndex - 1) + let delta = (previousScale - rawScale) * percentageOffset + rawScale += delta + return CGAffineTransform(scaleX: rawScale, y: rawScale) + } +} diff --git a/CardsCollectionViewVerticleLayout.swift b/CardsCollectionViewVerticleLayout.swift index 642f948..1b29f47 100644 --- a/CardsCollectionViewVerticleLayout.swift +++ b/CardsCollectionViewVerticleLayout.swift @@ -8,9 +8,9 @@ import UIKit -open class CardsCollectionViewVerticleLayout: UICollectionViewLayout { +open class CardsCollectionViewVerticleLayout: UICollectionViewLayout, CardsCollectionViewLayout { - // MARK: - Layout configuration + // MARK: Layout configuration public var itemSize: CGSize = CGSize(width: 200, height: 300) { didSet{ @@ -30,7 +30,7 @@ open class CardsCollectionViewVerticleLayout: UICollectionViewLayout { } } - // MARK: UICollectionViewLayout + // MARK: - UICollectionViewLayout override open var collectionView: UICollectionView { return super.collectionView! @@ -49,101 +49,60 @@ open class CardsCollectionViewVerticleLayout: UICollectionViewLayout { override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { let totalItemsCount = collectionView.numberOfItems(inSection: 0) - let minVisibleIndex = max(Int(collectionView.contentOffset.y) / Int(collectionView.bounds.height), 0) let maxVisibleIndex = min(minVisibleIndex + maximumVisibleItems, totalItemsCount) - - let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) - let deltaOffset = Int(collectionView.contentOffset.y) % Int(collectionView.bounds.height) - let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.height let visibleIndices = minVisibleIndex.. UICollectionViewLayoutAttributes? { - - let contentCenterX = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0) - let minVisibleIndex = Int(collectionView.contentOffset.x) / Int(collectionView.bounds.width) - let deltaOffset = Int(collectionView.contentOffset.y) % Int(collectionView.bounds.height) + let minVisibleIndex = Int(collectionView.contentOffset.y) / Int(collectionView.bounds.height) + let deltaOffset = CGFloat(Int(collectionView.contentOffset.y) % Int(collectionView.bounds.height)) let percentageDeltaOffset = CGFloat(deltaOffset) / collectionView.bounds.height - print("Called layoutAttributesForItem IP = \(indexPath), delta = \(deltaOffset), percentage = \(percentageDeltaOffset)%") - return computeLayoutAttributesForItem(indexPath: indexPath, - minVisibleIndex: minVisibleIndex, - contentCenterX: contentCenterX, - deltaOffset: CGFloat(deltaOffset), - percentageDeltaOffset: percentageDeltaOffset) - } - - override open func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { - return true - } -} - - -// MARK: - Layout computations -fileprivate extension CardsCollectionViewVerticleLayout { - - private func scale(at index: Int) -> CGFloat { - let translatedCoefficient = CGFloat(index) - CGFloat(self.maximumVisibleItems) / 2 - return CGFloat(pow(0.95, translatedCoefficient)) - } - - private func transform(atCurrentVisibleIndex visibleIndex: Int, percentageOffset: CGFloat) -> CGAffineTransform { - var rawScale = visibleIndex < maximumVisibleItems ? scale(at: visibleIndex) : 1.0 + let visibleIndex = indexPath.row - minVisibleIndex - if visibleIndex != 0 { - let previousScale = scale(at: visibleIndex - 1) - let delta = (previousScale - rawScale) * percentageOffset - rawScale += delta - } - return CGAffineTransform(scaleX: rawScale, y: rawScale) - } - - fileprivate func computeLayoutAttributesForItem(indexPath: IndexPath, - minVisibleIndex: Int, - contentCenterX: CGFloat, - deltaOffset: CGFloat, - percentageDeltaOffset: CGFloat) -> UICollectionViewLayoutAttributes { let attributes = UICollectionViewLayoutAttributes(forCellWith:indexPath) - let visibleIndex = indexPath.row - minVisibleIndex - - attributes.size = self.itemSize - + attributes.size = itemSize let midY = self.collectionView.bounds.midY let midX = self.collectionView.bounds.midX attributes.center = CGPoint(x: midX + spacing.x * CGFloat(visibleIndex), y: midY + spacing.y * CGFloat(visibleIndex)) attributes.zIndex = maximumVisibleItems - visibleIndex - - attributes.transform = transform(atCurrentVisibleIndex: visibleIndex, - percentageOffset: percentageDeltaOffset) + attributes.transform = scaleTransform(forVisibleIndex: visibleIndex, + percentageOffset: percentageDeltaOffset) + + switch visibleIndex { case 0: attributes.center.y -= deltaOffset + attributes.isHidden = false break case 1.. Bool { + return true + } } From 25bec17a60d535693127e856cdd35226d9d75440 Mon Sep 17 00:00:00 2001 From: Filipp Fediakov Date: Wed, 3 Jan 2018 22:57:22 +0300 Subject: [PATCH 6/6] Update pod spec, fix typo in file name --- ...ayout.swift => CardsCollectionViewVerticalLayout.swift | 4 ++-- CardsExample/CardsExample.xcodeproj/project.pbxproj | 8 ++++---- CardsLayout.podspec | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename CardsCollectionViewVerticleLayout.swift => CardsCollectionViewVerticalLayout.swift (97%) diff --git a/CardsCollectionViewVerticleLayout.swift b/CardsCollectionViewVerticalLayout.swift similarity index 97% rename from CardsCollectionViewVerticleLayout.swift rename to CardsCollectionViewVerticalLayout.swift index 1b29f47..caf96ec 100644 --- a/CardsCollectionViewVerticleLayout.swift +++ b/CardsCollectionViewVerticalLayout.swift @@ -1,5 +1,5 @@ // -// CardsCollectionViewVerticleLayout.swift +// CardsCollectionViewVerticalLayout.swift // CardsLayout // // Created by Filipp Fediakov on 18.08.17. @@ -8,7 +8,7 @@ import UIKit -open class CardsCollectionViewVerticleLayout: UICollectionViewLayout, CardsCollectionViewLayout { +open class CardsCollectionViewVerticalLayout: UICollectionViewLayout, CardsCollectionViewLayout { // MARK: Layout configuration diff --git a/CardsExample/CardsExample.xcodeproj/project.pbxproj b/CardsExample/CardsExample.xcodeproj/project.pbxproj index f7cede8..3390de5 100644 --- a/CardsExample/CardsExample.xcodeproj/project.pbxproj +++ b/CardsExample/CardsExample.xcodeproj/project.pbxproj @@ -13,7 +13,7 @@ 365EC0BF1FABCC9200583152 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 365EC0BE1FABCC9200583152 /* Assets.xcassets */; }; 365EC0C21FABCC9200583152 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 365EC0C01FABCC9200583152 /* LaunchScreen.storyboard */; }; 365EC0CA1FABCD1500583152 /* CardsCollectionViewHorizontalLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 365EC0C91FABCD1400583152 /* CardsCollectionViewHorizontalLayout.swift */; }; - 36B5506A1FCE28EB005BBDD5 /* CardsCollectionViewVerticleLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B550691FCE28EB005BBDD5 /* CardsCollectionViewVerticleLayout.swift */; }; + 36B5506A1FCE28EB005BBDD5 /* CardsCollectionViewVerticalLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B550691FCE28EB005BBDD5 /* CardsCollectionViewVerticalLayout.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -25,7 +25,7 @@ 365EC0C11FABCC9200583152 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 365EC0C31FABCC9200583152 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 365EC0C91FABCD1400583152 /* CardsCollectionViewHorizontalLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CardsCollectionViewHorizontalLayout.swift; path = ../../CardsCollectionViewHorizontalLayout.swift; sourceTree = ""; }; - 36B550691FCE28EB005BBDD5 /* CardsCollectionViewVerticleLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CardsCollectionViewVerticleLayout.swift; path = ../../CardsCollectionViewVerticleLayout.swift; sourceTree = ""; }; + 36B550691FCE28EB005BBDD5 /* CardsCollectionViewVerticalLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CardsCollectionViewVerticalLayout.swift; path = ../../CardsCollectionViewVerticalLayout.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -58,7 +58,7 @@ 365EC0B61FABCC9200583152 /* CardsExample */ = { isa = PBXGroup; children = ( - 36B550691FCE28EB005BBDD5 /* CardsCollectionViewVerticleLayout.swift */, + 36B550691FCE28EB005BBDD5 /* CardsCollectionViewVerticalLayout.swift */, 365EC0C91FABCD1400583152 /* CardsCollectionViewHorizontalLayout.swift */, 365EC0B71FABCC9200583152 /* AppDelegate.swift */, 365EC0B91FABCC9200583152 /* ViewController.swift */, @@ -144,7 +144,7 @@ files = ( 365EC0BA1FABCC9200583152 /* ViewController.swift in Sources */, 365EC0CA1FABCD1500583152 /* CardsCollectionViewHorizontalLayout.swift in Sources */, - 36B5506A1FCE28EB005BBDD5 /* CardsCollectionViewVerticleLayout.swift in Sources */, + 36B5506A1FCE28EB005BBDD5 /* CardsCollectionViewVerticalLayout.swift in Sources */, 365EC0B81FABCC9200583152 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/CardsLayout.podspec b/CardsLayout.podspec index 46580b2..5e83a78 100644 --- a/CardsLayout.podspec +++ b/CardsLayout.podspec @@ -88,7 +88,7 @@ Pod::Spec.new do |s| # Not including the public_header_files will make all headers public. # - s.source_files = "CardsCollectionViewLayout.swift" + s.source_files = "CardsCollectionViewHorizontalLayout.swift", "CardsCollectionViewVerticalLayout.swift" s.exclude_files = "CardsExample", "CardsOldSource" # s.public_header_files = "Classes/**/*.h"