UIPageViewController with only 1 UIViewController and infinite loop - swift

First at all, I am not using UICollectionView because I need put my Slider inside of a ViewContainer and it's not allowed put repeatable elements in it.
As my question title says, I want to get work a UIPageViewController with only 1 UIViewController and infinite loop.
This is my current code:
class ListPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pages: [UIViewController] = []
let viewModel = ListPageViewModel()
override func viewDidLoad() {
super.viewDidLoad()
viewModel.delegate = self
delegate = self
dataSource = self
if let storyboard = storyboard {
pages = [
storyboard.instantiateViewController(withIdentifier: "listViewController"),
storyboard.instantiateViewController(withIdentifier: "listViewController")
]
setViewControllers([pages.first!], direction: .forward, animated: true)
}
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
return brother(controller: viewController)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
return brother(controller: viewController)
}
func brother(controller: UIViewController) -> UIViewController? {
if controller == pages.first { return pages.last }
return pages.first
}
}
The problem is that I am not able to reference an unique UIViewController because I'm create two instead of one, and I need manipulate that child UIViewController from my UIPageViewController.
Some help?

Related

Issue Implementing UIPageViewController

I am trying to implement to scroll between two view controllers, one to be a message page, and the other view controller to be a camera page, similar to snapchat app. If I didn't make myself understood I attached an image with the app that has exactly the same implementation as I'm trying to do, it has 5 views which you can scroll between them. image
Now I have tried it to implement in multiple ways. One of them is with UIPageViewController, snippet of my code:
class ViewController: UIViewController , UIPageViewControllerDelegate
{
var chatVC = one()
var cameraVC= two()
var array = [UIViewController]()
var arr = [UIViewController]()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
self.array = [chatVC,cameraVC]
self.arr = [chatVC]
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
self.presentPageVC()
})
}
func presentPageVC(){
var vc = UIPageViewController.init(transitionStyle: .scroll, navigationOrientation: .horizontal , options: nil)
vc.delegate = self
vc.dataSource = self
vc.setViewControllers(self.arr, direction: .forward, animated: true, completion: nil)
self.present(vc, animated: true, completion: nil)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let index = self.array.firstIndex(of: viewController),index>0 else {return nil}
let beforeIndex = index - 1
return self.array[beforeIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let index = self.array.firstIndex(of: viewController), index < (self.array.count-1) else {
return nil
}
var indexAfter = index + 1
return self.array[indexAfter]
}
With this implementation I have one problem as I try to scroll after the last page on the cameraVC, on the right it appears the view behind it. How could I turnOff scrolling when there is no other page after or before.
As well, Im new to software development, if somebody has a better idea for implementing it, please let me know. I get stuck at trying to implementing with UICollectionView because I don't know how to convert a UIViewController to a cell, and how to change depending on it on the delegate. As well when trying to add via UIScrollView, I don't understand how I could a UIViewController subview to the UIScrollview, because it accepts only UIView.
Thank you in advance for the help,
For anybody who wants the UIScrollView to don't show the viewUnder when scrolling there are no existent views to scroll.
scrollView.showsLargeContentViewer = false

How to enable tab bar transition animation to work without initial view controller

Essentially I have the following custom transition animation for Tab Bar Controller:
MyFadeTransition.swift
import UIKit
class MyFadeTransition: NSObject, UIViewControllerAnimatedTransitioning {
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
if let fromVC = transitionContext.viewController(forKey: .from), let toVC = transitionContext.viewController(forKey: .to) {
toVC.view.frame = fromVC.view.frame
toVC.view.alpha = 0
fromVC.view.alpha = 1
transitionContext.containerView.addSubview(fromVC.view)
transitionContext.containerView.addSubview(toVC.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
toVC.view.alpha = 1
}) { (finished) in
transitionContext.completeTransition(finished)
}
}
}
func animationEnded(_ transitionCompleted: Bool) {
// no-op
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.12
}
}
The issue is the code also requires the Tab Bar Controller to be the initial view controller during launch and the code below in the AppDelegate
let tab = window!.rootViewController as! UITabBarController
tab.delegate = self
The following must also be added to AppDelegate.swift
extension AppDelegate: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let fade = MyFadeTransition()
return fade
}
}
I am using a different ViewController as the Initial ViewController, how can I make the code continue to work without having it as the Initial Controller?
You need to subclass UITabBarController and use
class CustomTab:UITabBarController,UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return MyFadeTransition()
}
}
Then assign CustomTab as class name to the tabBar in IB

Swift 3 - UIPageViewController - Presenting view controllers on detached view controllers is discouraged

