-
Notifications
You must be signed in to change notification settings - Fork 123
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Kazuhiro Hayashi
committed
Jul 2, 2017
0 parents
commit 44d16ac
Showing
11 changed files
with
1,109 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
7 changes: 7 additions & 0 deletions
7
PagingKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Binary file added
BIN
+56.4 KB
...t.xcodeproj/project.xcworkspace/xcuserdata/Kaz.xcuserdatad/UserInterfaceState.xcuserstate
Binary file not shown.
99 changes: 99 additions & 0 deletions
99
PagingKit.xcodeproj/xcshareddata/xcschemes/PagingKit.xcscheme
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<Scheme | ||
LastUpgradeVersion = "0830" | ||
version = "1.3"> | ||
<BuildAction | ||
parallelizeBuildables = "YES" | ||
buildImplicitDependencies = "YES"> | ||
<BuildActionEntries> | ||
<BuildActionEntry | ||
buildForTesting = "YES" | ||
buildForRunning = "YES" | ||
buildForProfiling = "YES" | ||
buildForArchiving = "YES" | ||
buildForAnalyzing = "YES"> | ||
<BuildableReference | ||
BuildableIdentifier = "primary" | ||
BlueprintIdentifier = "6EC140BA1F0940F40054B514" | ||
BuildableName = "PagingKit.framework" | ||
BlueprintName = "PagingKit" | ||
ReferencedContainer = "container:PagingKit.xcodeproj"> | ||
</BuildableReference> | ||
</BuildActionEntry> | ||
</BuildActionEntries> | ||
</BuildAction> | ||
<TestAction | ||
buildConfiguration = "Debug" | ||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||
shouldUseLaunchSchemeArgsEnv = "YES"> | ||
<Testables> | ||
<TestableReference | ||
skipped = "NO"> | ||
<BuildableReference | ||
BuildableIdentifier = "primary" | ||
BlueprintIdentifier = "6EC140C31F0940F40054B514" | ||
BuildableName = "PagingKitTests.xctest" | ||
BlueprintName = "PagingKitTests" | ||
ReferencedContainer = "container:PagingKit.xcodeproj"> | ||
</BuildableReference> | ||
</TestableReference> | ||
</Testables> | ||
<MacroExpansion> | ||
<BuildableReference | ||
BuildableIdentifier = "primary" | ||
BlueprintIdentifier = "6EC140BA1F0940F40054B514" | ||
BuildableName = "PagingKit.framework" | ||
BlueprintName = "PagingKit" | ||
ReferencedContainer = "container:PagingKit.xcodeproj"> | ||
</BuildableReference> | ||
</MacroExpansion> | ||
<AdditionalOptions> | ||
</AdditionalOptions> | ||
</TestAction> | ||
<LaunchAction | ||
buildConfiguration = "Debug" | ||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||
launchStyle = "0" | ||
useCustomWorkingDirectory = "NO" | ||
ignoresPersistentStateOnLaunch = "NO" | ||
debugDocumentVersioning = "YES" | ||
debugServiceExtension = "internal" | ||
allowLocationSimulation = "YES"> | ||
<MacroExpansion> | ||
<BuildableReference | ||
BuildableIdentifier = "primary" | ||
BlueprintIdentifier = "6EC140BA1F0940F40054B514" | ||
BuildableName = "PagingKit.framework" | ||
BlueprintName = "PagingKit" | ||
ReferencedContainer = "container:PagingKit.xcodeproj"> | ||
</BuildableReference> | ||
</MacroExpansion> | ||
<AdditionalOptions> | ||
</AdditionalOptions> | ||
</LaunchAction> | ||
<ProfileAction | ||
buildConfiguration = "Release" | ||
shouldUseLaunchSchemeArgsEnv = "YES" | ||
savedToolIdentifier = "" | ||
useCustomWorkingDirectory = "NO" | ||
debugDocumentVersioning = "YES"> | ||
<MacroExpansion> | ||
<BuildableReference | ||
BuildableIdentifier = "primary" | ||
BlueprintIdentifier = "6EC140BA1F0940F40054B514" | ||
BuildableName = "PagingKit.framework" | ||
BlueprintName = "PagingKit" | ||
ReferencedContainer = "container:PagingKit.xcodeproj"> | ||
</BuildableReference> | ||
</MacroExpansion> | ||
</ProfileAction> | ||
<AnalyzeAction | ||
buildConfiguration = "Debug"> | ||
</AnalyzeAction> | ||
<ArchiveAction | ||
buildConfiguration = "Release" | ||
revealArchiveInOrganizer = "YES"> | ||
</ArchiveAction> | ||
</Scheme> |
27 changes: 27 additions & 0 deletions
27
PagingKit.xcodeproj/xcuserdata/Kaz.xcuserdatad/xcschemes/xcschememanagement.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>SchemeUserState</key> | ||
<dict> | ||
<key>PagingKit.xcscheme_^#shared#^_</key> | ||
<dict> | ||
<key>orderHint</key> | ||
<integer>0</integer> | ||
</dict> | ||
</dict> | ||
<key>SuppressBuildableAutocreation</key> | ||
<dict> | ||
<key>6EC140BA1F0940F40054B514</key> | ||
<dict> | ||
<key>primary</key> | ||
<true/> | ||
</dict> | ||
<key>6EC140C31F0940F40054B514</key> | ||
<dict> | ||
<key>primary</key> | ||
<true/> | ||
</dict> | ||
</dict> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>CFBundleDevelopmentRegion</key> | ||
<string>en</string> | ||
<key>CFBundleExecutable</key> | ||
<string>$(EXECUTABLE_NAME)</string> | ||
<key>CFBundleIdentifier</key> | ||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
<key>CFBundleInfoDictionaryVersion</key> | ||
<string>6.0</string> | ||
<key>CFBundleName</key> | ||
<string>$(PRODUCT_NAME)</string> | ||
<key>CFBundlePackageType</key> | ||
<string>FMWK</string> | ||
<key>CFBundleShortVersionString</key> | ||
<string>1.0</string> | ||
<key>CFBundleVersion</key> | ||
<string>$(CURRENT_PROJECT_VERSION)</string> | ||
<key>NSPrincipalClass</key> | ||
<string></string> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
// | ||
// PagingContentViewController.swift | ||
// PagingViewController | ||
// | ||
// Created by Kazuhiro Hayashi on 7/2/17. | ||
// Copyright © 2017 Kazuhiro Hayashi. All rights reserved. | ||
// | ||
|
||
import UIKit | ||
|
||
public protocol PagingContentViewControllerDelegate: class { | ||
|
||
func contentViewController(viewController: PagingContentViewController, willBeginScrollFrom index: Int) | ||
func contentViewController(viewController: PagingContentViewController, didScrollOn index: Int?, percent: CGFloat) | ||
func contentViewController(viewController: PagingContentViewController, didEndScrollFrom previousIndex: Int, to nextIndex: Int, transitionComplete: Bool) | ||
} | ||
|
||
extension PagingContentViewControllerDelegate { | ||
|
||
func contentViewController(viewController: PagingContentViewController, willBeginScrollFrom index: Int){} | ||
func contentViewController(viewController: PagingContentViewController, didScrollOn index: Int?, percent: CGFloat) {} | ||
func contentViewController(viewController: PagingContentViewController, didEndScrollFrom previousIndex: Int, to nextIndex: Int, transitionComplete: Bool) {} | ||
} | ||
|
||
public protocol PagingContentViewControllerDataSource: class { | ||
func numberOfItemForContentViewController(viewController: PagingContentViewController) -> Int | ||
func contentViewController(viewController: PagingContentViewController, viewControllerAt Index: Int) -> UIViewController | ||
} | ||
|
||
public class PagingContentViewController: UIViewController { | ||
private var cachedViewControllers = [UIViewController?]() | ||
|
||
public weak var delegate: PagingContentViewControllerDelegate? | ||
public weak var dataSource: PagingContentViewControllerDataSource? | ||
|
||
public var contentOffsetRatio: CGFloat { | ||
return scrollView.contentOffset.x / (scrollView.contentSize.width - scrollView.bounds.width) | ||
} | ||
|
||
public var pagingPercent: CGFloat { | ||
return scrollView.contentOffset.x.truncatingRemainder(dividingBy: scrollView.bounds.width) / scrollView.bounds.width | ||
} | ||
|
||
public func scroll(to page: Int, animated: Bool) { | ||
isProgramaticallyScrolling = true | ||
let offsetX = scrollView.bounds.width * CGFloat(page) | ||
let offset = CGPoint(x: offsetX, y: 0) | ||
loadPagesIfNeeded(page: page) | ||
scrollView.setContentOffset(offset, animated: animated) | ||
if !animated { | ||
isProgramaticallyScrolling = false | ||
} | ||
} | ||
|
||
fileprivate var numberOfPages: Int = 0 | ||
|
||
fileprivate var lastContentOffset = CGPoint.zero | ||
fileprivate var leftSidePageIndex = 0 | ||
fileprivate(set) var isProgramaticallyScrolling = false | ||
|
||
fileprivate let scrollView: UIScrollView = { | ||
let scrollView = UIScrollView() | ||
scrollView.isPagingEnabled = true | ||
scrollView.bounces = false | ||
scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleTopMargin, .flexibleLeftMargin] | ||
scrollView.translatesAutoresizingMaskIntoConstraints = true | ||
return scrollView | ||
}() | ||
|
||
override public func viewDidLoad() { | ||
super.viewDidLoad() | ||
|
||
scrollView.frame = view.bounds | ||
scrollView.delegate = self | ||
view.addSubview(scrollView) | ||
} | ||
|
||
override public func viewWillAppear(_ animated: Bool) { | ||
super.viewWillAppear(animated) | ||
|
||
if cachedViewControllers.isEmpty { | ||
initialLoad(with: 0) | ||
} | ||
} | ||
|
||
override public func viewDidLayoutSubviews() { | ||
super.viewDidLayoutSubviews() | ||
scrollView.contentSize = CGSize(width: view.bounds.size.width * CGFloat(numberOfPages), height: view.bounds.size.height) | ||
cachedViewControllers.enumerated().forEach { (offset, vc) in | ||
vc?.view.frame = scrollView.bounds | ||
vc?.view.frame.origin.x = scrollView.bounds.width * CGFloat(offset) | ||
} | ||
} | ||
|
||
override public func didReceiveMemoryWarning() { | ||
super.didReceiveMemoryWarning() | ||
} | ||
|
||
override public func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { | ||
super.viewWillTransition(to: size, with: coordinator) | ||
|
||
let currentIndex = Int(scrollView.contentOffset.x / scrollView.bounds.width) | ||
coordinator.animate(alongsideTransition: { [weak self] (context) in | ||
guard let _self = self else { return } | ||
_self.initialLoad(with: currentIndex) | ||
|
||
let point = CGPoint(x: _self.scrollView.bounds.width * CGFloat(currentIndex), y: 0) | ||
_self.scrollView.setContentOffset(point, animated: false) | ||
}) { (context) in } | ||
} | ||
|
||
fileprivate func initialLoad(with page: Int) { | ||
scrollView.subviews.forEach { $0.removeFromSuperview() } | ||
childViewControllers.forEach { $0.removeFromParentViewController() } | ||
|
||
numberOfPages = dataSource?.numberOfItemForContentViewController(viewController: self) ?? 0 | ||
cachedViewControllers = Array(repeating: nil, count: numberOfPages) | ||
|
||
loadScrollView(with: page - 1) | ||
loadScrollView(with: page) | ||
loadScrollView(with: page + 1) | ||
} | ||
|
||
fileprivate func loadScrollView(with page: Int) { | ||
guard (0..<cachedViewControllers.count) ~= page else { return } | ||
|
||
if case nil = cachedViewControllers[page], let dataSource = dataSource { | ||
let vc = dataSource.contentViewController(viewController: self, viewControllerAt: page) | ||
addChildViewController(vc) | ||
vc.view.frame = scrollView.bounds | ||
vc.view.frame.origin.x = scrollView.bounds.width * CGFloat(page) | ||
scrollView.addSubview(vc.view) | ||
vc.didMove(toParentViewController: self) | ||
cachedViewControllers[page] = vc | ||
} | ||
} | ||
} | ||
|
||
extension PagingContentViewController: UIScrollViewDelegate { | ||
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { | ||
guard !isProgramaticallyScrolling else { return } | ||
|
||
delegate?.contentViewController(viewController: self, willBeginScrollFrom: leftSidePageIndex) | ||
} | ||
|
||
public func scrollViewDidScroll(_ scrollView: UIScrollView) { | ||
guard !isProgramaticallyScrolling else { return } | ||
|
||
let leftSideContentOffset = CGFloat(leftSidePageIndex) * scrollView.bounds.width | ||
let percent = (scrollView.contentOffset.x - leftSideContentOffset) / scrollView.bounds.width | ||
|
||
delegate?.contentViewController(viewController: self, didScrollOn: leftSidePageIndex, percent: min(max(0, percent), 1)) | ||
|
||
lastContentOffset = scrollView.contentOffset | ||
leftSidePageIndex = Int(scrollView.contentOffset.x / scrollView.bounds.width) | ||
} | ||
|
||
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { | ||
loadPagesIfNeeded() | ||
} | ||
|
||
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { | ||
if !decelerate { | ||
loadPagesIfNeeded() | ||
} | ||
} | ||
|
||
public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { | ||
isProgramaticallyScrolling = false | ||
} | ||
|
||
fileprivate func loadPagesIfNeeded(page: Int? = nil) { | ||
let currentIndex = Int(scrollView.contentOffset.x / scrollView.bounds.width) | ||
let loadingPage = page ?? currentIndex | ||
loadScrollView(with: loadingPage - 1) | ||
loadScrollView(with: loadingPage) | ||
loadScrollView(with: loadingPage + 1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// | ||
// PagingKit.h | ||
// PagingKit | ||
// | ||
// Created by Kazuhiro Hayashi on 7/2/17. | ||
// Copyright © 2017 Kazuhiro Hayashi. All rights reserved. | ||
// | ||
|
||
#import <UIKit/UIKit.h> | ||
|
||
//! Project version number for PagingKit. | ||
FOUNDATION_EXPORT double PagingKitVersionNumber; | ||
|
||
//! Project version string for PagingKit. | ||
FOUNDATION_EXPORT const unsigned char PagingKitVersionString[]; | ||
|
||
// In this header, you should import all the public headers of your framework using statements like #import <PagingKit/PublicHeader.h> | ||
|
||
|
Oops, something went wrong.