I embedded a PageController within a ContainerView.
Why are the Views I display in the PageController not scaled to fit the PageController? How can I achieve "scale to fit"?
(At least the PageController itself adapts its size to the ContainerView)
My classes are:
import UIKit
class YellowController : UIViewController
{
}
class GreenController : UIViewController
{
}
//from a tutorial:
class PageController : UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate
{
// from stackoverflow to avoid the black box at the bottom of the pagecontroller
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for view in self.view.subviews
{
if view is UIScrollView
{
view.frame = UIScreen.main.bounds
}
else
{
view.backgroundColor = UIColor.clear
}
}
}
lazy var VCArray: [UIViewController] =
{
return [self.VCInstance(name: "InfoPage"),
self.VCInstance(name: "FragePage"),
self.VCInstance(name: "AntwortPage") ]
}()
private func VCInstance (name: String) -> UIViewController
{
let VCC = UIStoryboard(name: "Lerneinheit", bundle: nil).instantiateViewController(withIdentifier: name)
return VCC
}
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
self.delegate = self
if let InfoPage = VCArray.first
{
setViewControllers([InfoPage], direction: .forward, animated: true, completion: nil)
}
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController?
{
guard let viewControllerIndex = VCArray.index(of: viewController)
else
{
return nil
}
if (viewControllerIndex<1)
{
return nil
}
return VCArray[viewControllerIndex - 1]
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController?
{
guard let viewControllerIndex = VCArray.index(of: viewController)
else
{
return nil
}
if viewControllerIndex == VCArray.count-1
{
return nil
}
return VCArray[viewControllerIndex + 1]
}
public func presentationCount(for pageViewController: UIPageViewController) -> Int
{
return VCArray.count
}
public func presentationIndex(for pageViewController: UIPageViewController) -> Int
{
guard let firstViewController = viewControllers?.first, let firstViewControllerIndex = VCArray.index(of: firstViewController)
else
{
return 0
}
return firstViewControllerIndex
}
IB:
Result wth the yellow view beeing cut instead of scaled to fit the PageController:
Constraints for the yellow view:
The code below is redundant, remove it:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for view in self.view.subviews
{
if view is UIScrollView
{
view.frame = UIScreen.main.bounds
}
else
{
view.backgroundColor = UIColor.clear
}
}
}
Related
Everything working fine but dots not showing. My code is...
import UIKit
class MyPageViewController: UIPageViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
dataSource = self
if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController],
direction: .forward,
animated: true,
completion: nil)
}
}
private(set) lazy var orderedViewControllers: [UIViewController] = {
return [self.newColoredViewController(color: ""),
self.newColoredViewController(color: "Second"),
self.newColoredViewController(color: "Third")]
}()
private func newColoredViewController(color: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil) .
instantiateViewController(withIdentifier: "\(color)ViewController")
}
}
// MARK: UIPageViewControllerDataSource
extension MyPageViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.firstIndex(of: 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,
viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.firstIndex(of: 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 {
setupPageControl()
return orderedViewControllers.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
private func setupPageControl() {
let appearance = UIPageControl.appearance()
appearance.pageIndicatorTintColor = UIColor.darkGray
appearance.currentPageIndicatorTintColor = UIColor.red
appearance.backgroundColor = UIColor.black
}
}
You are using the wrong version of those functions.
In order to show UIPageControl, you need to implement two optional datasource methods. Just return the whole number of pages for presentationCount and the initially selected index for presentationIndex
func presentationCount(for pageViewController: UIPageViewController) -> Int {
setupPageControl()
return orderedViewControllers.count
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
return 0
}
you should add page view controller data source and number of pages :
pageControl.numberOfPages = 3
This question already has answers here:
Refresh UIPageViewController - reorder pages and add new pages
(4 answers)
Closed 4 years ago.
I am looking to how to reload the UI page controller.
On a table VC I have added UIHeaderView and embedded the UIPage VC.
Page VC is loading images dynamically from firebase.
First time it is loading the right set of images but when on the table VC I selecting other cel the Page VC need to refresh its data.
How to achieve this functionality
Here is the coed I have written for PAGE VC
//-----PAGE VC CODE------
import UIKit
import Firebase
protocol ProductImagesPageVCDelegate: class
{
func setupPageController(numberOfPages: Int)
func turnPageController(to index: Int)
}
class ProductImagesPageVC: UIPageViewController {
var product: Product!
weak var pageViewControllerDelegate: ProductImagesPageVCDelegate?
struct StoryBoard {
static let productImageVC = "ProductImageVC"
}
lazy var controllers: [UIViewController] = {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var controllers = [UIViewController]()
if let imageLinks = self.product.imageLinks
{
for imageLink in imageLinks
{
let productImageVC = storyboard.instantiateViewController(withIdentifier: StoryBoard.productImageVC)
controllers.append(productImageVC)
}
}
self.pageViewControllerDelegate?.setupPageController(numberOfPages: controllers.count)
return controllers
}()
override func viewDidLoad() {
super.viewDidLoad()
// if #available(iOS 11.0, *) {
// contentInsetAdjustmentBehavior = .never
// } else {
// automaticallyAdjustsScrollViewInsets = false
// }
automaticallyAdjustsScrollViewInsets = false
dataSource = self
delegate = self
self.turnToPage(index: 0)
}
func turnToPage(index: Int)
{
let controller = controllers[index]
var direction = UIPageViewControllerNavigationDirection.forward
if let currentVC = viewControllers?.first
{
guard let currentIndex = controllers.index(of: currentVC) else {return}
if currentIndex > index
{
direction = .reverse
}
}
self.configuewDisplaying(viewController: controller)
setViewControllers([controller], direction: direction, animated: true, completion: nil)
}
func configuewDisplaying(viewController: UIViewController)
{
for (index, vc) in controllers.enumerated()
{
if viewController === vc {
if let productImageVC = viewController as? ProductImageVC
{
productImageVC.imageLink = self.product.imageLinks?[index]
self.pageViewControllerDelegate?.turnPageController(to: index)
}
}
}
}
}
extension ProductImagesPageVC: UIPageViewControllerDataSource
{
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
if let index = controllers.index(of: viewController)
{
if index < controllers.count - 1
{
return controllers[index + 1]
}
}
return controllers.first
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
if let index = controllers.index(of: viewController)
{
if index > 0
{
return controllers[index - 1]
}
}
return controllers.last
}
}
extension ProductImagesPageVC: UIPageViewControllerDelegate
{
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
self.configuewDisplaying(viewController: pendingViewControllers.first as! ProductImageVC)
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if !completed
{
self.configuewDisplaying(viewController: previousViewControllers.first as! ProductImageVC)
}
}
}
Try this-
let viewControllers: [UIViewControllers] = [UIViewController()]
if let pageViewController = parentViewController as? UIPageViewController {
pageViewController.setViewControllers(viewControllers, direction: .Forward, animated: true, completion: nil)}
I've searched every question/answer I can find on here, but I can't figure out how to hide the status bar for all of the view controllers in my UIPageViewController. Here's the code from my UIPageViewController class:
class TipsVC: UIPageViewController, UIPageViewControllerDelegate {
lazy var VCArr: [UIViewController] = {
return [self.VCInstance(name: "T1"),
self.VCInstance(name: "T2"),
self.VCInstance(name: "T3")]
}()
private func VCInstance(name: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: name)
}
override public func viewDidLoad() {
super.viewDidLoad()
//self.dataSource = self
self.delegate = self
if let OB1 = VCArr.first {
setViewControllers([OB1], direction: .forward, animated: true, completion: nil)
let pageController = UIPageControl.appearance()
pageController.pageIndicatorTintColor = UIColor(red:1.00, green:0.88, blue:0.92, alpha:1.0)
pageController.currentPageIndicatorTintColor = UIColor(red:1.00, green:0.25, blue:0.51, alpha:1.0)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for view in self.view.subviews {
if view is UIScrollView {
view.frame = UIScreen.main.bounds
} else if view is UIPageControl {
view.backgroundColor = UIColor.clear
}
}
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController?{
guard let viewControllerIndex = VCArr.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return VCArr.last
}
guard VCArr.count > previousIndex else {
return nil
}
return VCArr[previousIndex]
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController?{
guard let viewControllerIndex = VCArr.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
guard nextIndex < VCArr.count else {
return VCArr.first
}
guard VCArr.count > nextIndex else {
return nil
}
return VCArr[nextIndex]
}
public func presentationCount(for pageViewController: UIPageViewController) -> Int{
return VCArr.count
}
public func presentationIndex(for pageViewController: UIPageViewController) -> Int{
guard let OB1 = viewControllers?.first,
let OB1Index = VCArr.index(of: OB1) else {
return 0
}
return OB1Index
}
public func nextPageWithIndex(index: Int)
{
let nextVC = VCArr[index]
setViewControllers([nextVC], direction: .forward, animated: true, completion: nil)
}
}
and here's a code sample from one of my viewControllers:
class T1: UIViewController {
#IBOutlet var nextBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
UIApplication.shared.isStatusBarHidden = true
}
#IBAction func nextBtnDidPress(_ sender: Any) {
let next = self.parent as! TipsVC
next.nextPageWithIndex(index: 1)
}
}
I'm well aware of how to hide the status bar on regular view controllers, but I'm unable to get the same results when I'm using a UIPageViewController. What's going on?
To hide statusbar from particluar viewController you need to hide status bar in viewWillAppear and hide status bar in viewWillDisAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UIApplication.shared.isStatusBarHidden = true
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
UIApplication.shared.isStatusBarHidden = false
}
viewWillAppear will call before opening particular viewController and viewWillDisAppear call after dismiss particular viewController
if you face problem with UIPageViewController use UIViewController and embed UIPageViewController inside that for reference check below code
var pageContainer: UIPageViewController!
pageContainer = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
pageContainer.delegate = self
pageContainer.dataSource = self
pageContainer.setViewControllers([getViewControllerAtIndex(index: 0)] as [UIViewController], direction: .forward, animated: false, completion: nil)
pageContainer.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height)
self.view.addSubview(pageContainer.view)
Hope this will help you
I have a uipageviewcontroller with four individual view controllers in it. I want to disable the right swipe action for the first view controller and disable the left swipe action for the fourth view controller. When the app launches, I want the user to only be able to swipe left and not right, because swiping right, takes the user to the fourth view controller and swiping left on the fourth view controller takes the user to the first view controller. Here is my code.
class TutorialPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
weak var tutorialDelegate: TutorialPageViewControllerDelegate?
lazy var VCArr: [UIViewController] = {
return [self.VCInstance(name: "FirstVC"),
self.VCInstance(name: "SecondVC"),
self.VCInstance(name: "ThirdVC"),
self.VCInstance(name: "FourthVC")]
}()
private func VCInstance(name: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: name)
}
override func viewDidLoad() {
super.viewDidLoad()
let pageControl = UIPageControl.appearance()
//Customizing
pageControl.pageIndicatorTintColor = UIColor.lightGray
pageControl.currentPageIndicatorTintColor = UIColor(red:0.23, green:0.73, blue:1.00, alpha:1.0)
dataSource = self
delegate = self
self.dataSource = self
self.delegate = self
if let FirstVC = VCArr.first {
setViewControllers([FirstVC], direction: .forward, animated: true, completion: nil)
tutorialDelegate?.tutorialPageViewController(tutorialPageViewController: self, didUpdatePageCount: VCArr.count)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for view in self.view.subviews {
if view is UIScrollView {
view.frame = UIScreen.main.bounds
} else if view is UIPageControl {
view.backgroundColor = UIColor.clear
}
}
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = VCArr.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return VCArr.last
}
guard VCArr.count > previousIndex else {
return nil
}
return VCArr[previousIndex]
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = VCArr.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
guard nextIndex < VCArr.count else {
return VCArr.first
}
guard VCArr.count > nextIndex else {
return nil
}
return VCArr[nextIndex]
}
public func presentationCount(for pageViewController: UIPageViewController) -> Int {
return VCArr.count
}
public func presentationIndex(for pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first,
let firstViewControllerIndex = VCArr.index(of: firstViewController) else {
return 0
}
return firstViewControllerIndex
}
}
extension TutorialPageViewController {
func pageViewController(pageViewController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed: Bool) {
if let firstViewController = viewControllers?.first,
let index = VCArr.index(of: firstViewController) {
tutorialDelegate?.tutorialPageViewController(tutorialPageViewController: self, didUpdatePageIndex: index)
}
}
}
Basically you need to change following lines
guard previousIndex >= 0 else {
return nil
}
guard nextIndex < VCArr.count else {
return nil
}
----- OR -----
Change viewControllerAfter as
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = VCArr.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
guard nextIndex < VCArr.count else {
return nil
}
guard VCArr.count > nextIndex else {
return nil
}
return VCArr[nextIndex]
}
Change ViewControllerBefore as
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = VCArr.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return nil
}
guard VCArr.count > previousIndex else {
return nil
}
return VCArr[previousIndex]
}
You can use this extension for disable all direction swipe :
extension UIPageViewController {
var isPagingEnabled: Bool {
get {
var isEnabled: Bool = true
for view in view.subviews {
if let subView = view as? UIScrollView {
isEnabled = subView.isScrollEnabled
}
}
return isEnabled
}
set {
for view in view.subviews {
if let subView = view as? UIScrollView {
subView.isScrollEnabled = newValue
}
}
}
}
}
and call this:
pageCtrl.isPagingEnabled = false
I have a Page view controller (see below), what i want to achieve is to go to the FirstVC from ThirdVC and only from that!! with a function or a button, can anyone help me?
Here you can see the code of my PageVC
import UIKit
class PageVC: UIPageViewController,UIPageViewControllerDataSource, UIPageViewControllerDelegate{
lazy var VCArr: [UIViewController] = {
return [self.VCInstance(name: "FirstVC"), self.VCInstance(name: "SecondVC"), self.VCInstance(name: "ThirdVC")]
}()
private func VCInstance(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 firstVC = VCArr.first {
setViewControllers([firstVC], direction: .forward, animated: true, completion: nil)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for view in self.view.subviews {
if view is UIScrollView {
view.frame = UIScreen.main.bounds
} else if view is UIPageControl {
view.backgroundColor = UIColor.clear
}
}
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = VCArr.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else{
return VCArr.last
}
guard VCArr.count > previousIndex else {
return nil
}
return VCArr[previousIndex]
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = VCArr.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
guard nextIndex < VCArr.count else{
return VCArr.first
}
guard VCArr.count > nextIndex else {
return nil
}
return VCArr[nextIndex]
}
public func presentationCount(for pageViewController: UIPageViewController) -> Int {
return VCArr.count
}
public func presentationIndex(for pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first, let firstViewControllerIndex = VCArr.index(of: firstViewController) else{
return 0
}
return firstViewControllerIndex
}
}
Ok i found a solution here:
How do I change the UIPageViewController from WITHIN one of the UIViewControllers that is part of the UIPageViewController?
Simply i added this function to PageVC:
func nextPageWithIndex(index: Int)
{
let nextWalkthroughVC = VCArr[index]
setViewControllers([nextWalkthroughVC], direction: .forward, animated: true, completion: nil)
}
Then in the ThirdVC view controller i added in my function:
let parent = self.parent as! PageVC
parent.nextPageWithIndex(index: 0)
Where 0 is the index of the view controller that i want to show
And works perfectly!