Go to next Page in UIPageViewController - swift

I have a button and I just want to go to the next page.
I have read in other threads people saying to just used this,
setViewControllers([UIVIewController], direction: .forward, animated: false, completion: nil)
I have tried this in the button on a child view. I see that the PageControl (the little dots) has the functionality I want but I can't seem to find how apple implemented this. I have been reading a lot of posts on stackOverflow but I am not quite grasping it.
I have a rootViewController that implements the dataSource and delegate.

First get the current view controller and next view controller and then pass the next view controller in setViewControllers function
func moveToNextPage() {
guard let currentViewController = self.viewControllers?.first else { return print("Failed to get current view controller") }
guard let nextViewController = self.dataSource?.pageViewController( self, viewControllerAfter: currentViewController) else { return }
setViewControllers([nextViewController], direction: .forward, animated: false, completion: nil)
}
I hope this helps you.

Related

Making a new view controller appear after interstitial ad is dismissed

I am building an iOS app in swift with Xcode that has a button that, when pressed, will (among other things) show an interstitial Admob ad, and then go to a different view controller. Right now, when I press the button, an interstitial ad will appear. However, when I close the ad, the view is still on the original page. If I click the button again, then I am taken to the second view controller. Sometimes, however, I have to dismiss three or four ads before I get to the second view controller. How can I make it so that, after I dismiss the interstitial ad, I go straight to the second view controller?
Here is the current code:
#IBAction func postPressed(_ sender: Any) {
if contact.text?.isEmpty == true {
print("No contact")
return
}
if avail.text?.isEmpty == true {
print("No availability")
return
}
uploadSpot()
}
func uploadSpot() {
let key = Int.random(in: 20001..<30000)
let object: [String: Any] = [
"Contact": contact.text!,
"Availability": avail.text!,
]
geoFireRef.child("\(key)").setValue(object)
let location = manager.location!
geoFire.setLocation(location, forKey: "\(key)")
if interstitialAd?.isReady == true {
interstitialAd?.present(fromRootViewController: self)
}
let storyboard = UIStoryboard(name: "SpotLoaded", bundle: nil)
let vc = storyboard.instantiateViewController(identifier: "SpotLoaded")
vc.modalPresentationStyle = .overFullScreen
present(vc, animated: true)
}
The import part is in the function "uploadSpot()," where
if interstitialAd?.isReady == true {
interstitialAd?.present(fromRootViewController: self)
}
presents the ad and
let storyboard = UIStoryboard(name: "SpotLoaded", bundle: nil)
let vc = storyboard.instantiateViewController(identifier: "SpotLoaded")
vc.modalPresentationStyle = .overFullScreen
present(vc, animated: true)
switches the viewcontroller. I have tried reversing the order if these two (i.e. putting let storyboard, etc., before if insterstitialAd), but that just takes me to the new view controller and the ad never appears. I have also tried putting the let storyboard chunk inside of the brackets following the if interstitialAd. This does not help either. If I put it after interstitialAd?.present(fromRootViewController: self), I have the same problem where the ad shows but the view controller does not change. If I put it after, the viewcontroller changes but the ad never shows.
As the main purpose of interstitial ads is to appear in between views of an app, I do not think what I am trying to do should be so difficult. I think there must just be something I am missing or do not understand. Please help in whatever way you can.

Present Next ViewController to Dismiss current ViewController Using Swift

My Scenario, I have three ViewControllers, those ViewControllers First, Second, Third. Here, First ViewController UIButton click to present model second ViewController and then Second ViewController UIButton click to present model Third ViewController. I need to Implement the Second ViewController button click to dismiss current ViewController after presenting Third ViewController because once I close Third ViewController I need to show First ViewController. How to Achieve this?
NOTE: I am having NavigationController for Third ViewController also I am using Storyboard.
Here's my code:
#IBAction func ClickThirdVC(_ sender: Any) {
if let VC_Third = self.storyboard?.instantiateViewController(withIdentifier: "thirdviewcontroller") as? ThirdvViewController {
weak var pvc = self.presentingViewController
VC_B.modalTransitionStyle = .crossDissolve
let navController = UINavigationController(rootViewController: VC_B)
self.dismiss(animated: true, completion: {
pvc?.present(navController, animated: true, completion: nil)
})
}
}
You can try to dismiss self after presenting the third UIViewController, in the completion handler of the present function .
secondVC?.present(navController, animated: true) {
self.dismiss(animated: false, completion: nil) //dismiss self after presenting the third.
}

