As the title states, there is a way it can return the wrong index. This messes up the index presentation dots at the bottom of the page. The way this is done is by skipping a page without releasing a finger from the screen. If this happens, it messes of the rest of the presentation dots.
here is what the bug it looks like in action.
And here is the code that was used for the UIPageViewController.
import UIKit
class PageViewController: UIPageViewController {
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return UIStatusBarStyle.LightContent
}
private(set) lazy var orderedViewControllers: [UIViewController] = {
return [self.newColoredViewController("Green"),
self.newColoredViewController("Red"),
self.newColoredViewController("Blue")]
}()
private func newColoredViewController(color: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil) .
instantiateViewControllerWithIdentifier("\(color)ViewController")
}
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController],
direction: .Forward,
animated: true,
completion: nil)
}
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
//MARK: UIPageViewControllerDataSource
extension PageViewController: UIPageViewControllerDataSource {
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return nil
}
guard orderedViewControllers.count > previousIndex else {
return nil
}
return orderedViewControllers[previousIndex]
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = orderedViewControllers.count
guard orderedViewControllersCount != nextIndex else {
return nil
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return orderedViewControllers.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first,
firstViewControllerIndex = orderedViewControllers.indexOf(firstViewController) else {
return 0
}
return firstViewControllerIndex
}
}
Any help with this would be greatly appreciated, thanks.
I made a workaround because this is still not fixed!! It has been more than 4 years darn it.
Class to subclass instead of UIPageViewController:
import UIKit
class PageController: UIPageViewController {
lazy var pages: [UIViewController] = []
var newOffset: CGFloat = 20
var showReal = true
var pageControlX: CGFloat = 0 // 0 is the middle of the screen
var pageControlY: CGFloat = 200
private var scrollView: UIScrollView?
private var scrollPoints: [UIView] = []
private var oldScrollPoints: [UIView] = []
private var indexKeeper = IndexKeeper()
private var selectedColor = UIColor(white: 1, alpha: 1)
private var unselectedColor = UIColor(white: 1, alpha: 0.2)
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// whole screen scroll
scrollView = view.subviews.filter{ $0 is UIScrollView }.first! as? UIScrollView
scrollView!.frame = UIScreen.main.bounds
// get pageControl and scroll view from view's subviews
let pageControl = view.subviews.filter{ $0 is UIPageControl }.first! as! UIPageControl
oldScrollPoints = pageControl.subviews
// remove all constraint from view that are tied to pageControl
let const = view.constraints.filter { $0.firstItem as? NSObject == pageControl || $0.secondItem as? NSObject == pageControl }
view.removeConstraints(const)
// customize pageControl
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.frame = CGRect(x: pageControlX, y: pageControlY,
width: view.frame.width, height: 0)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
self.replacePoints()
self.updateIndex()
}
}
private func replacePoints() {
for point in scrollPoints {
point.removeFromSuperview()
}
scrollPoints = []
for oldPoint in oldScrollPoints {
let newPoint = UIView(frame: oldPoint.frame)
// make rounded
newPoint.layer.borderWidth = 0
newPoint.layer.masksToBounds = false
newPoint.layer.cornerRadius = newPoint.frame.height / 2
newPoint.clipsToBounds = true
newPoint.center = CGPoint(x: oldPoint.center.x, y: newOffset)
newPoint.backgroundColor = oldPoint.backgroundColor
oldPoint.superview?.addSubview(newPoint)
scrollPoints.append(newPoint)
oldPoint.alpha = showReal ? 1 : 0
}
}
private func offsetFromFirstPage() -> CGFloat {
var coordinates: [CGFloat] = []
for (index, page) in pages.enumerated() {
let offset = scrollView!.convert(scrollView!.bounds.origin, to: page.view!)
coordinates.append(offset.x + CGFloat(index) * view.frame.width)
}
let duplicates: [CGFloat] = coordinates.enumerated().map
{ current in
if coordinates.firstIndex(of: current.element) != coordinates.lastIndex(of: current.element) {
return current.element
} else {
return .nan
}
}
return duplicates.first(where: { !$0.isNaN }) ?? 0
}
private var lastIndex: Int = 0
private func pageIndexFrom(offset: CGFloat, visibleRatio: CGFloat=0.6) -> Int {
let adjustedOffset = (offset / view.frame.width)
if adjustedOffset > CGFloat(lastIndex) + visibleRatio {
if lastIndex + 1 < pages.count {
lastIndex += 1
}
} else if adjustedOffset < CGFloat(lastIndex) - visibleRatio {
if lastIndex - 1 >= 0 {
lastIndex -= 1
}
}
return lastIndex
}
private func updateIndex() {
let fastIndex = pages.firstIndex(of: viewControllers!.first!)!
indexKeeper.fastIndexUpdate(index: fastIndex)
let offset = offsetFromFirstPage()
let slowIndex = pageIndexFrom(offset: offset)
indexKeeper.slowIndexUpdate(index: slowIndex)
for (index, point) in scrollPoints.enumerated() {
if index == indexKeeper.finalIndex {
point.backgroundColor = selectedColor
} else {
point.backgroundColor = unselectedColor
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
self.updateIndex()
}
}
}
class IndexKeeper {
var finalIndex = 0
private var slowIndex = 0
private var fastIndex = 0
func slowIndexUpdate(index: Int) {
if index != slowIndex {
slowIndex = index
finalIndex = slowIndex
}
}
func fastIndexUpdate(index: Int) {
if index != fastIndex {
fastIndex = index
finalIndex = fastIndex
}
}
}
Example Use:
import UIKit
class PageViewController: PageController, UIPageViewControllerDataSource {
private func pageInstance(name:String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: name)
}
override func viewDidLoad() {
super.viewDidLoad()
pages = [
pageInstance(name: "FirstPage"),
pageInstance(name: "SecondPage"),
pageInstance(name: "ThirdPage"),
pageInstance(name: "FourthPage")
]
dataSource = self
setViewControllers([pages.first!], direction: .forward, animated: false, completion: nil)
}
// get page before current page
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let index = pages.firstIndex(of: viewController), index > 0 else {
return nil
}
return pages[index - 1]
}
// get page after current page
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let index = pages.firstIndex(of: viewController), index + 1 < pages.count else {
return nil
}
return pages[index + 1]
}
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return pages.count
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
if let vc = viewControllers?.first {
return pages.firstIndex(of: vc)!
}
return 0
}
}
Related
I created a PageController with 3 pages .. I allocated and declared everything, I implemented the two functions that allow scrolling views, in the same I entered the commands to enable the controller with the right page reported. the result is that if I flow my pages is all ok, while under the page counter works as he wants.. and I can not understand what is wrong
problemproblem2problem3
import UIKit
import AVFoundation
protocol IntroNavigationDelegate: class {
func showNextViewController()
func showPreviousViewController()
var isPagingEnabled: Bool { get set }
}
final class IntroViewController: UIPageViewController, UIPageViewControllerDelegate, IntroNavigationDelegate, UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
if let viewControllerIndex = self.displayedControllers.index(of: viewController) {
if viewControllerIndex == 0 {
} else if viewControllerIndex == 1 {
return self.displayedControllers[viewControllerIndex - 1]
}
}
return nil
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
if let viewControllerIndex = self.displayedControllers.index(of: viewController) {
if viewControllerIndex < self.displayedControllers.count - 1 {
self.pageControl.currentPage = 1
return self.displayedControllers[viewControllerIndex + 1]
} else {
self.pageControl.currentPage = 0
}
}
return nil
}
var introRouter: IntroRouter?
var pageControl = UIPageControl()
var displayedControllers: [UIViewController] = []
private var scrollView: UIScrollView? {
for view in view.subviews {
if let subView = view as? UIScrollView {
return subView
}
}
return nil
}
var isPagingEnabled: Bool {
get {
return scrollView?.isScrollEnabled ?? true
}
set {
scrollView?.isScrollEnabled = newValue
}
}
override func viewDidLoad() {
super.viewDidLoad()
configurePageControl()
setupUI()
self.pageControl.updateCurrentPageDisplay()
arrangeSubviews()
self.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.isNavigationBarHidden = false
}
func setDiplayedControllers(_ controllers: [UIViewController], visualizedController: UIViewController) {
displayedControllers = controllers
setViewControllers([visualizedController], direction: .forward, animated: false, completion: nil)
}
func showNextViewController() {
if let current = viewControllers?.first, let next = IntroViewController(self, viewControllerAfter: current) {
setViewControllers([next], direction: .forward, animated: true, completion: nil)
}
}
func showPreviousViewController() {
if let current = viewControllers?.first, let previous = IntroViewController(self, viewControllerBefore: current) {
setViewControllers([previous], direction: .reverse, animated: true, completion: nil)
}
}
func configurePageControl(){
self.pageControl.frame = CGRect()
self.pageControl.numberOfPages = self.displayedControllers.count
self.pageControl.translatesAutoresizingMaskIntoConstraints = false
self.pageControl.updateCurrentPageDisplay()
self.view.addSubview(self.pageControl)
pageControl.activate([
pageControl.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -20),
pageControl.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.1),
pageControl.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 1),
])
}
override init(transitionStyle style: UIPageViewControllerTransitionStyle, navigationOrientation: UIPageViewControllerNavigationOrientation, options: [String : Any]? = nil) {
super.init(transitionStyle: style, navigationOrientation: navigationOrientation, options: options)
setup()
}
func setRouter(introRouter: IntroRouter) {
self.introRouter = introRouter
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setup() {
view.backgroundColor = .white
dataSource = self
}
}
private extension IntroViewController {
func IntroViewController(_ IntroViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
if let viewControllerIndex = self.displayedControllers.index(of: viewController) {
if viewControllerIndex == 0 {
// wrap to last page in array
return self.displayedControllers.last
} else {
// go to previous page in array
return self.displayedControllers[viewControllerIndex + 1]
}
}
return nil
}
func IntroViewController(_ IntroViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
self.pageControl.updateCurrentPageDisplay()
if let viewControllerIndex = self.displayedControllers.index(of: viewController) {
if viewControllerIndex < self.displayedControllers.count - 1 {
// go to next page in array
self.pageControl.currentPage = viewControllerIndex
return self.displayedControllers[viewControllerIndex + 1]
} else {
// wrap to first page in array
self.pageControl.currentPage = viewControllerIndex
return self.displayedControllers.first
}
}
return nil
}
func IntroViewController(_ IntroViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
// set the pageControl.currentPage to the index of the current viewController in pages
if let viewControllers = IntroViewController.viewControllers {
if let viewControllerIndex = self.displayedControllers.index(of: viewControllers[0]) {
self.pageControl.currentPage = viewControllerIndex
self.pageControl.updateCurrentPageDisplay()
}
}
}
func setupUI() {
configurePageControl()
pageControl.do {
$0.numberOfPages = 3
// $0.currentPage = 0
$0.pageIndicatorTintColor = .lightGray
$0.currentPageIndicatorTintColor = Theme.Colors.white
$0.currentPage = self.displayedControllers.count
}
}
func arrangeSubviews() {
view.addSubview(pageControl)
}
}
Just try the below code. It would be helpfull.
class PageViewModel: NSObject {
private var timer: Timer?
private var interval: Double?
var enableAutoScroll: Bool = false {
didSet {
self.toggleTimer()
}
}
var viewControllers: [UIViewController]
var currentIndex: Int = 0
var direction: UIPageViewControllerNavigationDirection = .forward
init(viewControllers: [UIViewController]) {
self.viewControllers = viewControllers
super.init()
}
override convenience init() {
self.init(viewControllers: [])
}
private func stopTimer() {
if self.timer != nil {
timer?.invalidate()
timer = nil
return
}
}
private func startTimer() {
if self.timer != nil {
return
}
if #available(iOS 10.0, *) {
if let time = interval {
timer = Timer.scheduledTimer(withTimeInterval: time, repeats: true, block: { (timer: Timer) in
self.timerFire()
})
}
} else {
// Fallback on earlier versions
if let time = interval {
timer = Timer.scheduledTimer(timeInterval: time, target: self, selector: #selector(PageViewModel.timerFire), userInfo: nil, repeats: true)
}
}
}
#objc private func timerFire() {
if direction == .forward {
var index = currentIndex
if index < self.viewControllers.count - 1 {
index += 1
} else {
index = 0
}
currentIndex = index
scrollToNextPage?(currentIndex)
pageNumberChangedObserver?(currentIndex)
} else {
var index = currentIndex
if index <= 0 {
index = (self.viewControllers.count - 1)
} else {
index -= 1
}
currentIndex = index
scrollToNextPage?(index)
pageNumberChangedObserver?(currentIndex)
}
}
// MARK: - Timer
func toggleTimer() {
if enableAutoScroll == true {
startTimer()
} else {
stopTimer()
}
}
fileprivate var scrollToNextPage: ((_ index: Int) -> Void)?
func scrollToNextPage(callBack: #escaping (_ index: Int) -> Void) {
scrollToNextPage = callBack
}
// MARK: - PageNumberChangedObserver
fileprivate var pageNumberChangedObserver: ((_ index: Int) -> Void)?
func pageNumberChanged(callBack: #escaping (_ index: Int) -> Void) {
pageNumberChangedObserver = callBack
}
}
// MARK: - UIPageViewControllerDataSource
extension PageViewModel: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = viewControllers.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
// User is on the first view controller and swiped left to loop to
// the last view controller.
guard previousIndex >= 0 else {
//return orderedViewControllers.last
// Uncommment the line below, remove the line above if you don't want the page control to loop.
return nil
}
guard viewControllers.count > previousIndex else {
return nil
}
return viewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = viewControllers.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = viewControllers.count
// User is on the last view controller and swiped right to loop to
// the first view controller.
guard orderedViewControllersCount != nextIndex else {
//return orderedViewControllers.first
// Uncommment the line below, remove the line above if you don't want the page control to loop.
return nil
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return viewControllers[nextIndex]
}
}
// MARK: - UIPageViewControllerDelegate
extension PageViewModel: UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if let pageContentViewController = pageViewController.viewControllers?[0] {
if let index = viewControllers.index(of: pageContentViewController) {
currentIndex = index
pageNumberChangedObserver?(currentIndex)
}
}
}
}
Write below code in your Container View Controller: -
// set current page to the page controller
func setNextPage(index: Int, direction: UIPageViewControllerNavigationDirection) {
if let firstViewController = pageViewModel?.viewControllers[selectedCategoryCell] {
pageViewController?.setViewControllers([firstViewController],
direction: direction,
animated: true,
completion: nil)
}
}
// MARK: - SET PAGE CONTROLLER's VIEW's
private func setupPageViewController() {
let pageViewController: UIPageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
pageViewController.dataSource = pageViewModel
pageViewController.delegate = pageViewModel
if let firstViewController = pageViewModel?.viewControllers[selectedCategoryCell] {
pageViewController.setViewControllers([firstViewController],
direction: .forward,
animated: true,
completion: nil)
}
for recognizer in pageViewController.gestureRecognizers {
if recognizer is UITapGestureRecognizer {
recognizer.isEnabled = false
}
}
self.addChildViewController(pageViewController)
self.containerView.addSubview(pageViewController.view)
var pageViewRect = self.containerView.bounds
if UIDevice.current.userInterfaceIdiom == .pad {
pageViewRect = pageViewRect.insetBy(dx: 40.0, dy: 40.0)
}
pageViewController.view.frame = pageViewRect
pageViewController.didMove(toParentViewController: self)
self.pageViewController = pageViewController
}
// set the selected page and accordingly set the above collection view ( that is treatment option's collection view)
func pageNumberChanged(index: Int) {
self.selectedCategoryCell = index
}
// MARK: Init View Controller for Page Controller
func initViewController(index: Int) -> UIViewController {
guard let itemsViewController = self.storyboard?.instantiateViewController(withIdentifier: "MatchesV2ViewController") as? MatchesV2ViewController else {
fatalError("Items View Controller Not Found")
}
itemsViewController.delegate = self
itemsViewController.currentMatch = matches.user[index]
return itemsViewController
}
// MARK: Setting VC In Page View Controller
func instantiateViewControllers() {
matchesScreen.removeAll()
for i in 0..<matches.user.count {
matchesScreen.append(initViewController(index: i))
}
pageViewModel = PageViewModel(viewControllers: matchesScreen)
pageViewModel?.pageNumberChanged(callBack: { (index: Int) in
self.pageNumberChanged(index: index)
})
pageViewModel?.scrollToNextPage(callBack: { (index: Int) in
self.setNextPage(index: index, direction: .forward)
})
if matchesScreen.count > 0 {
self.setupPageViewController()
}
}
I currently have 3 UIViewControllers in the storyboard and I connected them into one UIPageViewController with scrollView. However, I'm having some issues initializing the app on the center UIViewController due to not preloading all the UIViewControllers in the PageViewController. I'm using the scrollview contentOffset to show the center ViewController when the user first open the app viewDidLoad.
I have attached the code I'm using here:
var currentIndex = 0
var mainScrollView = UIScrollView()
lazy var ViewControllerArray: [UIViewController] = {
return [self.ViewControllerInstance(name: "RightVC"),
self.ViewControllerInstance(name: "CenterVC"),
self.ViewControllerInstance(name: "LeftVC")]
}()
private func ViewControllerInstance(name: String) -> UIViewController{
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: name)
}
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
self.delegate = self
if let CenterViewController = ViewControllerArray.first {
setViewControllers([CenterViewController] , direction: .reverse, animated: false, completion: nil)
}
}
override func viewDidAppear(_ animated: Bool) {
mainScrollView = view.subviews.filter { $0 is UIScrollView }.first as! UIScrollView
mainScrollView.delegate = self
mainScrollView.contentOffset.x = self.view.bounds.size.width * 2
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let ViewControllerIndex = ViewControllerArray.index(of: viewController) else {
return nil
}
let PreviousIndex = ViewControllerIndex - 1
currentIndex = ViewControllerIndex
guard PreviousIndex >= 0 else {
return nil
}
guard ViewControllerArray.count > PreviousIndex else {
return nil
}
return ViewControllerArray[PreviousIndex]
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let ViewControllerIndex = ViewControllerArray.index(of: viewController) else {
return nil
}
let NextIndex = ViewControllerIndex + 1
currentIndex = ViewControllerIndex
guard NextIndex < ViewControllerArray.count else {
return nil
}
guard ViewControllerArray.count > NextIndex else {
return nil
}
return ViewControllerArray[NextIndex]
}
// Control bounce # DidScroll & EndDragging
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let lastPosition = scrollView.contentOffset.x
if (currentIndex == ViewControllerArray.count - 1) && (lastPosition > scrollView.frame.width) {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
} else if currentIndex == 0 && lastPosition < scrollView.frame.width {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
}
}
// ^^
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let lastPosition = scrollView.contentOffset.x
if (currentIndex == ViewControllerArray.count - 1) && (lastPosition > scrollView.frame.width) {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
} else if currentIndex == 0 && lastPosition < scrollView.frame.width {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
}
}
UPDATE:
I used a variable IntialOpen as a bool for the system to understand whether the app was opened before and I also added If/else statement inside the public functions.
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let ViewControllerIndex = ViewControllerArray.index(of: viewController) else {
return nil
}
let PreviousIndex = ViewControllerIndex - 1
if !initialOpen {
currentIndex = ViewControllerIndex
}else{
currentIndex = ViewControllerIndex + 1
initialOpen = false
}
guard PreviousIndex >= 0 else {
return nil
}
guard ViewControllerArray.count > PreviousIndex else {
return nil
}
return ViewControllerArray[PreviousIndex]
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let ViewControllerIndex = ViewControllerArray.index(of: viewController) else {
return nil
}
let NextIndex = ViewControllerIndex + 1
if !initialOpen {
currentIndex = ViewControllerIndex
}else{
currentIndex = ViewControllerIndex + 1
initialOpen = false
}
guard NextIndex < ViewControllerArray.count else {
return nil
}
guard ViewControllerArray.count > NextIndex else {
return nil
}
return ViewControllerArray[NextIndex]
}
This worked by pretty much forcing it to open on the second page if its the first time the app has started.
I implement the Page controller ..automatically moving the page in swift
but my issue is the page controller dots not getting changed not indicates the page any one help me to solve this issue
here is my code
override func viewDidLoad() {
super.viewDidLoad()
UpdateCounter = 0
arrPageTitle = ["In SignUp screen user can able to input the first name, last name, emailid and password.", "After SignUp email verification link has been send to his mail then add basic profile information and sport preferences.", "In Profile setting can view profile, privacy and notifications, friends, account and championships won."];
self.pageViewController = self.storyboard?.instantiateViewController(withIdentifier: "myPageviewcontroller") as! UIPageViewController
self.pageViewController.dataSource = self
let initialContentviewcontroller = self.getViewControllerAtIndex(index: 0) as PageContentViewController
let viewcontrollers = NSArray(object: initialContentviewcontroller)
self.pageViewController.setViewControllers(viewcontrollers as? [UIViewController], direction: UIPageViewControllerNavigationDirection.forward, animated: true, completion: nil)
self.pageViewController.view.frame = CGRect(x: 0, y: 50, width:self.view.frame.width,height: 350)
//pagecontroller.delegate = self
pagecontroller.numberOfPages = arrPageTitle.count
pagecontroller.currentPage = 0;
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.didMove(toParentViewController: self)
timer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: (#selector(StartUpPage.advancePage)), userInfo: nil, repeats: true)
}
func getViewControllerAtIndex(index: Int) -> PageContentViewController
{
// Create a new view controller and pass suitable data.
let pageContentViewController = self.storyboard?.instantiateViewController(withIdentifier: "PageContentViewController") as! PageContentViewController
pageContentViewController.strTitle = "\(arrPageTitle[index])"
pageContentViewController.pageIndex = index
return pageContentViewController
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController?
{
let viewController = viewController as! PageContentViewController
var index = viewController.pageIndex as Int
if(index == 0 || index == NSNotFound)
{ return nil
}
index -= 1
return self.getViewControllerAtIndex(index: index)
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController?
{
let viewController = viewController as! PageContentViewController
var index = viewController.pageIndex as Int
if(( index == NSNotFound))
{
return nil
}
index += 1
if(index == arrPageTitle.count)
{
return nil
}
return self.getViewControllerAtIndex(index: index)
}
public func presentationCount(for pageViewController: UIPageViewController) -> Int
{
return arrPageTitle.count
}
public func presentationIndex(for pageViewController: UIPageViewController) -> Int
{
let viewController = self.getViewControllerAtIndex(index: 0)
let index = viewController.pageIndex as Int
return index
}
func advancePage ()
{
UpdateCounter += 1
if UpdateCounter > 2 {
UpdateCounter = 0
}
var nextviewcontroller = self.getViewControllerAtIndex(index: UpdateCounter)
if (nextviewcontroller .isEqual(nil)) {
UpdateCounter = 0
nextviewcontroller = self.getViewControllerAtIndex(index: UpdateCounter)
}
let startingViewControllers = [nextviewcontroller]
pageViewController.setViewControllers(startingViewControllers, direction: UIPageViewControllerNavigationDirection.forward, animated: true, completion: nil)
pagecontroller.currentPage = UpdateCounter
pagecontroller.numberOfPages = 3
pagecontroller.currentPage = 0
pagecontroller.addTarget(self, action: #selector(pageControlTapHandler(sender:)), for: .touchUpInside)
}
Any one help me how to solve this issues when auto scroll the page ..pagecontroll dots also get moved
Thanks in advance
public func presentationIndex(for pageViewController: UIPageViewController) -> Int
{
let viewController = pageViewController.viewControllers?[0] as! PageContentViewController
let index = viewController.pageIndex
pagecontroller.currentPage = index
UIPageControl.appearance().pageIndicatorTintColor = UIColor.lightGray
UIPageControl.appearance().currentPageIndicatorTintColor = UIColor.red
return index
}
add this code this is delegate method for page controller
It will Work fine
Happy Code:)
try change
pagecontroller.currentPage = 0
to
pagecontroller.currentPage = UpdateCounter
You reseted the current in func AdvancedPage
pagecontroller.currentPage = UpdateCounter
pagecontroller.numberOfPages = 3
pagecontroller.currentPage = 0
just delete "pagecontroller.currentPage = 0" is ok.
try this (I only know objective-c codes, try to use it in swift)
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
if (finished) {
YourViewControllersInPageController *childViewController = [pageViewController.viewControllers firstObject];
[self.pageControl setCurrentPage:childViewController.index];
}
}
remember set delegate
pagecontroller.delegate = self
From Another related answer
A page indicator will be visible if both methods are implemented,
transition style is 'UIPageViewControllerTransitionStyleScroll', and
navigation orientation is
'UIPageViewControllerNavigationOrientationHorizontal'. Both methods
are called in response to a 'setViewControllers:...' call, but the
presentation index is updated automatically in the case of
gesture-driven navigation.
You also need to implement below functions to set the page counts for UIPageViewController
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController NS_AVAILABLE_IOS(6_0); // The number of items reflected in the page indicator.
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController NS_AVAILABLE_IOS(6_0); // The selected item reflected in the page indicator.
have implemented a custom page control with the help of which you can change your size , color , shape and page number very easily ... here is the class
import UIKit
import PureLayout
extension PageControl {
func setupView() {
for subview in subviews {
subview.removeFromSuperview()
}
pages.removeAll(keepingCapacity: true)
container = UIView(frame: CGRect.zero)
self.addSubview(container!)
container?.autoCenterInSuperview()
container?.autoPinEdge(toSuperviewEdge: .top)
container?.autoPinEdge(toSuperviewEdge: .bottom)
for index in 0 ..< totalPages {
let page = UIView()
container?.addSubview(page)
pages.append(page)
page.autoMatch(.width, to: .height, of: page)
page.autoPinEdge(toSuperviewEdge: .top, withInset: padding)
page.autoPinEdge(toSuperviewEdge: .bottom, withInset: padding)
if index == 0 {
page.autoPinEdge(toSuperviewEdge: .left, withInset: padding, relation: .greaterThanOrEqual)
} else if index == totalPages-1 {
page.autoPinEdge(toSuperviewEdge: .right, withInset: padding, relation: .greaterThanOrEqual)
} else {
page.autoPinEdge(.left, to: .right, of: pages[index-1], withOffset: padding)
}
}
let size = frame.height - padding * 2
let width = padding * CGFloat(totalPages+1) + size * CGFloat(totalPages)
container?.autoSetDimension(.width, toSize: width)
//layoutIfNeeded()
setNeedsDisplay()
}
}
#IBDesignable class PageControl: UIControl {
var container:UIView?
var pages = [UIView]()
#IBInspectable var currentPage:Int = 0 {
didSet {
setNeedsLayout()
}
}
#IBInspectable var allPagesColor: UIColor = Colors.greyELight {
didSet {
setNeedsDisplay()
}
}
#IBInspectable var currentPageColor: UIColor = Colors.green {
didSet {
setNeedsDisplay()
}
}
#IBInspectable var totalPages: Int = 3 {
didSet {
if totalPages > 0 {
setupView()
}
}
}
#IBInspectable var padding: CGFloat = 2 {
didSet {
if padding > 0 {
setupView()
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
override func layoutSubviews() {
super.layoutSubviews()
let size = frame.height - padding * 2
for (index, view) in pages.enumerated() {
view.layer.cornerRadius = size / 2
view.layer.masksToBounds = true
view.backgroundColor = (index == currentPage) ? currentPageColor: allPagesColor
}
}
}
here is the implentation
var pageControl = PageControl()
and viewDidLoad add this code
let f = CGRect(x: CGFloat(0), y: CGFloat.adjustYAxis(91), width: CGFloat.adjustXAxis(100), height: CGFloat.adjustYAxis(6))
pageControl = PageControl(frame:f)
pageControl.totalPages = 5
pageControl.tag = 716
pageControl.delegate = self
pageControl.allPagesColor = UIColor(hexString:"#afc6de")
let firstMood = self.moodsArray[0] as! MoodsModel
pageControl.currentPageColor = UIColor(hexString: firstMood.MoodBackGroundColor!)
pageControl.currentPage = 0
pageControl.padding = 10
self.pageViewController.view.addSubview(pageControl)
and change you page number in delegate methods just like that
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController?
{
let viewController = viewController as! PageContentViewController
var index = viewController.pageIndex as Int
if(index == 0 || index == NSNotFound)
{ return nil
}
index -= 1
pageControl.currentPage = index
return self.getViewControllerAtIndex(index: index)
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController?
{
let viewController = viewController as! PageContentViewController
var index = viewController.pageIndex as Int
if(( index == NSNotFound))
{
return nil
}
index += 1
if(index == arrPageTitle.count)
{
return nil
}
pageControl.currentPage = index
return self.getViewControllerAtIndex(index: index)
}
I have the following PageViewController class :
class ProjectorPageViewController : UIPageViewController, UIPageViewControllerDataSource {
var randUsed : [String]?
var pageViewMatches : [SingleMatch]? {
didSet {
//irrelevant code
}
let initialcontroller = viewControlerAtIndex(0)
let viewControllers = [initialcontroller!]
setViewControllers(viewControllers, direction: .Forward, animated: true, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
let pageControl = UIPageControl.appearance()
pageControl.currentPageIndicatorTintColor = UIColor.whiteColor()
pageControl.pageIndicatorTintColor = UIColor(red: (69/255.0), green: (209/255.0), blue: (153/255.0), alpha: 1.0)
}
func viewControlerAtIndex(index : Int) -> PageViewContentViewController? {
if (self.pageUsers!.count == 0 || index >= self.pageUsers!.count) {
return nil
}
let controller = PageViewContentViewController()
controller.location = pageLocations![index]
controller.image_name = pageImages![index]
controller.user_name = pageUsers![index]
controller.pageIndex = index
return controller
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let curr_index = (viewController as! PageViewContentViewController).pageIndex
print ("attempting after with index : " + String(curr_index))
if (curr_index! < pageViewMatches!.count - 1) {
return viewControlerAtIndex(curr_index! + 1) }
else {
return nil
}
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let curr_index = (viewController as! PageViewContentViewController).pageIndex
print ("attempting before with index : " + String(curr_index))
if (curr_index! > 0 ) {
return viewControlerAtIndex(curr_index! - 1)
}
else {
return nil
}
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
if let matches = pageViewMatches {
return matches.count
}
else {
return 0
}
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return pageViewController.viewControllers!.indexOf((pageViewController.viewControllers?.first)!)!
}
}
The problem is that if I swipe fast enough between the pages, the page indicator becomes out of sync, and points to the wrong page. If I swipe at a moderate pace this doesn't occur. I have seen a similar post which said the solution was to implement the presentationIndexForPageViewController method, however translating this from Objective-C (unless I made a mistake in translation) did not solve the problem. Here is the post I am referring to : UIPageViewController setViewControllers, UIPageControl not showing right current number
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
let vc = pageViewController.viewControllers!.first as! PageViewContentViewController
return vc.pageIndex
}
You must return pageIndex... pageViewController.viewControllers!.indexOf((pageViewController.viewControllers?.first)!)! won't return right index
I'm starting study swift, and use UIPageViewController to slide 3 page. I use class:
import Foundation
import UIKit
class MyPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pages = [UIViewController]()
var pageIndicator : UIPageControl!
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
self.dataSource = self
let page1: UIViewController! = storyboard?.instantiateViewControllerWithIdentifier("page1")
let page2: UIViewController! = storyboard?.instantiateViewControllerWithIdentifier("page2")
let page3: UIViewController! = storyboard?.instantiateViewControllerWithIdentifier("page3")
pages.append(page1)
pages.append(page2)
pages.append(page3)
setViewControllers([page1], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
func viewControllerAtIndex(index: Int) -> UINavigationController! {
return nil
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.indexOf(viewController)!
pageIndicator.currentPage = currentIndex
let previousIndex = abs((currentIndex - 1) % pages.count)
return pages[previousIndex]
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.indexOf(viewController)!
pageIndicator.currentPage = currentIndex
let nextIndex = abs((currentIndex + 1) % pages.count)
return pages[nextIndex]
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return pages.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
But sliding didn't stops on the third page, and begins again with first page.
How limit slide page forward and backward?
I think your index calculation is wrong in the following line.
`
let nextIndex = abs((currentIndex + 1) % pages.count)
return pages[nextIndex]`
for the last view controller currentIndex=2 ,then nextIndex=(2+1)%3=0.
So you are returning the view controller at index 0 again.
add the following line to your method `
if(currentIndex == (pages.count-1))
{
return nil;
}
`Note:this is according to objective c.
As UIBittu already pointed out: your calculation needs a fix.
A possible (probably more readable) solution may look like this:
func indexOfViewController(viewController: UIViewController) -> Int {
guard let index = pages.indexOf(viewController) else {
return NSNotFound
}
return index
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
var index = indexOfViewController(viewController)
if (index == 0) || (index == NSNotFound) {
return nil
}
index--
return pages[index]
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var index = indexOfViewController(viewController)
if (index == NSNotFound) || (index+1 == pages.count) {
return nil
}
index++
return pages[index]
}
This is different solution.
You don't need to have a full set of complex code to execute what you need.
Create a ScrollView. Set its content size width as the multiple of number of pages.
Try this code. this may help You
override func viewDidLoad() {
super.viewDidLoad()
let screensize = UIScreen.mainScreen().bounds
pagescroll.frame = CGRectMake(0, 0,screensize.width, screensize.height)
pagescroll.contentSize = CGSize(width: screensize.width*3, height: screensize.height)
pagescroll.pagingEnabled = true
page1.frame = CGRectMake(0, 0, screensize.width, screensize.height)
page2.frame = CGRectMake(screensize.width, 0, screensize.width, screensize.height)
page3.frame = CGRectMake(screensize.width*2, 0, screensize.width, screensize.height)
pagescroll.addSubview(page1)
pagescroll.addSubview(page2)
pagescroll.addSubview(page2)
self.view.addSubview(pagescroll)
// Do any additional setup after loading the view.
}
var pagescroll = UIScrollView()
var page1 = UIView()
var page2 = UIView()
var page3 = UIView()
You can also add a pagecontroller: UIPageControl according to your requirement