Learning swift at the moment, any help would be appreciated - Updating code from swift 3 to swift 4 - getting the following error when building:
Argument labels '(_:, viewControllerAfterViewController:)' do not match any available overloads
I believe the code that is relevant to this error is in the goToNextVC func below:
func goToNextVC() {
let nextVC = pageViewController(self, viewControllerAfterViewController: viewControllers![0] )!
setViewControllers([nextVC], direction: .Forward, animated: true, completion: nil)
}
extension ViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
// navigate right
switch viewController {
case cardsVC:
return profileVC
case profileVC:
return nil
case matchesVC:
return cardsVC
default:
return nil
}
}
full code:
// sets up page view controller
let pageController = ViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
class ViewController: UIPageViewController {
// page view controllers here
let cardsVC: UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "CardsNavController")
let profileVC: UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ProfileNavController")
let matchesVC: UIViewController! = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MatchesNavController")
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
dataSource = self
// loads first view
setViewControllers([cardsVC], direction: .forward, animated: true, completion: nil)
}
// load left view
func goToNextVC() {
let nextVC = pageViewController(self, viewControllerAfterViewController: viewControllers![0] )!
setViewControllers([nextVC], direction: .Forward, animated: true, completion: nil)
}
// load right view
func goToPreviousVC() {
let previousVC = pageViewController(self, viewControllerBeforeViewController: viewControllers![0] )!
setViewControllers([previousVC], direction: .Reverse, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
// MARK - UIPageViewControllerDataSource
extension ViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
// navigate right
switch viewController {
case cardsVC:
return profileVC
case profileVC:
return nil
case matchesVC:
return cardsVC
default:
return nil
}
}
// navigate left
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
switch viewController {
case cardsVC:
return matchesVC
case profileVC:
return cardsVC
case matchesVC:
return nil
default:
return nil
}
}
}
This:
let nextVC = pageViewController(self, viewControllerAfterViewController: viewControllers![0] )!
needs to be:
let nextVC = pageViewController(self, viewControllerAfter: viewControllers![0] )!
And while you are at it, you need to get rid of the forced unwrap (the !). pageViewController(_:viewControllerAfter:) can return nil and the use of ! will cause your app to crash.
func goToNextVC() {
if let viewControllers = viewControllers {
if let nextVC = pageViewController(self, viewControllerAfter: viewControllers[0]) {
setViewControllers([nextVC], direction: .Forward, animated: true, completion: nil)
}
}
}
Related
I've got my RootPageViewController set up and all I need is to add UIButtons. I've already added the button and its function from the UIViewController which is to be displayed first.
How am I able to also add UIButtons from f.e. the third UIViewController and assign them a function?
Just like with the button from the secondViewController which is displayed first: secondViewController.buttonAdd.action = #selector(addData(_:))
If I solely use let's say firstViewController.buttonBack.action = #selector(backData(_:)) - I'll get an error saying:
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
I assume thats because, there's no if let firstViewController ...
But let's say I'd add:
if let firstViewController = viewControllerList.first as? TimelineViewController {
self.setViewControllers([firstViewController], direction: .forward, animated: true, completion: nil)
firstViewController.buttonBack.action = #selector(backData(_:))
}
In this case the order which view will be shown initially and which next etc. will be mixed up.
I'm open for suggestion and better approaches, I just want this to work,
RootPageViewController:
import UIKit
class RootPageViewController: UIPageViewController, UIPageViewControllerDataSource {
lazy var viewControllerList:[UIViewController] = {
let sb = UIStoryboard(name: "Main", bundle: nil)
let vc1 = sb.instantiateViewController(withIdentifier: "timelineView")
let vc2 = sb.instantiateViewController(withIdentifier: "mainView")
let vc3 = sb.instantiateViewController(withIdentifier: "addView")
return [vc1, vc2, vc3]
}()
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
if let secondViewController = viewControllerList[1] as? MainViewController {
self.setViewControllers([secondViewController], direction: .forward, animated: true, completion: nil)
secondViewController.buttonAdd.action = #selector(addData(_:))
}
}
#objc func addData(_ sender: Any) {
if let thirdViewController = viewControllerList.last {
self.setViewControllers([thirdViewController], direction: .forward, animated: true, completion: nil)
}
}
#objc func backData(_ sender: Any) {
let secondViewController = viewControllerList[1]
self.setViewControllers([secondViewController], direction: .forward, animated: true, completion: nil)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let vcIndex = viewControllerList.firstIndex(of: viewController) else { return nil }
let previousIndex = vcIndex - 1
guard previousIndex >= 0 else { return nil }
guard viewControllerList.count > previousIndex else { return nil }
return viewControllerList[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let vcIndex = viewControllerList.firstIndex(of: viewController) else { return nil }
let nextIndex = vcIndex + 1
guard viewControllerList.count != nextIndex else { return nil }
guard viewControllerList.count > nextIndex else { return nil }
return viewControllerList[nextIndex]
}
}
The solution was to call this in any UIViewControllers button action:
// get parent view controller
let parentVC = self.parent as! UserDetailsPVC
// change page of PageViewController
parentVC.setViewControllers([parentVC.pages[1]], direction: .forward, animated: true, completion: nil)
This is my first attempt at an IOS app, and I have no experience with swift, and a lot of the code is borrowed from the web and edited.
I am trying to create a set of slides. I go from the main Landing page to another View Controller, TestVC, that runs the slides. The landing page and the slides work. I can swipe back and forth. I am now trying to add a timer so that the slides auto advance every 5 or so seconds.
I believe that the code that needs to be run is:
pageViewController.setViewControllers(varPageVC, direction: UIPageViewControllerNavigationDirection.forward, animated: true, completion: nil)
I get an error :
test.swift:31:9: Ambiguous reference to member 'pageViewController(_:viewControllerBefore:)'.
I do not know how to interpret this error and move forward. The error is triggered in the test.swift, where a timer calls a function that tries to advance the slide. Advise is appreciated. If I am doing it wrong, please point me in the appropriate direction.
The landing page has a button, that opens a ViewController testVC. I have 2 files, test.swift and alphabetItemController.swift. The storyboard has, in addition to the landing page ViewController, a PageViewController called alphabetPVC, a ViewController called alphabetVC and a ViewController called TestVC.
Here is the code for alphabetItemController.swift ...
import UIKit
class alphabetItemController: UIViewController {
#IBOutlet weak var contentImageView2: UIImageView!
#IBOutlet weak var contentWordPn: UILabel!
var itemIndex: Int = 0
var imageName: String = ""
var wordPN: String = ""
var tTime: Timer!
override func viewDidLoad() {
super.viewDidLoad()
contentImageView2!.image = UIImage(named: imageName)
contentWordPn!.text = wordPN
}
}
Here is the code for test.swift ...
import Foundation
import UIKit
class testItemController: UIViewController, UIPageViewControllerDataSource {
var tTime: Timer!
override func viewDidLoad() {
super.viewDidLoad()
createPageViewController()
setupPageControl()
tTime = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(changeSlide), userInfo: nil, repeats: true)
//tTime = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(goToNextPage), userInfo: nil, repeats: true)
}
func changeSlide() {
pageViewController.setViewControllers(varPageVC, direction: UIPageViewControllerNavigationDirection.forward, animated: true, completion: nil)
}
// MARK: - Variables
private var varPageVC: UIPageViewController?
private let contentTextWordPN = ["A", "B", "C", "D", "E"]
private let contentCount = 5 //TODO ADJUST THIS FOR EACH COLLECTION
private func createPageViewController() {
let pageController = self.storyboard!.instantiateViewController(withIdentifier: "alphabetPVC") as! UIPageViewController
pageController.dataSource = self
if contentCount > 0 {
let firstController = getItemController(itemIndex: 0)!
let startingViewControllers = [firstController]
pageController.setViewControllers(startingViewControllers, direction: UIPageViewControllerNavigationDirection.forward, animated: false, completion: nil)
}
varPageVC = pageController
addChildViewController(varPageVC!)
self.view.addSubview(varPageVC!.view)
varPageVC!.didMove(toParentViewController: self)
}
private func setupPageControl() {
let appearance = UIPageControl.appearance()
appearance.pageIndicatorTintColor = UIColor.gray
appearance.currentPageIndicatorTintColor = UIColor.white
appearance.backgroundColor = UIColor.darkGray
}
func pageViewController(_ varPageVC: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let itemController = viewController as! alphabetItemController
if itemController.itemIndex > 0 {
return getItemController(itemIndex: itemController.itemIndex-1)
}
return nil
}
func pageViewController(_ varPageVC: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let itemController = viewController as! alphabetItemController
if itemController.itemIndex+1 < contentCount {
return getItemController(itemIndex: itemController.itemIndex+1)
}
return nil
}
private func getItemController(itemIndex: Int) -> alphabetItemController? {
if itemIndex < contentCount {
let pageItemController = self.storyboard!.instantiateViewController(withIdentifier: "alphabetVC") as! alphabetItemController
pageItemController.itemIndex = itemIndex
pageItemController.imageName = "alphabet_" + String(format: "%02d", (itemIndex + 1)) //alphabet_01
pageItemController.wordPN = contentTextWordPN[itemIndex]
return pageItemController
}
return nil
}
func presentationCountForPageViewController(varPageVC: UIPageViewController) -> Int {
return contentCount
}
func presentationIndexForPageViewController(varPageVC: UIPageViewController) -> Int {
return 0
}
func currentControllerIndex() -> Int {
let pageItemController = self.currentController()
if let controller = pageItemController as? alphabetItemController {
return controller.itemIndex
}
return -1
}
func currentController() -> UIViewController? {
if (self.varPageVC?.viewControllers?.count)! > 0 {
return self.varPageVC?.viewControllers![0]
}
return nil
}
}
extension UIPageViewController {
func goToNextPage(animated: Bool = true) {
guard let currentViewController = self.viewControllers?.first else { return }
guard let nextViewController = dataSource?.pageViewController(self, viewControllerAfter: currentViewController) else { return }
setViewControllers([nextViewController], direction: .forward, animated: animated, completion: nil)
}
func goToPreviousPage(animated: Bool = true) {
guard let currentViewController = self.viewControllers?.first else { return }
guard let previousViewController = dataSource?.pageViewController(self, viewControllerBefore: currentViewController) else { return }
setViewControllers([previousViewController], direction: .reverse, animated: animated, completion: nil)
}
}
There is even an extension UIPageViewController, but I do not know how to call the goToNextPage function.
I ended up changing the changeSlide() function... I had the next slide function in the extension already, and only the syntax to call it was eluding me... I found some examples on SO and used them as references:
func changeSlide() {
varPageVC?.goToNextPage()
}
Similar questions to mine have been asked numerous times, however, in my situation I set up the UIPageViewController in an unconventional method. And over the past day and a half of trying to implement the other questions solutions into my program, I have come to believe that they only work with a more conventional method of setting up the UIPageViewController. For some background, I have two UIViewControllers that are being transitioned between in a UIPageViewController. I would like to be able to programmatically change the page of the UIPageViewController from one of the views controllers.
Here is my UIPageViewController class:
class transitionController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
lazy var arrayVC: [UIViewController] = {
return [
self.VCInstance(name: "VC1"),
self.VCInstance(name: "VC2")
]
}()
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 VC1 = arrayVC.first{
setViewControllers([VC1], direction: .forward, animated: true, completion: nil)
}
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = arrayVC.index(of: viewController) else{
return nil
}
guard FIRAuth.auth()?.currentUser?.uid != nil else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return nil
}
guard arrayVC.count > previousIndex else{
return nil
}
return arrayVC[previousIndex]
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = arrayVC.index(of: viewController) else{
return nil
}
guard FIRAuth.auth()?.currentUser?.uid != nil else {
return nil
}
let nextIndex = viewControllerIndex + 1
guard nextIndex < arrayVC.count else {
return nil
}
guard arrayVC.count > nextIndex else{
return nil
}
return arrayVC[nextIndex]
}
}
I have tried to flip the pages with this block of code in my VC1:
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "VC2")
transitionController().setViewControllers([vc]?, direction: .Forward, animated: true, completion: nil)
I believe that this doesn't work as I am creating a new instance of the transition controller(I could be wrong).
How can I programmatically flip pages of the UIPageViewController from within the VC1 or VC2 classes using my method of setting up the UIPageViewController?
Thanks in advance.
I have a pageViewController with 3 viewControllers which is embedded in a view of a viewcontroller.
i am setting the view controllers programatically:
lazy var vcArr: [UIViewController] = {
return [self.vcInstance(name: "vc1"),
self.vcInstance(name: "vc2"),
self.vcInstance(name: "vc3"),
]
}()
i am programatically setting the number of pages to 4
let pageController = UIPageControl.appearance()
pageController.numberOfPages = 4
what i want to happen is that if the user tries to swipe right from the 3rd page it will segue to a signup viewController.
I'm currently trying to achieve this in the viewcontroller after function testing for it the next item is greater than or equal to the total count.
public func pageViewController(_ pageViewController: UIPageViewController,
viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = vcArr.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
if nextIndex >= vcArr.count {
infoClick()
return nil
} else {
guard nextIndex < vcArr.count else {
return self.vcArr.last
}
guard self.vcArr.count > nextIndex else {
return nil
}
return self.vcArr[nextIndex]
}
}
with this test the way it is sometimes it stops on page 3 and then when swiped right it will segue off with my function infoClick(). however somethings it goes right off when it hits the 3rd page as you would think because its > or = the number of pages which is 3.
when i change this to just > it doesn't display anything on the third page.
I've searched heaps for a way around this but to no avail. does anyone have any ideas how to achieve this?
attaching entire class below:
import UIKit
import Parse
class pageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
#IBOutlet var createAccountBarButton: UIBarButtonItem!
lazy var vcArr: [UIViewController] = {
return [self.vcInstance(name: "vc1"),
self.vcInstance(name: "vc2"),
self.vcInstance(name: "vc3"),
]
}()
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
//---------------------------------------------- autoLogin
/// temporary location, needs to be launched during the loading screen
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
// if (PFUser.current() != nil) {
// let vc = self.storyboard!.instantiateViewController(withIdentifier: "PackViewController")
// self.present(vc, animated: true, completion: nil)
// if (PFUser.current() != nil) {
// let tbc = self.storyboard!.instantiateViewController(withIdentifier: "MyTabController") as! UITabBarController
// tbc.selectedIndex = 1
// self.present(tbc, animated: true, completion: nil)
// }
}
//---------------------------------------------- set the viewcontrollwe instance
private func vcInstance(name: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: name)
}
//---------------------------------------------- set the direction and first page of the page controller
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.tabBar.isHidden = true
let pageController = UIPageControl.appearance()
pageController.pageIndicatorTintColor = UIColor.lightGray
pageController.currentPageIndicatorTintColor = GeneralFunctions.UIColorFromHEX(hexValue: 0xbb0d2a)
pageController.backgroundColor = UIColor.clear
pageController.bounds = CGRect(x: 0, y: 0, width: 0, height: 0)
pageController.numberOfPages = 4
//GeneralFunctions.commonButtonSettings(buttonName: signUpButton, hexValue: 0x0c1537)
self.dataSource = self
self.dataSource = self
if let firstVC = vcArr.first {
setViewControllers([firstVC], direction: .forward, animated: true, completion: nil)
}
}
//---------------------------------------------- page view controller before settings
public func pageViewController(_ pageViewController: UIPageViewController,
viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = vcArr.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
if previousIndex < 0 {
return nil
} else {
guard previousIndex >= 0 else {
return self.vcArr.last
}
guard self.vcArr.count > previousIndex else {
return nil
}
return self.vcArr[previousIndex]
}
}
//---------------------------------------------- page view controller after settings
public func pageViewController(_ pageViewController: UIPageViewController,
viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = vcArr.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
if nextIndex >= vcArr.count {
infoClick()
return nil
} else {
guard nextIndex < vcArr.count else {
return self.vcArr.last
}
guard self.vcArr.count > nextIndex else {
return nil
}
return self.vcArr[nextIndex]
}
}
//---------------------------------------------- presentation count
public func presentationCount(for pageViewController: UIPageViewController) -> Int {
return self.vcArr.count
}
//---------------------------------------------- presentation index
public func presentationIndex(for pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first,
let firstViewControllerIndex = vcArr.index(of: firstViewController) else {
return 0
}
return firstViewControllerIndex
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//---------------------------------------------- launch the new view controller - still launching before swipped to 4th window sometimes
// not sure if this is the best way to do this need to look at this a bit more
func infoClick() {
let storyboard: UIStoryboard = UIStoryboard (name: "Main", bundle: nil)
let vc: SignupViewController = storyboard.instantiateViewController(withIdentifier: "SignupViewController") as! SignupViewController
let currentController = getCurrentViewController()
currentController?.present(vc, animated: false, completion: nil)
}
func getCurrentViewController() -> UIViewController? {
if let rootController = UIApplication.shared.keyWindow?.rootViewController {
var currentController: UIViewController! = rootController
while( currentController.presentedViewController != nil ) {
currentController = currentController.presentedViewController
}
return currentController
}
return nil
}
}
When I do fast swipe between pages sometimes I get duplicate pages. My index counting may be wrong, but I tried everything and always get the same.
DetailedProfileViewController.swift
class DetailedProfileViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var currentProfileIndex : Int?
var nextProfileIndex :Int?
var data: Main?
var mainPageView : UIPageViewController!
override func viewDidLoad() {
super.viewDidLoad()
let profile = ProfileViewController()
profile.profile = currentProfile()
let arraysOfViews = [profile]
mainPageView = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:])
mainPageView.setViewControllers(arraysOfViews, direction: .forward, animated: true, completion: nil)
mainPageView.dataSource = self
mainPageView.delegate = self
addChildViewController(mainPageView)
view.addSubview(mainPageView.view)
mainPageView.didMove(toParentViewController: self)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
if currentProfileIndex == (data?.filteredProfiles.count)!-1 { return nil }
nextProfileIndex = abs((currentProfileIndex! + 1) % (data?.filteredProfiles.count)!)
let profile = ProfileViewController()
profile.profile = data?.filteredProfiles[nextProfileIndex!]
return profile
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
if currentProfileIndex == 0 { return nil }
nextProfileIndex = abs((currentProfileIndex! - 1) % (data?.filteredProfiles.count)!)
let profile = ProfileViewController()
profile.profile = data?.filteredProfiles[nextProfileIndex!]
return profile
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed == true {
currentProfileIndex = nextProfileIndex
}
}
}
I'm not setting all of View Controllers in viewDidLoad because it could be hundreds of them..
The dumbest solution is to add an index to ProfileViewController. If you do so, you can set it as zero to the first page. And whenever you are asked to provide the next or previous view controller, you always know relatively to what index, because you may extract it from the given pageViewController (which is in fact your ProfileViewController).
Otherwise the handling of the currentProfileIndex may be very error-prone.
UPDATE
class DetailedProfileViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var data: Main?
var mainPageView : UIPageViewController!
override func viewDidLoad() {
super.viewDidLoad()
let profile = ProfileViewController()
profile.index = 0
profile.profile = currentProfile()
let arraysOfViews = [profile]
mainPageView = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:])
mainPageView.setViewControllers(arraysOfViews, direction: .forward, animated: true, completion: nil)
mainPageView.dataSource = self
mainPageView.delegate = self
addChildViewController(mainPageView)
view.addSubview(mainPageView.view)
mainPageView.didMove(toParentViewController: self)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let pagesCount = data?.filteredProfiles.count else {
return nil
}
guard let newIndex = (viewController as? ProfileViewController).index + 1, newIndex < pagesCount else {
return nil
}
let profile = ProfileViewController()
profile.profile = data?.filteredProfiles[newIndex]
profile.index = newIndex
return profile
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let pagesCount = data?.filteredProfiles.count else {
return nil
}
guard let newIndex = (viewController as? ProfileViewController).index - 1, newIndex >= 0 else {
return nil
}
let profile = ProfileViewController()
profile.profile = data?.filteredProfiles[newIndex!]
profile.index = newIndex
return profile
}
}