From 7f7e5fd1df0941eada0e3db3b430dd0aef249435 Mon Sep 17 00:00:00 2001 From: hryk224 Date: Wed, 19 Oct 2016 17:10:31 +0900 Subject: [PATCH] Support orientation changed --- Example/Example.xcodeproj/project.pbxproj | 7 +- Example/Example/ImageCollectionViewCell.swift | 4 +- Example/Example/Pattern1.storyboard | 16 ++-- Example/Example/Pattern1ViewController.swift | 12 ++- Example/Example/Pattern2.storyboard | 30 ++++--- Example/Example/Pattern2ViewController.swift | 28 +++++- .../InfiniteCollectionView.swift | 86 ++++++++++--------- 7 files changed, 119 insertions(+), 64 deletions(-) diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index b650ae5..2570874 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -163,7 +163,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0730; + LastUpgradeCheck = 0810; ORGANIZATIONNAME = "hiroyuki yoshida"; TargetAttributes = { 935BD6C41CE26EB600E5FDF7 = { @@ -279,8 +279,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -324,8 +326,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -344,6 +348,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; }; name = Release; diff --git a/Example/Example/ImageCollectionViewCell.swift b/Example/Example/ImageCollectionViewCell.swift index 966464a..e4dd723 100644 --- a/Example/Example/ImageCollectionViewCell.swift +++ b/Example/Example/ImageCollectionViewCell.swift @@ -12,8 +12,8 @@ final class ImageCollectionViewCell: UICollectionViewCell { @IBOutlet weak var imageView: UIImageView! static let identifier = "ImageCollectionViewCell" static let nib = UINib(nibName: "ImageCollectionViewCell", bundle: nil) - func configure(dequeueIndexPath: IndexPath) { - let image = UIImage(named: String(dequeueIndexPath.item)) + func configure(_ indexPath: IndexPath) { + let image = UIImage(named: String(indexPath.item)) imageView.image = image setNeedsLayout() layoutIfNeeded() diff --git a/Example/Example/Pattern1.storyboard b/Example/Example/Pattern1.storyboard index e748ad1..6f36083 100644 --- a/Example/Example/Pattern1.storyboard +++ b/Example/Example/Pattern1.storyboard @@ -1,8 +1,11 @@ - - + + + + + - + @@ -19,6 +22,7 @@ + @@ -28,13 +32,15 @@ - + + - + + diff --git a/Example/Example/Pattern1ViewController.swift b/Example/Example/Pattern1ViewController.swift index 5c754fd..7c68f7f 100644 --- a/Example/Example/Pattern1ViewController.swift +++ b/Example/Example/Pattern1ViewController.swift @@ -21,7 +21,7 @@ final class Pattern1ViewController: UIViewController { } @IBOutlet weak var layout: UICollectionViewFlowLayout! { didSet { - layout.itemSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height) + layout.itemSize = UIScreen.main.bounds.size } } @IBOutlet weak var pageControl: UIPageControl! { @@ -33,6 +33,14 @@ final class Pattern1ViewController: UIViewController { let storyboard = UIStoryboard(name: "Pattern1", bundle: nil) return storyboard.instantiateInitialViewController() as! Pattern1ViewController } + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + layout.itemSize = size + layout.invalidateLayout() + collectionView.cellWidth = size.width + collectionView.layoutIfNeeded() + collectionView.setNeedsLayout() + } } // MARK: - InfiniteCollectionViewDataSource, InfiniteCollectionViewDelegate @@ -42,7 +50,7 @@ extension Pattern1ViewController: InfiniteCollectionViewDataSource, InfiniteColl } func collectionView(_ collectionView: UICollectionView, dequeueForItemAt dequeueIndexPath: IndexPath, cellForItemAt usableIndexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImageCollectionViewCell.identifier, for: dequeueIndexPath) as! ImageCollectionViewCell - cell.configure(dequeueIndexPath: usableIndexPath) + cell.configure(usableIndexPath) return cell } func infiniteCollectionView(_ collectionView: UICollectionView, didSelectItemAt usableIndexPath: IndexPath) { diff --git a/Example/Example/Pattern2.storyboard b/Example/Example/Pattern2.storyboard index 3ccbdf7..1c3b140 100644 --- a/Example/Example/Pattern2.storyboard +++ b/Example/Example/Pattern2.storyboard @@ -1,8 +1,11 @@ - - + + + + + - + @@ -19,16 +22,18 @@ + - + + @@ -38,13 +43,15 @@ - + - + + + @@ -60,13 +67,15 @@ - + + - + + @@ -97,10 +106,11 @@ - + + @@ -110,7 +120,7 @@ - + diff --git a/Example/Example/Pattern2ViewController.swift b/Example/Example/Pattern2ViewController.swift index 5502652..a4c5382 100644 --- a/Example/Example/Pattern2ViewController.swift +++ b/Example/Example/Pattern2ViewController.swift @@ -62,6 +62,13 @@ extension Pattern2ViewController: UITableViewDataSource, UITableViewDelegate { final class InfiniteTableViewCell: UITableViewCell { static let identifier = "InfiniteTableViewCell" + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + NotificationCenter.default.addObserver(self, selector: #selector(InfiniteTableViewCell.rotate(_:)), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil) + } + deinit { + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil) + } @IBOutlet weak var collectionView: InfiniteCollectionView! { didSet { collectionView.infiniteDataSource = self @@ -80,6 +87,14 @@ final class InfiniteTableViewCell: UITableViewCell { pageControl.numberOfPages = 4 } } + func rotate(_ notification: Notification) { + let size = CGSize(width: UIScreen.main.bounds.width, height: 239) + layout.itemSize = size + layout.invalidateLayout() + collectionView.cellWidth = size.width + collectionView.layoutIfNeeded() + collectionView.setNeedsLayout() + } } // MARK: - InfiniteCollectionViewDataSource, InfiniteCollectionViewDelegate @@ -89,7 +104,7 @@ extension InfiniteTableViewCell: InfiniteCollectionViewDataSource, InfiniteColle } func collectionView(_ collectionView: UICollectionView, dequeueForItemAt dequeueIndexPath: IndexPath, cellForItemAt usableIndexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImageCollectionViewCell.identifier, for: dequeueIndexPath) as! ImageCollectionViewCell - cell.configure(dequeueIndexPath: usableIndexPath) + cell.configure(usableIndexPath) return cell } func scrollView(_ scrollView: UIScrollView, pageIndex: Int) { @@ -99,6 +114,13 @@ extension InfiniteTableViewCell: InfiniteCollectionViewDataSource, InfiniteColle final class Infinite2TableViewCell: UITableViewCell { static let identifier = "Infinite2TableViewCell" + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + NotificationCenter.default.addObserver(self, selector: #selector(Infinite2TableViewCell.rotate(_:)), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil) + } + deinit { + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil) + } @IBOutlet weak var collectionView: InfiniteCollectionView! { didSet { collectionView.infiniteDataSource = self @@ -116,10 +138,12 @@ extension Infinite2TableViewCell: InfiniteCollectionViewDataSource, InfiniteColl } func collectionView(_ collectionView: UICollectionView, dequeueForItemAt dequeueIndexPath: IndexPath, cellForItemAt usableIndexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImageCollectionViewCell.identifier, for: dequeueIndexPath) as! ImageCollectionViewCell - cell.configure(dequeueIndexPath: usableIndexPath) + cell.configure(usableIndexPath) return cell } func infiniteCollectionView(_ collectionView: UICollectionView, didSelectItemAt usableIndexPath: IndexPath) { print("didSelectItemAt: \(usableIndexPath.item)") } + func rotate(_ notification: Notification) { + } } diff --git a/InfiniteCollectionView/InfiniteCollectionView.swift b/InfiniteCollectionView/InfiniteCollectionView.swift index cb73c31..4b70692 100644 --- a/InfiniteCollectionView/InfiniteCollectionView.swift +++ b/InfiniteCollectionView/InfiniteCollectionView.swift @@ -32,9 +32,15 @@ open class InfiniteCollectionView: UICollectionView { fileprivate static let defaultIdentifier = "Cell" open weak var infiniteDataSource: InfiniteCollectionViewDataSource? open weak var infiniteDelegate: InfiniteCollectionViewDelegate? - open var cellWidth: CGFloat = UIScreen.main.bounds.width + open var cellWidth: CGFloat = UIScreen.main.bounds.width { + didSet { + contentWidth = totalContentWidth + setContentOffset(CGPoint(x: CGFloat(pageIndex + indexOffset) * cellWidth, y: contentOffset.y), animated: false) + } + } fileprivate var indexOffset: Int = 0 - fileprivate var currentIndex: Int = 0 + fileprivate var contentWidth: CGFloat = 0 + fileprivate var pageIndex = 0 required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) configure() @@ -43,65 +49,63 @@ open class InfiniteCollectionView: UICollectionView { super.init(frame: frame, collectionViewLayout: layout) configure() } + deinit { + NotificationCenter.default.removeObserver(self, name: .UIDeviceOrientationDidChange, object: nil) + } + open override func reloadData() { + super.reloadData() + contentWidth = totalContentWidth + } + func rotate(_ notification: Notification) { + contentWidth = totalContentWidth + } } // MARK: - private private extension InfiniteCollectionView { + var totalContentWidth: CGFloat { + let numberOfCells = infiniteDataSource?.number(ofItems: self) ?? 0 + return CGFloat(numberOfCells) * cellWidth + } func configure() { delegate = self dataSource = self register(UICollectionViewCell.self, forCellWithReuseIdentifier: Me.defaultIdentifier) + NotificationCenter.default.addObserver(self, selector: #selector(InfiniteCollectionView.rotate(_:)), name: .UIDeviceOrientationDidChange, object: nil) } - 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 centerX = (CGFloat(Me.dummyCount) * contentWidth - bounds.width) / 2 + let distFromCenter = centerX - currentOffset.x + if fabs(distFromCenter) > (contentWidth / 4) { + let cellcount = distFromCenter / cellWidth let shiftCells = Int((cellcount > 0) ? floor(cellcount) : ceil(cellcount)) - // Amount left over to correct for let offsetCorrection = (abs(cellcount).truncatingRemainder(dividingBy: 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) + if centerX > contentOffset.x { + contentOffset = CGPoint(x: centerX - offsetCorrection, y: currentOffset.y) + } else { + contentOffset = CGPoint(x: centerX + 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 = indexPathForItem(at: centerPoint) else { return } - infiniteDelegate?.scrollView?(scrollView, pageIndex: correctedIndex(indexPath.item - indexOffset)) + pageIndex = correctedIndex(indexPath.item - indexOffset) + infiniteDelegate?.scrollView?(scrollView, pageIndex: pageIndex) } func shiftContentArray(_ offset: Int) { indexOffset += offset } - func totalContentWidth() -> CGFloat { - let numberOfCells = infiniteDataSource?.number(ofItems: self) ?? 0 - return CGFloat(numberOfCells) * cellWidth - } func correctedIndex(_ indexToCorrect: Int) -> Int { - if let numberOfItems = infiniteDataSource?.number(ofItems: 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 + guard let numberOfItems = infiniteDataSource?.number(ofItems: self) else { return 0 } + if numberOfItems > indexToCorrect && indexToCorrect >= 0 { + return indexToCorrect } + let countInIndex = Float(indexToCorrect) / Float(numberOfItems) + let flooredValue = Int(floor(countInIndex)) + let offset = numberOfItems * flooredValue + return indexToCorrect - offset } } @@ -112,19 +116,17 @@ extension InfiniteCollectionView: UICollectionViewDataSource { return Me.dummyCount * numberOfItems } public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - var maybeCell: UICollectionViewCell! - maybeCell = infiniteDataSource?.collectionView(collectionView, dequeueForItemAt: indexPath, cellForItemAt: IndexPath(row: correctedIndex(indexPath.item - indexOffset), section: 0)) - if maybeCell == nil { - maybeCell = collectionView.dequeueReusableCell(withReuseIdentifier: Me.defaultIdentifier, for: indexPath) + if let maybeCell = infiniteDataSource?.collectionView(collectionView, dequeueForItemAt: indexPath, cellForItemAt: IndexPath(item: correctedIndex(indexPath.item - indexOffset), section: 0)) { + return maybeCell } - return maybeCell + return collectionView.dequeueReusableCell(withReuseIdentifier: Me.defaultIdentifier, for: indexPath) } } // MARK: - UICollectionViewDelegate extension InfiniteCollectionView: UICollectionViewDelegate { public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - infiniteDelegate?.infiniteCollectionView?(collectionView, didSelectItemAt: IndexPath(row: correctedIndex(indexPath.item - indexOffset), section: 0)) + infiniteDelegate?.infiniteCollectionView?(collectionView, didSelectItemAt: IndexPath(item: correctedIndex(indexPath.item - indexOffset), section: 0)) } public func scrollViewDidScroll(_ scrollView: UIScrollView) { centerIfNeeded(scrollView)