diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index d2c9af9..0c99e27 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -8,6 +8,12 @@ /* Begin PBXBuildFile section */ 49588A0F5E1A5829806CF162 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C685BA161AB98E8D668D2F26 /* Pods.framework */; }; + 9349943A1C399D70006C4B97 /* IntroViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934994391C399D70006C4B97 /* IntroViewController.swift */; }; + 9349943C1C399D99006C4B97 /* Intro.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9349943B1C399D99006C4B97 /* Intro.storyboard */; }; + 9349943F1C39A5B9006C4B97 /* IntroCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9349943D1C39A5B9006C4B97 /* IntroCollectionViewCell.swift */; }; + 934994401C39A5B9006C4B97 /* IntroCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9349943E1C39A5B9006C4B97 /* IntroCollectionViewCell.xib */; }; + 934994421C39B0F4006C4B97 /* SecoundViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934994411C39B0F4006C4B97 /* SecoundViewController.swift */; }; + 934994441C39B102006C4B97 /* Secound.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 934994431C39B102006C4B97 /* Secound.storyboard */; }; 93DC8E391BD232CA004BCCBC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93DC8E381BD232CA004BCCBC /* AppDelegate.swift */; }; 93DC8E3B1BD232CA004BCCBC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93DC8E3A1BD232CA004BCCBC /* ViewController.swift */; }; 93DC8E3E1BD232CA004BCCBC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 93DC8E3C1BD232CA004BCCBC /* Main.storyboard */; }; @@ -16,6 +22,12 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 934994391C399D70006C4B97 /* IntroViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntroViewController.swift; sourceTree = ""; }; + 9349943B1C399D99006C4B97 /* Intro.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Intro.storyboard; sourceTree = ""; }; + 9349943D1C39A5B9006C4B97 /* IntroCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntroCollectionViewCell.swift; sourceTree = ""; }; + 9349943E1C39A5B9006C4B97 /* IntroCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = IntroCollectionViewCell.xib; sourceTree = ""; }; + 934994411C39B0F4006C4B97 /* SecoundViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecoundViewController.swift; sourceTree = ""; }; + 934994431C39B102006C4B97 /* Secound.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Secound.storyboard; sourceTree = ""; }; 93DC8E351BD232CA004BCCBC /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 93DC8E381BD232CA004BCCBC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 93DC8E3A1BD232CA004BCCBC /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -63,7 +75,13 @@ children = ( 93DC8E381BD232CA004BCCBC /* AppDelegate.swift */, 93DC8E3A1BD232CA004BCCBC /* ViewController.swift */, + 934994411C39B0F4006C4B97 /* SecoundViewController.swift */, + 934994431C39B102006C4B97 /* Secound.storyboard */, + 934994391C399D70006C4B97 /* IntroViewController.swift */, + 9349943D1C39A5B9006C4B97 /* IntroCollectionViewCell.swift */, + 9349943E1C39A5B9006C4B97 /* IntroCollectionViewCell.xib */, 93DC8E3C1BD232CA004BCCBC /* Main.storyboard */, + 9349943B1C399D99006C4B97 /* Intro.storyboard */, 93DC8E3F1BD232CA004BCCBC /* Assets.xcassets */, 93DC8E411BD232CA004BCCBC /* LaunchScreen.storyboard */, 93DC8E441BD232CA004BCCBC /* Info.plist */, @@ -150,7 +168,10 @@ files = ( 93DC8E431BD232CA004BCCBC /* LaunchScreen.storyboard in Resources */, 93DC8E401BD232CA004BCCBC /* Assets.xcassets in Resources */, + 934994441C39B102006C4B97 /* Secound.storyboard in Resources */, + 934994401C39A5B9006C4B97 /* IntroCollectionViewCell.xib in Resources */, 93DC8E3E1BD232CA004BCCBC /* Main.storyboard in Resources */, + 9349943C1C399D99006C4B97 /* Intro.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -209,8 +230,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9349943A1C399D70006C4B97 /* IntroViewController.swift in Sources */, 93DC8E3B1BD232CA004BCCBC /* ViewController.swift in Sources */, + 934994421C39B0F4006C4B97 /* SecoundViewController.swift in Sources */, 93DC8E391BD232CA004BCCBC /* AppDelegate.swift in Sources */, + 9349943F1C39A5B9006C4B97 /* IntroCollectionViewCell.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Example/Example/Assets.xcassets/0.imageset/0.jpg b/Example/Example/Assets.xcassets/0.imageset/0.jpg new file mode 100644 index 0000000..a65e783 Binary files /dev/null and b/Example/Example/Assets.xcassets/0.imageset/0.jpg differ diff --git a/Example/Example/Assets.xcassets/0.imageset/Contents.json b/Example/Example/Assets.xcassets/0.imageset/Contents.json new file mode 100644 index 0000000..487b836 --- /dev/null +++ b/Example/Example/Assets.xcassets/0.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "0.jpg", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/Example/Assets.xcassets/1.imageset/1.jpg b/Example/Example/Assets.xcassets/1.imageset/1.jpg new file mode 100644 index 0000000..93da9ec Binary files /dev/null and b/Example/Example/Assets.xcassets/1.imageset/1.jpg differ diff --git a/Example/Example/Assets.xcassets/1.imageset/Contents.json b/Example/Example/Assets.xcassets/1.imageset/Contents.json new file mode 100644 index 0000000..5017a6b --- /dev/null +++ b/Example/Example/Assets.xcassets/1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "1.jpg", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/Example/Assets.xcassets/2.imageset/2.jpg b/Example/Example/Assets.xcassets/2.imageset/2.jpg new file mode 100644 index 0000000..fd4f5c6 Binary files /dev/null and b/Example/Example/Assets.xcassets/2.imageset/2.jpg differ diff --git a/Example/Example/Assets.xcassets/2.imageset/Contents.json b/Example/Example/Assets.xcassets/2.imageset/Contents.json new file mode 100644 index 0000000..d0a044a --- /dev/null +++ b/Example/Example/Assets.xcassets/2.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "2.jpg", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/Example/Assets.xcassets/3.imageset/3.jpg b/Example/Example/Assets.xcassets/3.imageset/3.jpg new file mode 100644 index 0000000..1916126 Binary files /dev/null and b/Example/Example/Assets.xcassets/3.imageset/3.jpg differ diff --git a/Example/Example/Assets.xcassets/3.imageset/Contents.json b/Example/Example/Assets.xcassets/3.imageset/Contents.json new file mode 100644 index 0000000..f2cc5e2 --- /dev/null +++ b/Example/Example/Assets.xcassets/3.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "3.jpg", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/Example/Base.lproj/Main.storyboard b/Example/Example/Base.lproj/Main.storyboard index 99d33c7..6e20c2a 100644 --- a/Example/Example/Base.lproj/Main.storyboard +++ b/Example/Example/Base.lproj/Main.storyboard @@ -19,7 +19,7 @@ - + @@ -53,7 +53,7 @@ - + diff --git a/Example/Example/Intro.storyboard b/Example/Example/Intro.storyboard new file mode 100644 index 0000000..196e3c5 --- /dev/null +++ b/Example/Example/Intro.storyboard @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/Example/IntroCollectionViewCell.swift b/Example/Example/IntroCollectionViewCell.swift new file mode 100644 index 0000000..bfef251 --- /dev/null +++ b/Example/Example/IntroCollectionViewCell.swift @@ -0,0 +1,21 @@ +// +// IntroCollectionViewCell.swift +// Example +// +// Created by hiroyuki yoshida on 2016/01/04. +// Copyright © 2016年 hiroyuki yoshida. All rights reserved. +// + +import UIKit + +final class IntroCollectionViewCell: UICollectionViewCell { + @IBOutlet weak var imageView: UIImageView! + static let identifier = "IntroCollectionViewCell" + static let nib = UINib(nibName: "IntroCollectionViewCell", bundle: nil) + func configure(dequeueIndexPath dequeueIndexPath: NSIndexPath) { + let image = UIImage(named: String(dequeueIndexPath.row)) + imageView.image = image + setNeedsLayout() + layoutIfNeeded() + } +} diff --git a/Example/Example/IntroCollectionViewCell.xib b/Example/Example/IntroCollectionViewCell.xib new file mode 100644 index 0000000..09c026d --- /dev/null +++ b/Example/Example/IntroCollectionViewCell.xib @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/Example/IntroViewController.swift b/Example/Example/IntroViewController.swift new file mode 100644 index 0000000..0cb9eac --- /dev/null +++ b/Example/Example/IntroViewController.swift @@ -0,0 +1,55 @@ +// +// IntroViewController.swift +// Example +// +// Created by hiroyuki yoshida on 2016/01/04. +// Copyright © 2016年 hiroyuki yoshida. All rights reserved. +// + +import UIKit +import InfiniteCollectionView + +final class IntroViewController: UIViewController { + var items = ["1", "2", "3", "4"] + @IBOutlet weak var collectionView: InfiniteCollectionView! { + didSet { + collectionView.infiniteDataSource = self + collectionView.infiniteDelegate = self + collectionView.cellWidth = UIScreen.mainScreen().bounds.width + collectionView.registerNib(IntroCollectionViewCell.nib, forCellWithReuseIdentifier: IntroCollectionViewCell.identifier) + collectionView.pagingEnabled = true + } + } + @IBOutlet weak var layout: UICollectionViewFlowLayout! { + didSet { + layout.scrollDirection = .Horizontal + layout.itemSize = CGSize(width: UIScreen.mainScreen().bounds.width, height: UIScreen.mainScreen().bounds.height) + } + } + @IBOutlet weak var pageControl: UIPageControl! { + didSet { + pageControl.numberOfPages = items.count + } + } + static func createFromStoryboard() -> IntroViewController { + let storyboard = UIStoryboard(name: "Intro", bundle: nil) + return storyboard.instantiateInitialViewController() as! IntroViewController + } +} + +// MARK: - InfiniteCollectionViewDataSource, InfiniteCollectionViewDelegate +extension IntroViewController: InfiniteCollectionViewDataSource, InfiniteCollectionViewDelegate { + func numberOfItems(collectionView: UICollectionView) -> Int { + return items.count + } + func cellForItemAtIndexPath(collectionView: UICollectionView, dequeueIndexPath: NSIndexPath, indexPath: NSIndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCellWithReuseIdentifier(IntroCollectionViewCell.identifier, forIndexPath: dequeueIndexPath) as! IntroCollectionViewCell + cell.configure(dequeueIndexPath: indexPath) + return cell + } + func didSelectCellAtIndexPath(collectionView: UICollectionView, indexPath: NSIndexPath) { + } + func didUpdatePageIndex(index: Int) { + pageControl.currentPage = index + } +} diff --git a/Example/Example/Secound.storyboard b/Example/Example/Secound.storyboard new file mode 100644 index 0000000..7d88cc8 --- /dev/null +++ b/Example/Example/Secound.storyboard @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/Example/SecoundViewController.swift b/Example/Example/SecoundViewController.swift new file mode 100644 index 0000000..9913383 --- /dev/null +++ b/Example/Example/SecoundViewController.swift @@ -0,0 +1,112 @@ +// +// SecoundViewController.swift +// Example +// +// Created by hiroyuki yoshida on 2016/01/04. +// Copyright © 2016年 hiroyuki yoshida. All rights reserved. +// + +import UIKit +import InfiniteCollectionView + +final class SecoundViewController: UIViewController { + + static func createFromStoryboard() -> SecoundViewController { + let storyboard = UIStoryboard(name: "Secound", bundle: nil) + return storyboard.instantiateInitialViewController() as! SecoundViewController + } + + @IBOutlet weak var tableView: UITableView! { + didSet { + tableView.delegate = self + tableView.dataSource = self + } + } +} + + +// MARK: - UITableViewDataSource, UITableViewDelegate +extension SecoundViewController: UITableViewDataSource, UITableViewDelegate { + func numberOfSectionsInTableView(tableView: UITableView) -> Int { + return 2 + } + func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + if section == 0 { + return 1 + } else { + return 50 + } + } + func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + if indexPath.section == 0 { + let cell = tableView.dequeueReusableCellWithIdentifier(SecoundInfiniteTableViewCell.identifier) as! SecoundInfiniteTableViewCell + return cell + } else { + let cell = tableView.dequeueReusableCellWithIdentifier(SecoundTableViewCell.identifier) + cell?.textLabel?.text = String(indexPath.row) + return cell! + } + } + func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { + if indexPath.section == 0 { + return 160 + } else { + return 50 + } + } +} + +final class SecoundTableViewCell: UITableViewCell { + static let identifier = "SecoundTableViewCell" +} + +final class SecoundInfiniteTableViewCell: UITableViewCell { + static let identifier = "SecoundInfiniteTableViewCell" + var items = ["1", "2", "3", "4"] + @IBOutlet weak var collectionView: InfiniteCollectionView! { + didSet { + collectionView.infiniteDataSource = self + collectionView.infiniteDelegate = self + collectionView.cellWidth = UIScreen.mainScreen().bounds.width + collectionView.registerNib(IntroCollectionViewCell.nib, forCellWithReuseIdentifier: IntroCollectionViewCell.identifier) + collectionView.pagingEnabled = true + } + } + @IBOutlet weak var layout: UICollectionViewFlowLayout! { + didSet { + layout.scrollDirection = .Horizontal + layout.itemSize = CGSize(width: UIScreen.mainScreen().bounds.width, height: 159) + } + } + @IBOutlet weak var pageControl: UIPageControl! { + didSet { + pageControl.numberOfPages = items.count + } + } +} + +// MARK: - InfiniteCollectionViewDataSource, InfiniteCollectionViewDelegate +extension SecoundInfiniteTableViewCell: InfiniteCollectionViewDataSource, InfiniteCollectionViewDelegate { + func numberOfItems(collectionView: UICollectionView) -> Int { + return items.count + } + func cellForItemAtIndexPath(collectionView: UICollectionView, dequeueIndexPath: NSIndexPath, indexPath: NSIndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCellWithReuseIdentifier(IntroCollectionViewCell.identifier, forIndexPath: dequeueIndexPath) as! IntroCollectionViewCell + cell.configure(dequeueIndexPath: indexPath) + return cell + } + func didSelectCellAtIndexPath(collectionView: UICollectionView, indexPath: NSIndexPath) { + } + func didUpdatePageIndex(index: Int) { + pageControl.currentPage = index + } +} + + + + + + + + + diff --git a/Example/Example/ViewController.swift b/Example/Example/ViewController.swift index 7a9eba7..5faa670 100644 --- a/Example/Example/ViewController.swift +++ b/Example/Example/ViewController.swift @@ -10,7 +10,41 @@ import UIKit import InfiniteCollectionView final class ViewController: UIViewController { - - @IBOutlet weak var tableView: UITableView! - + var patterns = ["pattern1", "pattern2"] + let identifier = "tableViewCell" + @IBOutlet weak var tableView: UITableView! { + didSet { + tableView.delegate = self + tableView.dataSource = self + tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: identifier) + tableView.rowHeight = 100 + tableView.estimatedRowHeight = 100 + } + } + override func viewWillAppear(animated: Bool) { + super.viewWillAppear(animated) + if let selectedIndexPath = tableView.indexPathForSelectedRow { + tableView.deselectRowAtIndexPath(selectedIndexPath, animated: true) + } + } } + +extension ViewController: UITableViewDelegate, UITableViewDataSource { + func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return patterns.count + } + func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCellWithIdentifier(identifier)! + cell.textLabel?.text = patterns[indexPath.row] + return cell + } + func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { + if indexPath.row == 0 { + let controller = IntroViewController.createFromStoryboard() + navigationController?.pushViewController(controller, animated: true) + } else if indexPath.row == 1 { + let controller = SecoundViewController.createFromStoryboard() + navigationController?.pushViewController(controller, animated: true) + } + } +} \ No newline at end of file diff --git a/InfiniteCollectionView.podspec b/InfiniteCollectionView.podspec index b7ab21e..2fffb5e 100644 --- a/InfiniteCollectionView.podspec +++ b/InfiniteCollectionView.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "InfiniteCollectionView" s.version = "1.0.0" - s.summary = "Component which presents a dismissible view from the bottom of the screen." + s.summary = "Infinite Scrolling Using UICollectionView." s.homepage = "https://github.com/hryk224/InfiniteCollectionView" s.screenshots = "https://github.com/hryk224/InfiniteCollectionView/wiki/images/sample1.gif" s.license = { :type => "MIT", :file => "LICENSE" } diff --git a/InfiniteCollectionView/InfiniteCollectionView.swift b/InfiniteCollectionView/InfiniteCollectionView.swift index 140d65f..905f0b9 100644 --- a/InfiniteCollectionView/InfiniteCollectionView.swift +++ b/InfiniteCollectionView/InfiniteCollectionView.swift @@ -8,5 +8,123 @@ import UIKit -public class InfiniteCollectionView { -} \ No newline at end of file +public protocol InfiniteCollectionViewDataSource: class { + func cellForItemAtIndexPath(collectionView: UICollectionView, dequeueIndexPath: NSIndexPath, indexPath: NSIndexPath) -> UICollectionViewCell + func numberOfItems(collectionView: UICollectionView) -> Int +} + +@objc public protocol InfiniteCollectionViewDelegate: class { + optional func didSelectCellAtIndexPath(collectionView: UICollectionView, indexPath: NSIndexPath) + optional func didUpdatePageIndex(index: Int) +} + +public class InfiniteCollectionView: UICollectionView { + private typealias Me = InfiniteCollectionView + private static let dummyCount: Int = 3 + private static let defaultIdentifier = "Cell" + public weak var infiniteDataSource: InfiniteCollectionViewDataSource? + public weak var infiniteDelegate: InfiniteCollectionViewDelegate? + public var cellWidth: CGFloat = UIScreen.mainScreen().bounds.width + private var indexOffset: Int = 0 + private var currentIndex: Int = 0 + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + configure() + } + override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { + super.init(frame: frame, collectionViewLayout: layout) + configure() + } +} + +// MARK: - private +private extension InfiniteCollectionView { + func configure() { + backgroundColor = UIColor.clearColor() + showsHorizontalScrollIndicator = false + showsVerticalScrollIndicator = false + clipsToBounds = true + scrollEnabled = true + pagingEnabled = true + delegate = self + dataSource = self + registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: Me.defaultIdentifier) + } + + func centerIfNeeded(scrollView: UIScrollView) { + let currentOffset = contentOffset + let contentWidth = totalContentWidth() + // Calculate the centre of content X position offset and the current distance from that centre point + let centerOffsetX: CGFloat = (CGFloat(Me.dummyCount) * contentWidth - bounds.size.width) / 2 + let distFromCentre = centerOffsetX - currentOffset.x + if fabs(distFromCentre) > (contentWidth / 4) { + // Total cells (including partial cells) from centre + let cellcount = distFromCentre / cellWidth + // Amount of cells to shift (whole number) - conditional statement due to nature of +ve or -ve cellcount + let shiftCells = Int((cellcount > 0) ? floor(cellcount) : ceil(cellcount)) + // Amount left over to correct for + let offsetCorrection = (abs(cellcount) % 1) * cellWidth + // Scroll back to the centre of the view, offset by the correction to ensure it's not noticable + if centerOffsetX > contentOffset.x { + //left scrolling + contentOffset = CGPoint(x: centerOffsetX - offsetCorrection, y: currentOffset.y) + } else if contentOffset.x > centerOffsetX { + //right scrolling + contentOffset = CGPoint(x: centerOffsetX + offsetCorrection, y: currentOffset.y) + } + // Make content shift as per shiftCells + shiftContentArray(correctedIndex(shiftCells)) + reloadData() + } + let centerPoint = CGPoint(x: scrollView.frame.size.width / 2 + scrollView.contentOffset.x, y: scrollView.frame.size.height / 2 + scrollView.contentOffset.y) + guard let indexPath = indexPathForItemAtPoint(centerPoint) else { return } + infiniteDelegate?.didUpdatePageIndex?(correctedIndex(indexPath.row - indexOffset)) + } + func shiftContentArray(offset: Int) { + indexOffset += offset + } + func totalContentWidth() -> CGFloat { + let numberOfCells = infiniteDataSource?.numberOfItems(self) ?? 0 + return CGFloat(numberOfCells) * cellWidth + } + func correctedIndex(indexToCorrect: Int) -> Int { + if let numberOfItems = infiniteDataSource?.numberOfItems(self) { + if numberOfItems > indexToCorrect && indexToCorrect >= 0 { + return indexToCorrect + } else { + let countInIndex = Float(indexToCorrect) / Float(numberOfItems) + let flooredValue = Int(floor(countInIndex)) + let offset = numberOfItems * flooredValue + return indexToCorrect - offset + } + } else { + return 0 + } + } +} + +// MARK: - UICollectionViewDataSource +extension InfiniteCollectionView: UICollectionViewDataSource { + public func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + let numberOfItems = infiniteDataSource?.numberOfItems(self) ?? 0 + return Me.dummyCount * numberOfItems + } + public func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { + var maybeCell: UICollectionViewCell! + maybeCell = infiniteDataSource?.cellForItemAtIndexPath(self, dequeueIndexPath: indexPath, indexPath: NSIndexPath(forRow: correctedIndex(indexPath.row - indexOffset), inSection: 0)) + if maybeCell == nil { + maybeCell = collectionView.dequeueReusableCellWithReuseIdentifier(Me.defaultIdentifier, forIndexPath: indexPath) + } + return maybeCell + } +} + +// MARK: - UICollectionViewDelegate +extension InfiniteCollectionView: UICollectionViewDelegate { + public func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { + infiniteDelegate?.didSelectCellAtIndexPath?(self, indexPath: NSIndexPath(forRow: correctedIndex(indexPath.row - indexOffset), inSection: 0)) + } + public func scrollViewDidScroll(scrollView: UIScrollView) { + centerIfNeeded(scrollView) + } +} diff --git a/README.md b/README.md index 37d546c..9cd961e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # InfiniteCollectionView -Component which presents a dismissible view from the bottom of the screen +Infinite Scrolling Using `UICollectionView` + ## Requirements - iOS 8.0+ @@ -28,39 +29,34 @@ import InfiniteCollectionView ## Usage +#### initialize + +```Swift +@IBOutlet weak var collectionView: InfiniteCollectionView! +``` + +#### delegate, dataSource + ```Swift -let controller = InfiniteCollectionView.Controller() - -// Adds Toolbar -controller.addToolbar({ toolbar in - // toolbar -}) - -// Adds View -let view = UIView -controller.addContentsView(view) - -// Adds NavigationBar -controller.addNavigationbar(configurationHandler: { navigationBar in - // navigationBar -}) - -// Adds CollectionView -controller.addCollectionView(configurationHandler: { [weak self] collectionView in - // collectionView -}) - -// Adds TableView -controller.addTableView(configurationHandler: { [weak self] tableView in - // tableView -}) - -// customize -controller.overlayBackgroundColor = UIColor(red: 255, green: 0, blue: 0, alpha: 0.3) -controller.viewActionType = .TappedDismiss -controller.initializeHeight = 200 +collectionView.infiniteDataSource = self +collectionView.infiniteDelegate = self +collectionView.cellWidth = XXX ``` -##License +```Swift +// protocol +func cellForItemAtIndexPath(collectionView: UICollectionView, dequeueIndexPath: NSIndexPath, indexPath: NSIndexPath) -> UICollectionViewCell +func numberOfItems(collectionView: UICollectionView) -> Int + +// optional +func didSelectCellAtIndexPath(collectionView: UICollectionView, indexPath: NSIndexPath) +func didUpdatePageIndex(index: Int) +``` + +## Photos from + +* by [pakutaso.com](https://www.pakutaso.com/) + +## License This project is made available under the MIT license. See LICENSE file for details.