Iam using UIPageViewController for my app's first open. And third page is my login page. This contains facebook login. When I clicked facebook login button, opening empty page and xcode give me this output "Presenting view controllers on detached view controllers is discouraged < tapusor.LoginPage: 0x101f09a00 >."
When I dont use UIPageViewController this button is working. So there is my code. How can I fix this issue?
import UIKit
class MyPageViewController: UIPageViewController,
UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pageContainer: UIPageViewController!
// The pages it contains
var pages = [UIViewController]()
// Track the current index
var currentIndex: Int?
private var pendingIndex: Int?
override func viewDidLoad() {
super.viewDidLoad()
// Setup the pages
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let page1: UIViewController! = storyboard.instantiateViewController(withIdentifier: "firstVC")
let page2: UIViewController! = storyboard.instantiateViewController(withIdentifier: "secondVC")
let page3: UIViewController! = storyboard.instantiateViewController(withIdentifier: "LoginPage")
pages.append(page1)
pages.append(page2)
pages.append(page3)
pageContainer = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
pageContainer.delegate = self
pageContainer.dataSource = self
pageContainer.setViewControllers([page1], direction: UIPageViewControllerNavigationDirection.forward, animated: false, completion: nil)
// Add it to the view
view.addSubview(pageContainer.view)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
if currentIndex == 0 {
return nil
}
let previousIndex = abs((currentIndex - 1) % pages.count)
return pages[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
if currentIndex == pages.count-1 {
return nil
}
let nextIndex = abs((currentIndex + 1) % pages.count)
return pages[nextIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
pendingIndex = pages.index(of: pendingViewControllers.first!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Also I can try this
let page3: UIViewController! = storyboard.instantiateViewController(withIdentifier: "LoginPage")
to this
let page3: UIViewController! = storyboard.instantiateViewController(withIdentifier: "LoginPage") as! LoginPage
or this
let page3: LoginPage! = storyboard.instantiateViewController(withIdentifier: "LoginPage") as! LoginPage
Not worked.
Create delegate for your LoginPage controller with function what will notify about Facebook login button pressed. Something like this:
protocol LoginPageDelegate: class {
func loginPageDidPressFacebookButton(_ controller: LoginPage)
}
Now add delegate property to your LoginPage class
class LoginPage: UIViewController {
weak var delegate: LoginPageDelegate?
}
Now you need to inform delegate when user press Facebook login button
#IBAction func facebookButtonPressed(button: UIButton) {
self.delegate?.loginPageDidPressFacebookButton(self)
}
And not you can subscribe to this delegate in your MyPageViewController.
First you need to make it class conform to LoginPageDelegate
class MyPageViewController: UIPageViewController,
UIPageViewControllerDataSource, UIPageViewControllerDelegate, LoginPageDelegate {
//MARK: LoginPageDelegate methods
func loginPageDidPressFacebookButton(_ controller: LoginPage) {
//present what you need here
}
}
And now before you show you LoginPage:
let page3: LoginPage! = storyboard.instantiateViewController(withIdentifier: "LoginPage")
page3.delegate = self

UIViewControllerTransitioningDelegate on tvOS not being called

So I'm trying to do an animated transition between viewcontrollers on tvOS 10.
The UIViewControllerTransitioningDelegate protocol is available on tvOS so I assumed I could animate it as well. But for some reason none of the functions are ever called when presenting the new viewcontroller.
I have no idea what I'm doing wrong since I'm doing basically the exact same on iOS.
Here is the code I'm using:
Presenting code
func showStuff() {
let viewController = ResultViewController()
viewController.transitioningDelegate = ResultViewControllerTransitionManager()
viewController.modalPresentationStyle = .custom
navigationController?.present(viewController, animated: true, completion: nil)
}
Transition Delegate
class ResultViewControllerTransitionManager: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {
var duration = 0.5
var isPresenting = false
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) else {return}
guard let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else {return}
(...)
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
isPresenting = false
return self
}
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
isPresenting = true
return self
}
}
You can implement UIViewControllerAnimatedTransitioning and UIViewControllerTransitioningDelegate protocols inside your first view controller. Your first UIViewController may have the following code:
class ViewController: UIViewController, UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning {
func showStuff() {
let viewController = ResultViewController()
viewController.transitioningDelegate = self
viewController.modalPresentationStyle = .custom
self.present(viewController, animated: true, completion: nil)
}
var duration = 0.5
var isPresenting = false
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
}
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 2.0
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) else {return}
guard let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else {return}
}
}
Also, I present a new controller from the first one (not from the navigationController).
So apparently initialising the animationmanager in the showStuff() function was the problem. Storing it as a property in the Main viewcontroller and passing it along did work.
Wouldn't the issue be that you are using a navigationController, so that navigationController?.delegate needs to equal your ResultViewControllerTransitionManager()?
We have one at work using a push animation (should be the same issue), but we set
navigationController?.delegate = transitionDelegate

UIPageViewController load ChildViews multiple times

I've subclassed UIPageViewController, and it works fine. Except the fact that going back and forth load new instances of the pages, instead of (as i would expect) retrieving the instance it loaded previously.
Aren't the references supposed to be strong?
Following is my code.
Thank you for your help.
Pierrick
class CustomPageViewController: UIPageViewController, UIPageViewControllerDataSource {
var view1: FirstCustomViewController!
var view2: SecondCustomViewController!
var view3: ThirdCustomViewController!
var views = []
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self;
self.view1 = FirstViewController()
self.view2 = SecondViewController()
self.view3 = ThirdViewController()
self.views = [self.view1, self.view2, self.view3]
self.setViewControllers([self.view1], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
}
// MARK: - PageViewControllerDataSource
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let index = self.views.indexOfObject(viewController)
if (index > 0) {
return self.views.objectAtIndex(index-1) as? UIViewController
}
return nil
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let index = self.views.indexOfObject(viewController)
if (index < self.views.count-1) {
return self.views.objectAtIndex(index+1) as? UIViewController
}
return nil
}
}
Seems like the faulty function is in FirstViewController, SecondViewController, ThirdViewController. When i wrote my question, the view controller loaded the view following the method "viewWillAppear".
As soon as i moved the code to "viewDidLoad", the views stopped to load multiple times.