Created
January 6, 2017 13:32
-
-
Save valeriomazzeo/b70689fa333656f00d1f0bc52347277a to your computer and use it in GitHub Desktop.
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
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