Present modal while transitioning to the previous view

When I leave a particular view controller, I want to have it display a modal view over the previous view in the navigation stack. Currently I'm doing it with this code:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.viewControllers.last?.present(myModalView, animated: false, completion: nil)
}
This seems to work, but every time I leave the view I get a warning in the console:
Presenting view controllers on detached view controllers is discouraged <Project.MyViewController: 0x7fe09281a400>.
So I'm concerned that there might be a problem I haven't run into yet. Is there a "correct" way of doing this?
As it turns out, self.present(myModalView, animated: false, completion: nil) works perfectly with no warnings. In retrospect, I probably should have tested that first, but I had assumed it wouldn't work since the view was disappearing.
Additionally, it does not work at all with animated: true, whereas my original solution does with no warnings (but I don't want it to be animated). Not sure if that's just how the asynchronous processes work out or if there's something more to it.
You are presenting the modal on the current view controller, not over the previous view controller. When you do
self.navigationController?.viewControllers.last?.present(myModalView, animated: false, completion: nil)
in the viewWillDisappear protocol, it's the same as saying
self.present(myModalView, animated: false, completion: nil)
Instead, try this:
let previousVCIndex = self.navigationController?.viewController.index(of: self)
self.navigationViewController?.viewControllers[previousVCIndex! - 1]?.present(myModalView, animated: false, completion: nil)

How to access a function in another class being within the same instance?

I am currently creating a login project in Swift 3.
I have a main view controller which contains
a container, which includes a page viewcontroller and multiple pages
a custom "menu" at the bottom which includes two buttons for navigating the page view controller and a custom page control
This is my storyboard setup
The view the user will be presented with is made of the pages controlled by the page viewcontroller, at the top, and the control menu, at the bottom. When the user clicks on the buttons in the control menu this code will be executed in the main viewcontroller:
#IBAction func buttonNext(_ sender: UIButton) {
print("buttonNext has been pressed!")
nextPageWithIndex(index: nextIndex)
}
and here is the function it calls:
func nextPageWithIndex(index: Int) {
let nextWalkthroughVC = pages[index]
pageContainer.setViewControllers([nextWalkthroughVC], direction: .forward, animated: true, completion: nil)
print("nextPageWithIndex has been executed with Index \(index)!")
if currentIndex as Int? == 2 {
currentIndex = 2
} else if currentIndex as Int? == 1 {
currentIndex = 2
} else if currentIndex as Int? == 0 {
currentIndex = 1
} else {
currentIndex = 0
print("currentIndex was in inpossible state!")
}
checkButtonNavigation()
assignIndexes()
if let index = currentIndex {
pageControl.currentPage = index
print("PageControl updated")
}
}
checkButtonNavigation() checks if the buttons at the bottom should be displayed depending on the page which is currently being displayed.
I am trying to implement a button of one of the pages (in this case page1) which navigates the user to the next page.
I tried to execute the function nextPageWithIndex(index: 1) with varAccountRegisterMainViewController.nextPageWithIndex(index: 1) on the action of pressing the button in the page1 viewcontroller class by defining:
var varAccountRegisterMainViewController: accountRegisterMainViewController!
in the AppDelegate,
and:
varAccountRegisterMainViewController = accountRegisterMainViewController()
in the page1 viewcontroller.
Sadly that doesn't seem to work. I get "fatal error: Index out of range" at the point:
let nextWalkthroughVC = pages[index]
in the function nextPageWithIndex(index: 1) in the main viewcontroller. I checked what count the array "pages" has. It's 0. When I execute the function right from the main viewcontroller, though, it has 3 as it should have. Here is the error as an image.
Thank you for your help!
EDIT:
here is how the pages Array is set-up:
At the beginning of the main viewcontroller class the following code is added:
var pages = [UIViewController]()
In the viewDidLoad() that code:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let page1: UIViewController! = storyboard.instantiateViewController(withIdentifier: "IDAccountRegisterPage1ViewController")
let page2: UIViewController! = storyboard.instantiateViewController(withIdentifier: "IDAccountRegisterPage2ViewController")
let page3: UIViewController! = storyboard.instantiateViewController(withIdentifier: "IDAccountRegisterPage3ViewController")
pages.append(page1)
pages.append(page2)
pages.append(page3)
ANOTHER EDIT:
I put the declaration of "pages" also into the externalNext() function. Now that problem is solved but there is another one... this is how the function looks now:
func externalNext(index: Int) {
var pageContainer: UIPageViewController!
var pages = [UIViewController]()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let page1: UIViewController! = storyboard.instantiateViewController(withIdentifier: "IDAccountRegisterPage1ViewController")
let page2: UIViewController! = storyboard.instantiateViewController(withIdentifier: "IDAccountRegisterPage2ViewController")
let page3: UIViewController! = storyboard.instantiateViewController(withIdentifier: "IDAccountRegisterPage3ViewController")
pages.append(page1)
pages.append(page2)
pages.append(page3)
// Create the page container
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)
pageContainer.view.addSubview(pageControlView)
print("Current array count of pages is \(pages.count)")
print("Function: nextPageWithIndex has been called with index \(index)")
let nextWalkthroughVC = pages[index]
pageContainer.setViewControllers([nextWalkthroughVC], direction: .forward, animated: true, completion: nil)
print("nextPageWithIndex has been executed with Index \(index)!")
pageControl.currentPage = index
print("PageControl updated")
print("Hello")
}
I copied the code from the top of the main viewcontroller class and just pasted it into the function. I alway get the error "fatal error: unexpectedly found nil while unwrapping an Optional value", though.
Is there any way I can redefine the objects in the function?
Or is there any elegant way? My way looks very messy...
Thanks to Phillip Mills I know that is has something to do with being another instance of the class main viewcontroller. How can I access the function in there but within the same instance? The main viewcontroller is visible all the time.
Thank you!
When you do this: accountRegisterMainViewController() you are creating a new object, a different instance of that class. Since the new one is not being displayed, viewDidLoad is never called and your array is empty.
You need to arrange links between objects so that the one loaded from your storyboard is also the one that nextPageWithIndex is called on.

How do you present a view controller in a SKScene?

I am trying to figure out how to use a share sheet for my SpriteKit game and when I try to present the view controller from my scene, it gives me an error.
if self.nodeAtPoint(location) == self.shareButton {
var tempMessage = "I just got \(score) on [insert app]"
let activityVC:UIActivityViewController = UIActivityViewController(activityItems: [tempMessage], applicationActivities: nil)
self.presentViewController(activityVC, animated: true, completion: nil)
}
This is in my GameOverScene. How exactly do you go about managing viewcontrollers in Swift because the answers I have seen were in OBJ-C.
Thanks!
Ok so I did some digging and found a solution that works.
Basically, I used NSNoticationCenter and set up the view controller in my GameViewController file as such.
override func viewDidLoad() {
.
.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "showSocialNetworks", name: "showSocialNetworksID", object: nil)
}
and made the function to be called
func showSocialNetworks(){
var tempMessage = "Insert your message to be shared on social network"
let activityVC:UIActivityViewController = UIActivityViewController(activityItems: [tempMessage], applicationActivities: nil)
self.presentViewController(activityVC, animated: true, completion: nil)
}
Now jump back to my GameOverScene, I set it up so when the user taps the Share Button, it performs the following code
if self.nodeAtPoint(location) == self.shareButton {
NSNotificationCenter.defaultCenter().postNotificationName("showSocialNetworksID", object: nil)
}
This may not be the cleanest way to do it, but it works for me. Hope this helps!