Skip to content

Instantly share code, notes, and snippets.

@andreif
Created March 16, 2016 22:15
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save andreif/3bace9961c7e70995856 to your computer and use it in GitHub Desktop.
Save andreif/3bace9961c7e70995856 to your computer and use it in GitHub Desktop.
Example of UIPageViewController without storyboard i.e. created programmatically
// derived from https://www.veasoftware.com/posts/uipageviewcontroller-in-swift-xcode-62-ios-82-tutorial
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window!.rootViewController = ViewController()
window!.makeKeyAndVisible()
return true
}
}
struct Page {
var title: String
var image: String
}
let PAGES = [
Page(title: "Explore", image: "https://i.imgur.com/FGknouw.png"),
Page(title: "Today Widget", image: "https://i.imgur.com/feuwhEc.png"),
]
class ViewController: UIViewController {
var pageViewController: UIPageViewController!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.whiteColor()
let pc = UIPageControl.appearance()
pc.pageIndicatorTintColor = UIColor.lightGrayColor()
pc.currentPageIndicatorTintColor = UIColor.blackColor()
pc.backgroundColor = UIColor.whiteColor()
let btn = UIButton(type: .System)
btn.setTitle("Restart", forState: .Normal)
btn.addTarget(self, action: "restartAction:", forControlEvents: .TouchUpInside)
self.pageViewController = UIPageViewController(transitionStyle: .Scroll, navigationOrientation: .Horizontal, options: nil)
self.pageViewController.dataSource = self
self.restartAction(self)
self.addChildViewController(self.pageViewController)
let views = [
"pg": self.pageViewController.view,
"btn": btn,
]
for (_, v) in views {
v.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(v)
}
NSLayoutConstraint.activateConstraints(
[NSLayoutConstraint(
item: btn,
attribute: .CenterX,
relatedBy: .Equal,
toItem: self.view,
attribute: .CenterX,
multiplier: 1,
constant: 0),
] +
NSLayoutConstraint.constraintsWithVisualFormat("H:|[pg]|", options: .AlignAllCenterX, metrics: [:], views: views) +
NSLayoutConstraint.constraintsWithVisualFormat("V:|-30-[pg]-[btn]-15-|", options: .AlignAllCenterX, metrics: [:], views: views)
)
self.pageViewController.didMoveToParentViewController(self)
}
func restartAction(sender: AnyObject) {
self.pageViewController.setViewControllers([self.viewControllerAtIndex(0)], direction: .Forward, animated: true, completion: nil)
}
func viewControllerAtIndex(index: Int) -> ContentViewController {
if (PAGES.count == 0) || (index >= PAGES.count) {
return ContentViewController()
}
let vc = ContentViewController()
vc.pageIndex = index
return vc
}
}
// MARK: - Page View Controller Data Source
extension ViewController: UIPageViewControllerDataSource {
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == 0 || index == NSNotFound) {
return nil
}
index--
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == NSNotFound) {
return nil
}
index++
if (index == PAGES.count) {
return nil
}
return self.viewControllerAtIndex(index)
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return PAGES.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
class ContentViewController: UIViewController {
var pageIndex: Int!
override func viewDidLoad() {
super.viewDidLoad()
let lb = UILabel()
lb.textAlignment = .Center
lb.text = PAGES[self.pageIndex].title
let iv = UIImageView()
iv.contentMode = .ScaleAspectFit
ImageLoader.sharedLoader.imageForUrl(PAGES[self.pageIndex].image) { (image, url) -> () in
iv.image = image
}
let views = [
"iv": iv,
"lb": lb,
]
for (_, v) in views {
v.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(v)
}
NSLayoutConstraint.activateConstraints(
NSLayoutConstraint.constraintsWithVisualFormat("H:|-[lb]-|", options: .AlignAllCenterX, metrics: [:], views: views) +
NSLayoutConstraint.constraintsWithVisualFormat("H:|-[iv]-|", options: .AlignAllCenterX, metrics: [:], views: views) +
NSLayoutConstraint.constraintsWithVisualFormat("V:|-[lb]-[iv]-|", options: .AlignAllCenterX, metrics: [:], views: views)
)
}
}
class ImageLoader {
// source and license: https://github.com/natelyman/SwiftImageLoader
let cache = NSCache()
class var sharedLoader : ImageLoader {
struct Static {
static let instance : ImageLoader = ImageLoader()
}
return Static.instance
}
func imageForUrl(urlString: String, completionHandler: (image: UIImage?, url: String) -> ()) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {()in
let data = self.cache.objectForKey(urlString) as? NSData
if let goodData = data {
let image = UIImage(data: goodData)
dispatch_async(dispatch_get_main_queue(), {() in
completionHandler(image: image, url: urlString)
})
return
}
let downloadTask: NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: urlString)!, completionHandler: {(data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
if (error != nil) {
completionHandler(image: nil, url: urlString)
return
}
if let data = data {
let image = UIImage(data: data)
self.cache.setObject(data, forKey: urlString)
dispatch_async(dispatch_get_main_queue(), {() in
completionHandler(image: image, url: urlString)
})
return
}
})
downloadTask.resume()
})
}
}
@andreif
Copy link
Author

andreif commented Mar 16, 2016

Info.plist needs presence of Launch screen interface file base name (which can be just an empty string) in order to support all screen sizes.

@gerchicov-bp
Copy link

gerchicov-bp commented Apr 12, 2018

@adreif where is index declaration?

The second problem which allows to use this code as the example only and requires significant changes is:
extension ViewController: UIPageViewControllerDataSource {
You write an extension over all view controllers even those ones where you have never needed page view controller

@ckalbas
Copy link

ckalbas commented Apr 27, 2018

@gerchicov-bp Actually, he's extending the custom class ViewController, not UIViewController

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment