Skip to content

Instantly share code, notes, and snippets.

@valeriomazzeo
Created January 6, 2017 13:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save valeriomazzeo/b70689fa333656f00d1f0bc52347277a to your computer and use it in GitHub Desktop.
Save valeriomazzeo/b70689fa333656f00d1f0bc52347277a to your computer and use it in GitHub Desktop.
public final class PageViewController: UIViewController {
// MARK: Initialization
public required init(viewControllers: [UIViewController]) {
self.viewControllers = viewControllers
super.init(nibName: nil, bundle: nil)
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: Setting and Getting Attributes
public var viewControllers: [UIViewController] {
didSet {
guard self.isViewLoaded else { return }
self.updateViewControllersDisplay(animated: false)
self.updateBackButtonDisplay(animated: false)
}
}
/// The visible view controller.
public var visibleViewController: UIViewController? {
return self.pageViewController.viewControllers?.first
}
// MARK: View Life Cycle
public override func viewDidLoad() {
super.viewDidLoad()
// PageViewController
self.pageViewController.willMove(toParentViewController: self)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.view.translatesAutoresizingMaskIntoConstraints = false
self.pageViewController.didMove(toParentViewController: self)
// Subviews
self.view.addSubview(self.backButton)
// Display
self.updateViewControllersDisplay(animated: false)
self.updateBackButtonDisplay(animated: false)
// Layout
self.view.setNeedsUpdateConstraints()
}
// MARK: ChildViewControllers
fileprivate lazy var pageViewController: UIPageViewController = {
let controller = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .vertical)
controller.delegate = self
controller.dataSource = self
return controller
}()
// MARK: Subviews
fileprivate lazy var backButton: UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
// Normal state
// Action
button.addTarget(self, action: #selector(backButtonDidTouchUpInside), for: .touchUpInside)
return button
}()
// MARK: Layout
private var didSetupConstraints = false
public override func updateViewConstraints() {
if !self.didSetupConstraints {
NSLayoutConstraint.activate([
self.pageViewController.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
self.pageViewController.view.topAnchor.constraint(equalTo: self.view.topAnchor),
self.pageViewController.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
self.pageViewController.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
self.backButton.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -16.0),
self.backButton.bottomAnchor.constraint(equalTo: self.bottomLayoutGuide.topAnchor, constant: -8.0)
])
self.didSetupConstraints = true
}
super.updateViewConstraints()
}
// MARK: Action Selectors
@objc private func backButtonDidTouchUpInside(sender: UIButton) {
guard let visibleViewController = self.visibleViewController else {
return
}
guard let viewController = self.pageViewController(self.pageViewController, viewControllerAfter: visibleViewController) else {
return
}
self.pageViewController.setViewControllers([viewController], direction: .forward, animated: true, completion: nil)
self.updateBackButtonDisplay(animated: false)
}
}
// MARK: - Display
extension PageViewController {
fileprivate func updateViewControllersDisplay(animated: Bool) {
var viewControllers: [UIViewController]? = nil
if let viewController = self.viewControllers.first {
viewControllers = [viewController]
}
self.pageViewController.setViewControllers(viewControllers, direction: .reverse, animated: animated, completion: nil)
}
fileprivate func updateBackButtonDisplay(animated: Bool) {
func setBackButtonAlpha(_ alpha: CGFloat) {
guard animated else {
self.backButton.alpha = alpha
return
}
UIView.animate(withDuration: 0.3) {
self.backButton.alpha = alpha
}
}
guard let visibleViewController = self.visibleViewController else {
setBackButtonAlpha(0.0)
return
}
setBackButtonAlpha(self.pageViewController(self.pageViewController, viewControllerAfter: visibleViewController) == nil ? 0.0 : 1.0)
}
}
// MARK: - UIPageViewControllerDelegate
extension PageViewController: UIPageViewControllerDelegate {
public func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
self.updateBackButtonDisplay(animated: true)
}
}
// MARK: - UIPageViewControllerDataSource
extension PageViewController: UIPageViewControllerDataSource {
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let index = self.viewControllers.index(of: viewController) else {
return nil
}
let indexAfter = self.viewControllers.index(after: index)
guard (self.viewControllers.startIndex..<self.viewControllers.endIndex).contains(indexAfter) else {
return nil
}
return self.viewControllers[indexAfter]
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let index = self.viewControllers.index(of: viewController) else {
return nil
}
let indexBefore = self.viewControllers.index(before: index)
guard (self.viewControllers.startIndex..<self.viewControllers.endIndex).contains(indexBefore) else {
return nil
}
return self.viewControllers[indexBefore]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment