Repository: lucoceano/Pager Branch: master Commit: 390520b7503b Files: 22 Total size: 74.5 KB Directory structure: gitextract_q9l42mii/ ├── .gitignore ├── .swift-version ├── .swiftlint.yml ├── LICENSE ├── Pager/ │ ├── AppDelegate.swift │ ├── Base.lproj/ │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Extensions.swift │ ├── GreyDetailViewController.swift │ ├── GreyViewController.swift │ ├── Images.xcassets/ │ │ └── AppIcon.appiconset/ │ │ └── Contents.json │ ├── Info.plist │ ├── ViewController.swift │ └── library/ │ ├── PagerController.swift │ ├── TabView.swift │ └── Utils.swift ├── Pager.podspec ├── Pager.xcodeproj/ │ ├── project.pbxproj │ └── project.xcworkspace/ │ └── contents.xcworkspacedata ├── PagerTests/ │ ├── Info.plist │ └── PagerTests.swift └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Created by .ignore support plugin (hsz.mobi) ### Swift template # Xcode # build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout *.moved-aside DerivedData *.hmap *.ipa *.xcuserstate # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control # # Pods/ # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts Carthage/Build ### macOS ### *.DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ================================================ FILE: .swift-version ================================================ 3 ================================================ FILE: .swiftlint.yml ================================================ disabled_rules: - line_length - type_name - weak_delegate force_cast: warning function_body_length: - 108 - 108 type_body_length: - 720 - 720 file_length: - 800 - 800 ================================================ FILE: LICENSE ================================================ Copyright (c) 2014-2015 Meng To (meng@designcode.io) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Pager/AppDelegate.swift ================================================ // // AppDelegate.swift // Pager // // Created by Lucas Oceano on 12/03/2015. // Copyright (c) 2015 Cheesecake. All rights reserved. // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? var navController: UINavigationController! func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { self.window = UIWindow(frame: UIScreen.main.bounds) let viewController = ViewController() let barButtomItem = UIBarButtonItem(title: "Purple Tab", style: .plain, target: viewController, action: #selector(ViewController.changeTab)) //NavigationController with title and button navController = UINavigationController(rootViewController: viewController) navController.navigationBar.topItem?.title = "Pager" navController.navigationBar.topItem?.rightBarButtonItem = barButtomItem navController.navigationBar.hideBottomHairline() self.window!.rootViewController = navController self.window?.makeKeyAndVisible() //NavigationBar customization UINavigationBar.appearance().titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white] UINavigationBar.appearance().shadowImage = UIImage() UINavigationBar.appearance().barTintColor = UIColor(rgb: 0x00AA00) UINavigationBar.appearance().tintColor = UIColor.white //Setting Status Bar to be white instead of black UIApplication.shared.statusBarStyle = .lightContent return true } } ================================================ FILE: Pager/Base.lproj/LaunchScreen.xib ================================================ ================================================ FILE: Pager/Base.lproj/Main.storyboard ================================================ ================================================ FILE: Pager/Extensions.swift ================================================ // // Extensions.swift // Pager // // Created by Lucas Farah on 3/20/16. // Copyright © 2016 Cheesecake. All rights reserved. // import UIKit extension UIColor { convenience init(rgb: UInt) { self.init( red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0, green: CGFloat((rgb & 0x00FF00) >> 8) / 255.0, blue: CGFloat(rgb & 0x0000FF) / 255.0, alpha: CGFloat(1.0) ) } } extension UINavigationBar { func hideBottomHairline() { let navigationBarImageView = hairlineImageViewInNavigationBar(self) navigationBarImageView!.isHidden = true } func showBottomHairline() { let navigationBarImageView = hairlineImageViewInNavigationBar(self) navigationBarImageView!.isHidden = false } fileprivate func hairlineImageViewInNavigationBar(_ view: UIView) -> UIImageView? { if let view = view as? UIImageView, view.bounds.size.height <= 1.0 { return view } let subviews = (view.subviews as [UIView]) for subview: UIView in subviews { if let imageView: UIImageView = hairlineImageViewInNavigationBar(subview) { return imageView } } return nil } } ================================================ FILE: Pager/GreyDetailViewController.swift ================================================ // // GreyDetailViewController.swift // Pager // // Created by Antonio Alves on 8/16/16. // Copyright © 2016 Cheesecake. All rights reserved. // import UIKit class GreyDetailViewController: UIViewController { @IBOutlet weak var detailText: UILabel! { didSet { detailText.text = text } } var text: String? } ================================================ FILE: Pager/GreyViewController.swift ================================================ // // GreyViewController.swift // Pager // // Created by Antonio Alves on 8/16/16. // Copyright © 2016 Cheesecake. All rights reserved. // import UIKit class GreyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { @IBOutlet weak var tableView: UITableView! var didSelectRow: ((String) -> Void)? override func viewDidLoad() { super.viewDidLoad() automaticallyAdjustsScrollViewInsets = false } // MARK: - Table view data source func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // #warning Incomplete implementation, return the number of rows return 10 } public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath) cell.textLabel?.text = "Example at index \(indexPath.row + 1)" return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { didSelectRow?("Detail for index \(indexPath.row + 1)") tableView.deselectRow(at: indexPath as IndexPath, animated: true) } } ================================================ FILE: Pager/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Pager/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleDisplayName Pager CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.1.1 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities armv7 UIStatusBarStyle UIStatusBarStyleLightContent UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance ================================================ FILE: Pager/ViewController.swift ================================================ // // ViewController.swift // Pager // // Created by Lucas Oceano on 12/03/2015. // Copyright (c) 2015 Cheesecake. All rights reserved. // import UIKit class ViewController: PagerController, PagerDataSource { var titles: [String] = [] override func viewDidLoad() { super.viewDidLoad() self.dataSource = self // Instantiating Storyboard ViewControllers let storyboard = UIStoryboard(name: "Main", bundle: nil) let controller1 = storyboard.instantiateViewController(withIdentifier: "firstView") let controller2 = storyboard.instantiateViewController(withIdentifier: "secondView") let controller3 = storyboard.instantiateViewController(withIdentifier: "thirdView") let controller4 = storyboard.instantiateViewController(withIdentifier: "tableView") let controller5 = storyboard.instantiateViewController(withIdentifier: "fifthView") let controller6 = storyboard.instantiateViewController(withIdentifier: "sixthView") // Setting up the PagerController with Name of the Tabs and their respective ViewControllers self.setupPager( tabNames: ["Blue", "Orange", "Light Blue", "Grey", "Purple", "Green"], tabControllers: [controller1, controller2, controller3, controller4, controller5, controller6]) customizeTab() if let controller = controller4 as? GreyViewController { controller.didSelectRow = { [weak self] (text: String) in let storyboard = UIStoryboard(name: "Main", bundle: nil) if let detail = storyboard.instantiateViewController(withIdentifier: "greyTableDetail") as? GreyDetailViewController { detail.text = text self?.navigationController?.pushViewController(detail, animated: true) } } } } // Customising the Tab's View func customizeTab() { indicatorColor = UIColor.white tabsViewBackgroundColor = UIColor(rgb: 0x00AA00) contentViewBackgroundColor = UIColor.gray.withAlphaComponent(0.32) startFromSecondTab = false centerCurrentTab = true tabLocation = PagerTabLocation.top tabHeight = 49 tabOffset = 36 tabWidth = 96.0 fixFormerTabsPositions = false fixLaterTabsPosition = false animation = PagerAnimation.during selectedTabTextColor = .blue tabsTextFont = UIFont(name: "HelveticaNeue-Bold", size: 20)! // tabTopOffset = 10.0 // tabsTextColor = .purpleColor() } // Programatically selecting a tab. This function is getting called on AppDelegate @objc func changeTab() { self.selectTabAtIndex(4) } } ================================================ FILE: Pager/library/PagerController.swift ================================================ // // ViewPager.swift // Pager // // Created by Lucas Oceano on 12/03/2015. // Copyright (c) 2015 Cheesecake. All rights reserved. // import Foundation import UIKit.UITableView // MARK: - Pager Enums //Enum for the location of the tab bar public enum PagerTabLocation: Int { case none = 0 // None will go to the bottom case top = 1 case bottom = 2 } //Enum for the animation of the tab indicator public enum PagerAnimation: Int { case none = 0 // No animation case end = 1 // pager indicator will animate after the VC changes case during = 2 // pager indicator will animate as the VC changes } // MARK: - Protocols @objc public protocol PagerDelegate: NSObjectProtocol { @objc optional func didChangeTabToIndex(_ pager: PagerController, index: Int) @objc optional func didChangeTabToIndex(_ pager: PagerController, index: Int, previousIndex: Int) @objc optional func didChangeTabToIndex(_ pager: PagerController, index: Int, previousIndex: Int, swipe: Bool) } @objc public protocol PagerDataSource: NSObjectProtocol { @objc optional func numberOfTabs(_ pager: PagerController) -> Int @objc optional func tabViewForIndex(_ index: Int, pager: PagerController) -> UIView @objc optional func viewForTabAtIndex(_ index: Int, pager: PagerController) -> UIView @objc optional func controllerForTabAtIndex(_ index: Int, pager: PagerController) -> UIViewController } open class PagerController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, UIScrollViewDelegate { // MARK: - public properties open var contentViewBackgroundColor: UIColor = UIColor.white open var indicatorColor: UIColor = UIColor.red open var tabsViewBackgroundColor: UIColor = UIColor.gray open var tabsTextColor: UIColor = UIColor.white open var selectedTabTextColor = UIColor.white open var tabsImageViewContentMode = UIViewContentMode.scaleAspectFit open weak var dataSource: PagerDataSource? open weak var delegate: PagerDelegate? open var tabHeight: CGFloat = 44.0 open var tabTopOffset: CGFloat = 0.0 open var tabOffset: CGFloat = 56.0 open var tabWidth: CGFloat = 128.0 open var tabsTextFont: UIFont = UIFont.boldSystemFont(ofSize: 16.0) open var indicatorHeight: CGFloat = 5.0 open var tabLocation: PagerTabLocation = PagerTabLocation.top open var animation: PagerAnimation = PagerAnimation.during open var startFromSecondTab: Bool = false open var centerCurrentTab: Bool = false open var fixFormerTabsPositions: Bool = false open var fixLaterTabsPosition: Bool = false open var ignoreTopBarHeight: Bool = false open var ignoreBottomBarHeight: Bool = false fileprivate var tabViews: [UIView] = [] fileprivate var tabControllers: [UIViewController] = [] // MARK: - Tab and content stuff internal var tabsView: UIScrollView? internal var pageViewController: UIPageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil) internal var actualDelegate: UIScrollViewDelegate? internal var contentView: UIView { let contentView = self.pageViewController.view contentView!.autoresizingMask = [.flexibleHeight, .flexibleWidth] contentView!.backgroundColor = self.contentViewBackgroundColor contentView!.bounds = self.view.bounds contentView!.tag = 34 return contentView! } // MARK: - Tab and content cache internal var underlineStroke: UIView = UIView() internal var tabs: [UIView?] = [] internal var contents: [UIViewController?] = [] internal var tabCount: Int = 0 internal var activeTabIndex: Int = 0 internal var activeContentIndex: Int = 0 internal var animatingToTab: Bool = false internal var defaultSetupDone: Bool = false internal var didTapOnTabView: Bool = false // MARK: - Important Methods // Initializing PagerController with Name of the Tabs and their respective ViewControllers open func setupPager(tabNames: [String], tabControllers: [UIViewController]) { let tabViews = tabNames.map { title -> UILabel in let label = UILabel() label.text = title label.textColor = tabsTextColor label.font = tabsTextFont label.backgroundColor = .clear label.sizeToFit() return label } setupPager(views: tabViews, tabControllers: tabControllers) } open func setupPager(tabImages: [UIImage], tabControllers: [UIViewController]) { let tabViews = tabImages.map { image -> UIImageView in let imageView = UIImageView(image: image) imageView.contentMode = tabsImageViewContentMode imageView.backgroundColor = .clear return imageView } setupPager(views: tabViews, tabControllers: tabControllers) } open func setupPager(views: [UIView], tabControllers: [UIViewController]) { self.tabViews = views self.tabControllers = tabControllers } open func reloadData() { self.defaultSetup() self.view.setNeedsDisplay() } open func selectTabAtIndex(_ index: Int) { self.selectTabAtIndex(index, swipe: false) } // MARK: - Other Methods override open func viewDidLoad() { super.viewDidLoad() self.defaultSettings() } override open func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) if !self.defaultSetupDone { self.defaultSetup() } } override open func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() self.layoutSubViews() } open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) self.layoutSubViews() self.changeActiveTabIndex(self.activeTabIndex) } override open func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } // MARK: - Private Methods func defaultSettings() { for (view): (UIView) in self.pageViewController.view!.subviews as [UIView] { if let view = view as? UIScrollView { self.actualDelegate = view.delegate view.delegate = self } } self.pageViewController.dataSource = self self.pageViewController.delegate = self } func defaultSetup() { // Empty tabs and contents self.tabs.forEach { (tabView) in tabView?.removeFromSuperview() } self.tabs.removeAll(keepingCapacity: true) self.contents.removeAll(keepingCapacity: true) self.underlineStroke.removeFromSuperview() // Get tabCount from dataSource if let dataSource = self.dataSource, let num = dataSource.numberOfTabs?(self) { self.tabCount = num } else { self.tabCount = tabControllers.count } // Populate arrays with nil self.tabs = Array(repeating: nil, count: self.tabCount) for _ in 0 ..< self.tabCount { self.tabs.append(nil) } self.contents = Array(repeating: nil, count: self.tabCount) for _ in 0 ..< self.tabCount { self.contents.append(nil) } // Add tabsView if self.tabsView == nil { self.tabsView = UIScrollView(frame: CGRect(x: 0.0, y: 0.0, width: self.view.frame.width, height: self.tabHeight)) self.tabsView!.autoresizingMask = .flexibleWidth self.tabsView!.backgroundColor = self.tabsViewBackgroundColor self.tabsView!.scrollsToTop = false self.tabsView?.bounces = false self.tabsView!.showsHorizontalScrollIndicator = false self.tabsView!.showsVerticalScrollIndicator = false self.tabsView?.isScrollEnabled = true self.tabsView!.tag = 38 self.view.insertSubview(self.tabsView!, at: 0) } else { self.tabsView = self.view.viewWithTag(38) as? UIScrollView } // Add tab views to _tabsView var contentSizeWidth: CGFloat = 0.0 // Give the standard offset if fixFormerTabsPositions is provided as YES if self.fixFormerTabsPositions { // And if the centerCurrentTab is provided as YES fine tune the offset according to it if self.centerCurrentTab { contentSizeWidth = (self.tabsView!.frame.width - self.tabWidth) / 2.0 } else { contentSizeWidth = self.tabOffset } } for i in 0 ..< self.tabCount { let tabView: UIView? = self.tabViewAtIndex(i) as UIView? var frame: CGRect = tabView!.frame frame.origin.x = contentSizeWidth frame.size.width = self.tabWidth tabView!.frame = frame self.tabsView!.addSubview(tabView!) contentSizeWidth += tabView!.frame.width // To capture tap events let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(PagerController.handleTapGesture(_:))) tabView!.addGestureRecognizer(tapGestureRecognizer) } // Extend contentSizeWidth if fixLatterTabsPositions is provided YES if self.fixLaterTabsPosition { // And if the centerCurrentTab is provided as YES fine tune the content size according to it if self.centerCurrentTab { contentSizeWidth += (self.tabsView!.frame.width - self.tabWidth) / 2.0 } else { contentSizeWidth += self.tabsView!.frame.width - self.tabWidth - self.tabOffset } } self.tabsView!.contentSize = CGSize(width: contentSizeWidth, height: self.tabHeight) self.view.insertSubview(self.contentView, at: 0) // Select starting tab let index: Int = self.startFromSecondTab ? 1 : 0 self.selectTabAtIndex(index, swipe: true) if self.tabCount > 0 { // creates the indicator var rect: CGRect = self.tabViewAtIndex(self.activeContentIndex)!.frame rect.origin.y = rect.size.height - self.indicatorHeight rect.size.height = self.indicatorHeight self.underlineStroke = UIView(frame: rect) self.underlineStroke.backgroundColor = self.indicatorColor self.tabsView!.addSubview(self.underlineStroke) } // Set setup done self.defaultSetupDone = true } func layoutSubViews() { var topLayoutGuide: CGFloat = 0.0 if !ignoreTopBarHeight && self.navigationController?.navigationBar.isTranslucent != false { topLayoutGuide = UIApplication.shared.isStatusBarHidden ? 0.0 : 20.0 if UIDevice.isIphoneX { topLayoutGuide += 24.0 } if let nav = self.navigationController { topLayoutGuide += nav.navigationBar.frame.size.height } } var frame: CGRect = self.tabsView!.frame frame.origin.x = 0.0 frame.origin.y = (self.tabLocation == .top) ? topLayoutGuide + tabTopOffset: self.view.frame.height - self.tabHeight frame.size.width = self.view.frame.width frame.size.height = self.tabHeight self.tabsView!.frame = frame frame = self.contentView.frame frame.origin.x = 0.0 frame.origin.y = (self.tabLocation == .top) ? topLayoutGuide + self.tabsView!.frame.height + tabTopOffset: topLayoutGuide frame.size.width = self.view.frame.width frame.size.height = self.view.frame.height - (topLayoutGuide + self.tabsView!.frame.height + tabTopOffset) if !ignoreBottomBarHeight && self.tabBarController != nil && self.tabBarController?.tabBar.isTranslucent == true { frame.size.height -= self.tabBarController!.tabBar.frame.height } self.contentView.frame = frame } func indexForViewController(_ viewController: UIViewController) -> Int { for (index, element) in self.contents.enumerated() where element == viewController { return index } return 0 } func selectTabAtIndex(_ index: Int, swipe: Bool) { if index >= self.tabCount { return } self.didTapOnTabView = !swipe self.animatingToTab = true let previousIndex: Int = self.activeTabIndex self.changeActiveTabIndex(index) self.setActiveContentIndex(index) if self.delegate != nil { if self.delegate!.responds(to: #selector(PagerDelegate.didChangeTabToIndex(_: index:))) { self.delegate!.didChangeTabToIndex!(self, index: index) } else if self.delegate!.responds(to: #selector(PagerDelegate.didChangeTabToIndex(_: index: previousIndex:))) { self.delegate!.didChangeTabToIndex!(self, index: index, previousIndex: previousIndex) } else if self.delegate!.responds(to: #selector(PagerDelegate.didChangeTabToIndex(_: index: previousIndex: swipe:))) { self.delegate!.didChangeTabToIndex!(self, index: index, previousIndex: previousIndex, swipe: swipe) } } // Updating selected tab color updateSelectedTab(index) } func updateSelectedTab(_ index: Int) { // Resetting all tab colors to white for tab in self.tabs { if let label = tab?.subviews.first as? UILabel { label.textColor = tabsTextColor } } // Setting current selected tab to red let tab = self.tabViewAtIndex(index) if let label = tab?.subviews.first as? UILabel { label.textColor = selectedTabTextColor } } func changeActiveTabIndex(_ newIndex: Int) { self.activeTabIndex = newIndex let tabView: UIView = self.tabViewAtIndex(self.activeTabIndex)! var frame: CGRect = tabView.frame if self.centerCurrentTab { if (frame.origin.x + frame.width + (self.tabsView!.frame.width / 2)) >= self.tabsView!.contentSize.width { frame.origin.x = (self.tabsView!.contentSize.width - self.tabsView!.frame.width) } else { frame.origin.x += (frame.width / 2) frame.origin.x -= (self.tabsView!.frame.width / 2) if frame.origin.x < 0 { frame.origin.x = 0 } } } else { frame.origin.x -= self.tabOffset frame.size.width = self.tabsView!.frame.width } self.tabsView!.setContentOffset(frame.origin, animated: true) } func tabViewAtIndex(_ index: Int) -> TabView? { guard let dataSource = self.dataSource, index < self.tabCount else { return nil } if (self.tabs[index] as UIView?) == nil { var tabViewContent = UIView() if let tab = dataSource.tabViewForIndex?(index, pager: self) { tabViewContent = tab } else { tabViewContent = tabViews[index] } tabViewContent.autoresizingMask = [.flexibleHeight, .flexibleWidth] let tabView: TabView = TabView(frame: CGRect(x: 0.0, y: 0.0, width: self.tabWidth, height: self.tabHeight)) tabView.addSubview(tabViewContent) tabView.clipsToBounds = true tabViewContent.center = tabView.center // Replace the null object with tabView self.tabs[index] = tabView } return self.tabs[index] as? TabView } func setNeedsReloadOptions() { // We should update contentSize property of our tabsView, so we should recalculate it with the new values var contentSizeWidth: CGFloat = 0.0 // Give the standard offset if fixFormerTabsPositions is provided as YES if self.fixFormerTabsPositions { // And if the centerCurrentTab is provided as YES fine tune the offset according to it if self.centerCurrentTab { contentSizeWidth = (self.tabsView!.frame.width - self.tabWidth) / 2.0 } else { contentSizeWidth = self.tabOffset } } // Update every tab's frame for i in 0 ..< self.tabCount { let tabView = self.tabViewAtIndex(i) var frame: CGRect = tabView!.frame frame.origin.x = contentSizeWidth frame.size.width = self.tabWidth tabView?.frame = frame contentSizeWidth += tabView!.frame.width } // Extend contentSizeWidth if fixLatterTabsPositions is provided YES if self.fixLaterTabsPosition { // And if the centerCurrentTab is provided as YES fine tune the content size according to it if self.centerCurrentTab { contentSizeWidth += (self.tabsView!.frame.width - self.tabWidth) / 2.0 } else { contentSizeWidth += self.tabsView!.frame.width - self.tabWidth - self.tabOffset } } // Update tabsView's contentSize with the new width self.tabsView!.contentSize = CGSize(width: contentSizeWidth, height: self.tabHeight) } func viewControllerAtIndex(_ index: Int) -> UIViewController? { guard let dataSource = self.dataSource, index < self.tabCount && index >= 0 else { return nil } if (self.contents[index] as UIViewController?) == nil { var viewController: UIViewController if dataSource.responds(to: #selector(PagerDataSource.controllerForTabAtIndex(_: pager:))) { viewController = dataSource.controllerForTabAtIndex!(index, pager: self) } else if dataSource.responds(to: #selector(PagerDataSource.viewForTabAtIndex(_: pager:))) { let view: UIView = dataSource.viewForTabAtIndex!(index, pager: self) // Adjust view's bounds to match the pageView's bounds let pageView: UIView = self.view.viewWithTag(34)! view.frame = pageView.bounds viewController = UIViewController() viewController.view = view } else { viewController = self.tabControllers[index] } self.contents[index] = viewController self.pageViewController.addChildViewController(viewController) } return self.contents[index] } // MARK: - Gestures @IBAction func handleTapGesture(_ sender: UITapGestureRecognizer) { let tabView: UIView = sender.view! let index: Int = self.tabs.find { $0 as UIView? == tabView }! if self.activeTabIndex != index { self.selectTabAtIndex(index) } } // MARK: - Page DataSource open func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { var index: Int = self.indexForViewController(viewController) index -= 1 return self.viewControllerAtIndex(index) } open func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { var index: Int = self.indexForViewController(viewController) index += 1 return self.viewControllerAtIndex(index) } // MARK: - Page Delegate open func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { let viewController: UIViewController = self.pageViewController.viewControllers![0] as UIViewController let index: Int = self.indexForViewController(viewController) self.selectTabAtIndex(index, swipe: true) } @nonobjc func setActiveContentIndex(_ activeContentIndex: Int) { // Get the desired viewController var viewController: UIViewController? = self.viewControllerAtIndex(activeContentIndex)! if viewController == nil { viewController = UIViewController() viewController!.view = UIView() viewController!.view.backgroundColor = UIColor.clear } weak var wPageViewController: UIPageViewController? = self.pageViewController weak var wSelf: PagerController? = self if activeContentIndex == self.activeContentIndex { DispatchQueue.main.async(execute: { self.pageViewController.setViewControllers([viewController!], direction: .forward, animated: false, completion: { _ -> Void in wSelf!.animatingToTab = false }) }) } else if !(activeContentIndex + 1 == self.activeContentIndex || activeContentIndex - 1 == self.activeContentIndex) { let direction: UIPageViewControllerNavigationDirection = (activeContentIndex < self.activeContentIndex) ? .reverse : .forward DispatchQueue.main.async(execute: { self.pageViewController.setViewControllers([viewController!], direction: direction, animated: true, completion: { completed in wSelf?.animatingToTab = false if completed { DispatchQueue.main.async(execute: { () -> Void in wPageViewController!.setViewControllers([viewController!], direction: direction, animated: false, completion: nil) }) } }) }) } else { let direction: UIPageViewControllerNavigationDirection = (activeContentIndex < self.activeContentIndex) ? .reverse : .forward DispatchQueue.main.async(execute: { self.pageViewController.setViewControllers([viewController!], direction: direction, animated: true, completion: { _ -> Void in wSelf!.animatingToTab = true }) }) } // Clean out of sight contents var index: Int = self.activeContentIndex - 1 if index >= 0 && index != activeContentIndex && index != activeContentIndex - 1 { self.contents[index] = nil } index = self.activeContentIndex if index != activeContentIndex - 1 && index != activeContentIndex && index != activeContentIndex + 1 { self.contents[index] = nil } index = self.activeContentIndex + 1 if index < self.contents.count && index != activeContentIndex && index != activeContentIndex + 1 { self.contents[index] = nil } self.activeContentIndex = activeContentIndex } // MARK: - UIScrollViewDelegate // MARK: Responding to Scrolling and Dragging open func scrollViewDidScroll(_ scrollView: UIScrollView) { if self.actualDelegate?.responds(to: #selector(UIScrollViewDelegate.scrollViewDidScroll(_:))) ?? false { self.actualDelegate?.scrollViewDidScroll?(scrollView) } let tabView: UIView = self.tabViewAtIndex(self.activeTabIndex)! if !self.animatingToTab { // Get the related tab view position var frame: CGRect = tabView.frame let movedRatio: CGFloat = (scrollView.contentOffset.x / scrollView.frame.width) - 1 frame.origin.x += movedRatio * frame.width if self.centerCurrentTab { frame.origin.x += (frame.size.width / 2) frame.origin.x -= self.tabsView!.frame.width / 2 frame.size.width = self.tabsView!.frame.width if frame.origin.x < 0 { frame.origin.x = 0 } if (frame.origin.x + frame.size.width) > self.tabsView!.contentSize.width { frame.origin.x = (self.tabsView!.contentSize.width - self.tabsView!.frame.width) } } else { frame.origin.x -= self.tabOffset frame.size.width = self.tabsView!.frame.width } self.tabsView!.scrollRectToVisible(frame, animated: false) } var rect: CGRect = tabView.frame let updateIndicator = { (newX: CGFloat) -> Void in rect.origin.x = newX rect.origin.y = self.underlineStroke.frame.origin.y rect.size.height = self.underlineStroke.frame.size.height self.underlineStroke.frame = rect } var newX: CGFloat let width: CGFloat = self.view.frame.width let distance: CGFloat = tabView.frame.size.width if self.animation == PagerAnimation.during && !self.didTapOnTabView { if scrollView.panGestureRecognizer.translation(in: scrollView.superview!).x > 0 { let mov: CGFloat = width - scrollView.contentOffset.x newX = rect.origin.x - ((distance * mov) / width) } else { let mov: CGFloat = scrollView.contentOffset.x - width newX = rect.origin.x + ((distance * mov) / width) } updateIndicator(newX) } else if self.animation == PagerAnimation.none { newX = tabView.frame.origin.x updateIndicator(newX) } else if self.animation == PagerAnimation.end || self.didTapOnTabView { newX = tabView.frame.origin.x UIView.animate(withDuration: 0.35, animations: { () -> Void in updateIndicator(newX) }) } } open func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { if self.actualDelegate != nil { if self.actualDelegate!.responds(to: #selector(UIScrollViewDelegate.scrollViewWillBeginDragging(_:))) { self.actualDelegate!.scrollViewWillBeginDragging!(scrollView) } } } open func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { if self.actualDelegate != nil { if self.actualDelegate!.responds(to: #selector(UIScrollViewDelegate.scrollViewWillEndDragging(_: withVelocity: targetContentOffset:))) { self.actualDelegate!.scrollViewWillEndDragging!(scrollView, withVelocity: velocity, targetContentOffset: targetContentOffset) } } } open func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { if self.actualDelegate != nil { if self.actualDelegate!.responds(to: #selector(UIScrollViewDelegate.scrollViewDidEndDragging(_: willDecelerate:))) { self.actualDelegate!.scrollViewDidEndDragging!(scrollView, willDecelerate: decelerate) } } self.didTapOnTabView = false } open func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool { if self.actualDelegate != nil { if self.actualDelegate!.responds(to: #selector(UIScrollViewDelegate.scrollViewShouldScrollToTop(_:))) { return self.actualDelegate!.scrollViewShouldScrollToTop!(scrollView) } } return false } open func scrollViewDidScrollToTop(_ scrollView: UIScrollView) { if self.actualDelegate != nil { if self.actualDelegate!.responds(to: #selector(UIScrollViewDelegate.scrollViewDidScrollToTop(_:))) { self.actualDelegate!.scrollViewDidScrollToTop!(scrollView) } } } open func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) { if self.actualDelegate != nil { if self.actualDelegate!.responds(to: #selector(UIScrollViewDelegate.scrollViewWillBeginDecelerating(_:))) { self.actualDelegate!.scrollViewWillBeginDecelerating!(scrollView) } } } open func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { if self.actualDelegate != nil { if self.actualDelegate!.responds(to: #selector(UIScrollViewDelegate.scrollViewDidEndDecelerating(_:))) { self.actualDelegate!.scrollViewDidEndDecelerating!(scrollView) } } self.didTapOnTabView = false } // MARK: Managing Zooming open func viewForZooming(in scrollView: UIScrollView) -> UIView? { if self.actualDelegate?.responds(to: #selector(UIScrollViewDelegate.viewForZooming(in:))) ?? false { return self.actualDelegate?.viewForZooming!(in: scrollView) } return nil } open func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) { if self.actualDelegate?.responds(to: #selector(UIScrollViewDelegate.scrollViewWillBeginZooming(_:with:))) ?? false { self.actualDelegate?.scrollViewWillBeginZooming!(scrollView, with: view) } } open func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) { if self.actualDelegate?.responds(to: #selector(UIScrollViewDelegate.scrollViewDidEndZooming(_:with:atScale:))) ?? false { self.actualDelegate?.scrollViewDidEndZooming!(scrollView, with: view, atScale: scale) } } open func scrollViewDidZoom(_ scrollView: UIScrollView) { if self.actualDelegate?.responds(to: #selector(UIScrollViewDelegate.scrollViewDidZoom(_:))) ?? false { self.actualDelegate?.scrollViewDidZoom!(scrollView) } } // UIScrollViewDelegate, Responding to Scrolling Animations open func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { if self.actualDelegate?.responds(to: #selector(UIScrollViewDelegate.scrollViewDidEndScrollingAnimation(_:))) ?? false { self.actualDelegate?.scrollViewDidEndScrollingAnimation!(scrollView) } self.didTapOnTabView = false } } ================================================ FILE: Pager/library/TabView.swift ================================================ import UIKit class TabView: UIView { override init(frame: CGRect) { super.init(frame: frame) self.backgroundColor = UIColor.clear } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.backgroundColor = UIColor.clear } } ================================================ FILE: Pager/library/Utils.swift ================================================ import UIKit extension Array { func find(_ includedElement: (Element) -> Bool) -> Int? { for (idx, element) in self.enumerated() { if includedElement(element) { return idx } } return 0 } } extension UIDevice { static var isIphoneX: Bool { var modelIdentifier = "" if isSimulator { modelIdentifier = ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "" } else { var size = 0 sysctlbyname("hw.machine", nil, &size, nil, 0) var machine = [CChar](repeating: 0, count: size) sysctlbyname("hw.machine", &machine, &size, nil, 0) modelIdentifier = String(cString: machine) } return modelIdentifier == "iPhone10,3" || modelIdentifier == "iPhone10,6" } static var isSimulator: Bool = TARGET_OS_SIMULATOR != 0 } ================================================ FILE: Pager.podspec ================================================ Pod::Spec.new do |s| s.name = 'Pager' s.version = '1.4.0' s.license = 'MIT' s.summary = 'Easily create sliding tabs with Pager.' s.homepage = 'https://github.com/lucoceano/Pager' s.authors = { 'Lucas Oceano' => 'lucoceano@ckl.io' } s.source = { :git => 'https://github.com/lucoceano/Pager.git', :tag => s.version.to_s } s.requires_arc = true s.ios.deployment_target = '8.0' s.source_files = 'Pager/library/*.swift' end ================================================ FILE: Pager.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ C2652A591C9E894D00FE8F50 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2652A581C9E894D00FE8F50 /* Extensions.swift */; }; C876EF5F2042364400701C03 /* PagerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C876EF5E2042364300701C03 /* PagerController.swift */; }; C876EF612042366800701C03 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C876EF602042366800701C03 /* Utils.swift */; }; C876EF63204236A500701C03 /* TabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C876EF62204236A500701C03 /* TabView.swift */; }; C8B7D13E1AB25F9300E9347C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B7D13D1AB25F9300E9347C /* AppDelegate.swift */; }; C8B7D1401AB25F9300E9347C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B7D13F1AB25F9300E9347C /* ViewController.swift */; }; C8B7D1431AB25F9300E9347C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C8B7D1411AB25F9300E9347C /* Main.storyboard */; }; C8B7D1451AB25F9300E9347C /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8B7D1441AB25F9300E9347C /* Images.xcassets */; }; C8B7D1481AB25F9300E9347C /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = C8B7D1461AB25F9300E9347C /* LaunchScreen.xib */; }; C8E736D71D92CAD5001491E6 /* GreyDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E736D51D92CAD5001491E6 /* GreyDetailViewController.swift */; }; C8E736D81D92CAD5001491E6 /* GreyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E736D61D92CAD5001491E6 /* GreyViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ C2652A581C9E894D00FE8F50 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; C876EF5E2042364300701C03 /* PagerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PagerController.swift; sourceTree = ""; }; C876EF602042366800701C03 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; C876EF62204236A500701C03 /* TabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabView.swift; sourceTree = ""; }; C8B7D1381AB25F9300E9347C /* Pager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pager.app; sourceTree = BUILT_PRODUCTS_DIR; }; C8B7D13C1AB25F9300E9347C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C8B7D13D1AB25F9300E9347C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C8B7D13F1AB25F9300E9347C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; C8B7D1421AB25F9300E9347C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; C8B7D1441AB25F9300E9347C /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; C8B7D1471AB25F9300E9347C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; C8B7D1521AB25F9300E9347C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C8B7D1531AB25F9300E9347C /* PagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagerTests.swift; sourceTree = ""; }; C8E736D51D92CAD5001491E6 /* GreyDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GreyDetailViewController.swift; sourceTree = ""; }; C8E736D61D92CAD5001491E6 /* GreyViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GreyViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ C8B7D1351AB25F9300E9347C /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ C876EF5D2042364300701C03 /* library */ = { isa = PBXGroup; children = ( C876EF5E2042364300701C03 /* PagerController.swift */, C876EF602042366800701C03 /* Utils.swift */, C876EF62204236A500701C03 /* TabView.swift */, ); path = library; sourceTree = ""; }; C8B7D12F1AB25F9300E9347C = { isa = PBXGroup; children = ( C8B7D13A1AB25F9300E9347C /* Pager */, C8B7D1501AB25F9300E9347C /* PagerTests */, C8B7D1391AB25F9300E9347C /* Products */, ); sourceTree = ""; }; C8B7D1391AB25F9300E9347C /* Products */ = { isa = PBXGroup; children = ( C8B7D1381AB25F9300E9347C /* Pager.app */, ); name = Products; sourceTree = ""; }; C8B7D13A1AB25F9300E9347C /* Pager */ = { isa = PBXGroup; children = ( C8E736D51D92CAD5001491E6 /* GreyDetailViewController.swift */, C8E736D61D92CAD5001491E6 /* GreyViewController.swift */, C876EF5D2042364300701C03 /* library */, C8B7D13D1AB25F9300E9347C /* AppDelegate.swift */, C8B7D13F1AB25F9300E9347C /* ViewController.swift */, C2652A581C9E894D00FE8F50 /* Extensions.swift */, C8B7D1411AB25F9300E9347C /* Main.storyboard */, C8B7D1441AB25F9300E9347C /* Images.xcassets */, C8B7D1461AB25F9300E9347C /* LaunchScreen.xib */, C8B7D13B1AB25F9300E9347C /* Supporting Files */, ); path = Pager; sourceTree = ""; }; C8B7D13B1AB25F9300E9347C /* Supporting Files */ = { isa = PBXGroup; children = ( C8B7D13C1AB25F9300E9347C /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; C8B7D1501AB25F9300E9347C /* PagerTests */ = { isa = PBXGroup; children = ( C8B7D1531AB25F9300E9347C /* PagerTests.swift */, C8B7D1511AB25F9300E9347C /* Supporting Files */, ); path = PagerTests; sourceTree = ""; }; C8B7D1511AB25F9300E9347C /* Supporting Files */ = { isa = PBXGroup; children = ( C8B7D1521AB25F9300E9347C /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ C8B7D1371AB25F9300E9347C /* Pager */ = { isa = PBXNativeTarget; buildConfigurationList = C8B7D1571AB25F9300E9347C /* Build configuration list for PBXNativeTarget "Pager" */; buildPhases = ( C8B7D1341AB25F9300E9347C /* Sources */, C8B7D1351AB25F9300E9347C /* Frameworks */, C8B7D1361AB25F9300E9347C /* Resources */, C82DD78F1D95C0F200CF596E /* Swift lint */, ); buildRules = ( ); dependencies = ( ); name = Pager; productName = Pager; productReference = C8B7D1381AB25F9300E9347C /* Pager.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ C8B7D1301AB25F9300E9347C /* Project object */ = { isa = PBXProject; attributes = { LastSwiftMigration = 0720; LastSwiftUpdateCheck = 0720; LastUpgradeCheck = 0920; ORGANIZATIONNAME = Cheesecake; TargetAttributes = { C8B7D1371AB25F9300E9347C = { CreatedOnToolsVersion = 6.1.1; LastSwiftMigration = 0920; }; }; }; buildConfigurationList = C8B7D1331AB25F9300E9347C /* Build configuration list for PBXProject "Pager" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = C8B7D12F1AB25F9300E9347C; productRefGroup = C8B7D1391AB25F9300E9347C /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( C8B7D1371AB25F9300E9347C /* Pager */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ C8B7D1361AB25F9300E9347C /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( C8B7D1431AB25F9300E9347C /* Main.storyboard in Resources */, C8B7D1481AB25F9300E9347C /* LaunchScreen.xib in Resources */, C8B7D1451AB25F9300E9347C /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ C82DD78F1D95C0F200CF596E /* Swift lint */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Swift lint"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ C8B7D1341AB25F9300E9347C /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( C876EF5F2042364400701C03 /* PagerController.swift in Sources */, C8B7D1401AB25F9300E9347C /* ViewController.swift in Sources */, C876EF63204236A500701C03 /* TabView.swift in Sources */, C8B7D13E1AB25F9300E9347C /* AppDelegate.swift in Sources */, C876EF612042366800701C03 /* Utils.swift in Sources */, C2652A591C9E894D00FE8F50 /* Extensions.swift in Sources */, C8E736D71D92CAD5001491E6 /* GreyDetailViewController.swift in Sources */, C8E736D81D92CAD5001491E6 /* GreyViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ C8B7D1411AB25F9300E9347C /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( C8B7D1421AB25F9300E9347C /* Base */, ); name = Main.storyboard; sourceTree = ""; }; C8B7D1461AB25F9300E9347C /* LaunchScreen.xib */ = { isa = PBXVariantGroup; children = ( C8B7D1471AB25F9300E9347C /* Base */, ); name = LaunchScreen.xib; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ C8B7D1551AB25F9300E9347C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.1; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; C8B7D1561AB25F9300E9347C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.1; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; }; name = Release; }; C8B7D1581AB25F9300E9347C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Pager/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = io.ckl.pager; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 4.0; }; name = Debug; }; C8B7D1591AB25F9300E9347C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Pager/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = io.ckl.pager; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 4.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ C8B7D1331AB25F9300E9347C /* Build configuration list for PBXProject "Pager" */ = { isa = XCConfigurationList; buildConfigurations = ( C8B7D1551AB25F9300E9347C /* Debug */, C8B7D1561AB25F9300E9347C /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C8B7D1571AB25F9300E9347C /* Build configuration list for PBXNativeTarget "Pager" */ = { isa = XCConfigurationList; buildConfigurations = ( C8B7D1581AB25F9300E9347C /* Debug */, C8B7D1591AB25F9300E9347C /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = C8B7D1301AB25F9300E9347C /* Project object */; } ================================================ FILE: Pager.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: PagerTests/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: PagerTests/PagerTests.swift ================================================ // // PagerTests.swift // PagerTests // // Created by Lucas Oceano on 12/03/2015. // Copyright (c) 2015 Cheesecake. All rights reserved. // import UIKit import XCTest class PagerTests: XCTestCase { override func setUp() { super.setUp() } override func tearDown() { super.tearDown() } func testExample() { // This is an example of a functional test case. XCTAssert(true, "Pass") } func testPerformanceExample() { // This is an example of a performance test case. } } ================================================ FILE: README.md ================================================ [![Language](https://img.shields.io/badge/swift-4.0-green.svg)](http://swift.org) Pager is the simplest and best way to implement sliding view controllers. ## Installation Drop in the Spring folder to your Xcode project. Or via CocoaPods pre-release: ```CocoaPods platform :ios, '8.0' pod 'Pager' use_frameworks! ``` ## Usage Subclass PagerController (as it's a `UIViewController` subclass) and implement data source methods in the subclass. #### Usage with Code ```Swift override func viewDidLoad() { super.viewDidLoad() self.dataSource = self } ``` ## Data Source ```Swift func numberOfTabs(pager: PagerController) -> Int func tabViewForIndex(index: Int, pager: PagerController) -> UIView optional func viewForTabAtIndex(index: Int, pager: PagerController) -> UIView optional func controllerForTabAtIndex(index: Int, pager: PagerController) -> UIViewController ``` ## Delegate ```Swift optional func didChangeTabToIndex(pager: PagerController, index: Int) optional func didChangeTabToIndex(pager: PagerController, index: Int, previousIndex: Int) optional func didChangeTabToIndex(pager: PagerController, index: Int, previousIndex: Int, swipe: Bool) ``` ## Contact - [Lucas Farah](mailto:lucas.farah@me.com) - [@7farah7](http://twitter.com/7farah7) - [Lucas Martins](mailto:lucoceano@gmail.com) - [lucoceano.com](http://www.lucoceano.com) Pager is a port from [CKViewPager](https://github.com/lucoceano/CKViewPager) to swift. ## Licence Pager is MIT licensed. See the LICENCE file for more info.