access the last element of the UIPageViewController - swift

How do I access the last item of a UIPageViewController so I can set self as its delegate?
self.present(wizard, animated: true, completion: {
let lastVC = wizard.viewControllers?.last as! thirdPageController
lastVC.delegate = self
})
I think the third item wasn't loaded yet so .last returns the wrong page (the first one)

Swift 5
Create your own UIPageViewController:
class MyPageViewController: UIPageViewController {
private lazy var _viewControllers = [UIViewController]()
public var lastViewController: UIViewController? {
return _viewControllers.last
}
override func setViewControllers(_ viewControllers: [UIViewController]?, direction: UIPageViewController.NavigationDirection, animated: Bool, completion: ((Bool) -> Void)? = nil) {
super.setViewControllers([viewController], direction: direction, animated: animated, completion: completion)
_viewControllers.removeAll()
_viewControllers.append(viewControllers)
}
}
Class usage:
let myPageViewController = MyPageViewController()
myPageViewController.lastViewController // This is an optional value
Finally you should change the class of wizard UIPageViewController to MyPageViewController
Then you can use this new class like:
self.present(wizard, animated: true, completion: {
if let lastViewController = wizard.lastViewController as? thirdPageController {
lastViewController.delegate = self
}
})

Related

ViewController present does not always work

I have some problems displaying a viewcontroller in my IOS app.
Sometimes it works and the view is displayed, but sometimes and I guess when the context is a bit different it will not work. No errors or warnings in the debugger and it can find the ViewController from the Main storyboard (at least it is not nil)
It use to work with self.present but that seems not to work anymore.
#IBAction func showHistoryButton(_ sender: MDCButton) {
let exercisesHistoryVC = ExercisesHistoryViewController.instantiate(from: .Main)
exercisesHistoryVC.modalPresentationStyle = .fullScreen
let appDeligate = UIApplication.shared.delegate as! AppDelegate
appDeligate.window?.rootViewController!.present(exercisesHistoryVC,animated: true,completion: nil)
// parent?.present(exercisesHistoryVC, animated: true, completion: nil)
}
Use the code like below,while Present New View Controller
#IBAction func showHistoryButton(_ sender: MDCButton) {
let exercisesHistoryVC = ExercisesHistoryViewController.instantiate(from: .Main)
exercisesHistoryVC.modalPresentationStyle = .fullScreen
UIApplication.topViewController()?.present(exercisesHistoryVC, animated: false, completion: nil)
}
extension UIApplication {
static func topViewController(base: UIViewController? = UIApplication.shared.delegate?.window??.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(base: nav.visibleViewController)
}
if let tab = base as? UITabBarController, let selected = tab.selectedViewController {
return topViewController(base: selected)
}
if let presented = base?.presentedViewController {
return topViewController(base: presented)
}
return base
}
}

How to push controller after dismiss presented controller in swift?

Hey I am showing a controller (A) in main controller with presentation style (not push), and I want to button tapped and push another controller (B) after dismiss this (A) controller, this situation occurred in main controller. I am using protocol for this situation. Any idea for that ? Code like below.`
//this is dismiss button action
var segueDelegate: segueFromController?
#objc func dismissController() {
self.dismiss(animated: true) {
self.segueDelegate?.segueFromController()
}
//and this one is protocol function in main controller
func segueFromController() {
let contProfile = ContViewController(collectionViewLayout: UICollectionViewFlowLayout())
navigationController?.pushViewController(contProfile, animated: true)
}
// and I am making "self" this protocol in main controller's didload
let aCont = AController()
override func viewDidLoad() {
super.viewDidLoad()
AController.segueDelegate = self
}
// protocol
protocol segueFromController {
func segueFromController()
}
// this is presenting (A) controller code in main page
func openController() {
let preController = AController()
preController.modalPresentationStyle = .fullScreen
self.present(preController, animated: true, completion: nil)
}
First you need to make this segueDelegate weak
protocol segueFromController : class {
func segueFromController()
}
weak var segueDelegate: segueFromController?
func openController() {
let preController = AController()
preController.segueDelegate = self
preController.modalPresentationStyle = .fullScreen
self.present(preController, animated: true, completion: nil)
}
Try to dismiss without animation
self.dismiss(animated: false) {
self.segueDelegate?.segueFromController()
}

Can't Dismiss View controller

I am trying to dismiss a View Controller that has been presented as a modal .overCurrentContext.
The controller is presented like so
let vc= UIViewController()
vc.modalPresentationStyle = .overCurrentContext
present(vc, animated: true, completion: nil)
When I call dismiss inside the vc controller that has appeared it does nothing.
To give more detail about the parent ViewController, it is a ViewController inside a NavigationController and it is third on the NavigationController's VC stack.
So the actual solution was very obscure.
I was using a Pod called FloatingPanelController that was causing some issues with dismissing my entire view stack intermittently.
In order to resolve it I modified an extension in the Pod
public extension UIViewController {
#objc func fp_original_dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
// Implementation will be replaced by IMP of self.dismiss(animated:completion:)
}
#objc func fp_dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
// Call dismiss(animated:completion:) to a content view controller
if let fpc = parent as? FloatingPanelController {
if fpc.presentingViewController != nil {
self.fp_original_dismiss(animated: flag, completion: completion)
} else {
fpc.removePanelFromParent(animated: flag, completion: completion)
}
return
}
// Call dismiss(animated:completion:) to FloatingPanelController directly
if let fpc = self as? FloatingPanelController {
if fpc.presentingViewController != nil {
self.fp_original_dismiss(animated: flag, completion: completion)
} else {
fpc.removePanelFromParent(animated: flag, completion: completion)
}
return
}
// For other view controllers
self.fp_original_dismiss(animated: flag, completion: completion)
}
}
In particular I removed the last line:
self.fp_original_dismiss(animated: flag, completion: completion)
This is what caused the issue. I had no idea that this method was actually overriding the entire dismiss method. Once I re-inserted that line the issue was resolved.
This is likely very little use to anyone else!

How do I bypass the welcome screen UI and present only the phone auth UI screen in Firebase Auth?

How can I present the viewController which is responsible for collecting phone number data in FirebaseUI rather than the welcome screen?
With the current set up I am presented with a Welcome screen.
class Login: UIViewController, FUIAuthDelegate {
let authUI = FUIAuth.defaultAuthUI()
override func viewDidAppear(_ animated: Bool) {
let phoneProvider = FUIPhoneAuth(authUI: authUI!)
authUI!.isSignInWithEmailHidden = true
authUI!.providers = [phoneProvider]
let vc = authUI?.authViewController()
self.present(vc!, animated: true, completion: nil)
}
}
EDIT
Thanks to proxpero I can present the phone UI like this:
class Login: UIViewController, FUIAuthDelegate {
let authUI = FUIAuth.defaultAuthUI()
override func viewDidAppear(_ animated: Bool) {
authUI?.delegate = self
let phoneProvider = FUIPhoneAuth(authUI: authUI!)
authUI!.isSignInWithEmailHidden = true
authUI!.providers = [phoneProvider]
phoneProvider.signIn(withPresenting: self, phoneNumber: nil)
}
}
If you want to bypass the welcome screen, and present the phone auth screen straightaway, I think that instead of self.present(vc!, animated: true, completion: nil), put phoneProvider.signIn(withPresenting: self, phoneNumber: nil).
This method on phoneProvider creates the phone auth ui view controller and presents it on the parent view controller that you pass in (self in this case).

nstimer not working properly showing the same data and page controller also not indicate page

I want to implement the page controller and set the timer for page controller
here I used the label array of object passing the label
but here I got reaping the same values page controller also not moving and when showing the same data in label please help me to solve this issues ...
Thanks in Advance.
Here is my code :
override func viewDidLoad()
{
super.viewDidLoad()
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)
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 advancePage ()
{
let pvcs = pageViewController.childViewControllers as! [PageContentViewController]
let itemIndex = pvcs[0].pageIndex
let firstController = getViewControllerAtIndex(index: itemIndex+1)
let startingViewControllers = [firstController]
pageViewController.setViewControllers(startingViewControllers, direction: UIPageViewControllerNavigationDirection.forward, animated: true, completion: nil)
}
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
{
return 0
}
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.pageIndicatorTintColor = UIColor.lightGray
pagecontroller.currentPageIndicatorTintColor = UIColor.red
print(UpdateCounter)
}
try with this code hope will work
So far where i know, UI changes are made in Main thread, so if you use any loop or other method to change this in an interval it will make only the last change in the UI. You could do this with NSRunloop, but it's a little bit dangerous and unpredictable sometimes.