Skip to content

Instantly share code, notes, and snippets.

@SmatchyLaPaglia
Last active October 22, 2016 01:53
Show Gist options
  • Save SmatchyLaPaglia/4c4c16d5f166df99d669c0792b4ce40a to your computer and use it in GitHub Desktop.
Save SmatchyLaPaglia/4c4c16d5f166df99d669c0792b4ce40a to your computer and use it in GitHub Desktop.
A Playground demonstrating a UIViewPageController that presents pages as rounded-rect UIBurEffect views. Does most of the work of configuring itself, you just have to give it a frame and closures that generate the views for each page. Some of the code uses extensions that aren't included in this file, but it will be obvious by the error messages…
import UIKit
import Foundation
import XCPlayground
/** A ViewController with a blurView in the middle.*/
public class BlurVC: UIViewController {
//properties
/** Used by BlurVCPageController to identify the index of the viewMaker for this BlurVC. (read-only)*/
private(set) var indexInPageController: Int?
/** The inset rounded rect with a UIBlurEffect in which the page's content should be drawn. */
public var blurView = UIVisualEffectView(effect: UIBlurEffect(style: .ExtraLight))
/** A property maintained for use during viewWillAppear. If the frame for the blurView is created dynamically at that time, it behaves unpredictably in UIPageViewControllers. */
public var blurViewFrame = CGRectZero
//functions
/** Creates a new BlurVC and calculates its blurView's insets and cornerRadius using the given frame.*/
public static func newWithFrame(frame: CGRect)->BlurVC {
let blurVC = BlurVC()
blurVC.view.frame = frame
blurVC.blurViewFrame = blurViewFrameFrom(frame)
blurVC.blurView.layer.cornerRadius = frame.width * 0.025
blurVC.blurView.clipsToBounds = true
return blurVC
}
/** Returns a frame inset by a factor based on the given frame's width and height. External classes can use this method to inspect the dimensions of a blurView before its controller displays it.*/
public static func blurViewFrameFrom(frame: CGRect)->CGRect {
let (insetX, insetY) = (frame.width * 0.05, frame.height * 0.05)
var blurFrame = frame.insetBy(dx: insetX, dy: insetY)
blurFrame.origin.y = blurFrame.origin.y - (insetY * 0.4)
return blurFrame
}
//override functions
/** Calculates blurView.frame based on the current view.frame, and adds blurView as a subview.*/
public override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
blurView.frame = blurViewFrame
view.addSubview(blurView)
}
}
/** A UIPageViewController that dynamically creates BlurVCs to display views generated from its viewMakers array. .*/
public class BlurVCPageController: UIPageViewController, UIPageViewControllerDataSource {
//properties
/** Returns viewMakers.count.*/
var numPages: Int { return viewMakers.count }
/** The closures that make the content for the pages.*/
var viewMakers: [()->UIView] = []
//functions
/** Uses given index to make a view and a BlurVC around it.*/
public func blurVCPageFrom(index index: Int)->BlurVC {
let blurVC = BlurVC.newWithFrame(view.frame)
blurVC.blurView.contentView.addSubview(viewMakers[index]())
blurVC.indexInPageController = index
return blurVC
}
/** Assigns self as the data source, creates a BlurVC using the first passed-in initializer, and sets that BlurVC as the first page.*/
public func setData(to viewMakers: [()->UIView]){
assert(viewMakers.count != 0, "need one or more viewMakers")
dataSource = self
self.viewMakers = viewMakers
setViewControllers([blurVCPageFrom(index: 0)],
direction: .Forward,
animated: false,
completion: nil)
}
/** Defaults are .OverFullScreen and .CoverVertical.*/
public func useDefaultModalPresentation() {
modalPresentationStyle = .OverFullScreen
modalTransitionStyle = .CoverVertical
}
//creation functions
/** Returns a new BlurVCPageController that shows a separate BlurVC page for each viewMaker in viewMakers. Only catch is that the viewMakers don't dynamically determine thier frame. They must be already set up to generate correctly-framed views.*/
public static func usingFrame(frame: CGRect, viewMakers: [()->UIView])->BlurVCPageController{
let blurPageVC = BlurVCPageController(transitionStyle: .Scroll, navigationOrientation: .Horizontal, options: nil)
blurPageVC.view.frame = frame
blurPageVC.setData(to: viewMakers)
blurPageVC.useDefaultModalPresentation()
return blurPageVC
}
//UIPageViewControllerDataSource functions
/** Uses the indexInPageController property of the current BlurVC to generate the previous BlurVC. Note: this method is called whenever the PageController need to know about the previous page, not only when it's switching to that page. For that reason an index value cannot be incremented or decremented here.*/
public func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let blurVC = viewController as! BlurVC
let index = blurVC.indexInPageController!
if index == 0 {
return nil
} else {
return blurVCPageFrom(index: index - 1)
}
}
/** Uses the indexInPageController property of the current BlurVC to generate the previous BlurVC. Note: this method is called whenever the PageController need to know about the next page, not only when it's switching to that page. For that reason an index value cannot be incremented or decremented here.*/
public func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let blurVC = viewController as! BlurVC
let derivedIndex = blurVC.indexInPageController!
if derivedIndex + 1 == numPages {
return nil
} else {
return blurVCPageFrom(index: derivedIndex + 1)
}
}
/** Returns the number of viewMakers.*/
public func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return numPages
}
/** Returns the current page's indexInPageController.*/
public func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
let blurVC = pageViewController.viewControllers![0] as! BlurVC
return blurVC.indexInPageController!
}
}
/** A ViewController with a red box inside it.*/
class RedBoxVC: UIViewController {
//functions
func redBoxSizedTo(frame: CGRect)->UIView {
let redBox = UIView(frame: frame.insetBy(dx: 80, dy: 200))
redBox.backgroundColor = .redColor()
return redBox
}
//override functions
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
view.addSubview(redBoxSizedTo(view.frame))
}
}
/** Creates a demo BlurVCPageController as the current PlaygroundPage.*/
func demonstrate() {
let redBoxVC = RedBoxVC()
XCPlaygroundPage.currentPage.liveView = redBoxVC
let page1 = ViewSpecStruct(frame: BlurVC.blurViewFrameFrom(redBoxVC.view.frame).usingOrigin(0, 0), backgroundColor: .clearColor(), text: "Hello!")
var (page2, page3, page4) = (page1, page1, page1)
page2.text = "How are ya?"
page3.text = "How you been?"
page4.text = "What's shakin'?"
let blurPageVC = BlurVCPageController.usingFrame(redBoxVC.view.frame, viewMakers: [ page1.build, page2.build, page3.build, page4.build])
//let blurPageVC = BlurVCPageController.usingFrame(redBoxVC.view.frame, viewMakers: [ { UIView() }])
redBoxVC.presentViewController(blurPageVC, animated: true, completion: nil)
}
demonstrate()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment