(Swift) need help swiping between view controllers - swift

These are my view controllers:
I want to make it so that I can swipe between the three of them starting in the middle one. All of the tutorials I've found require you to start from scratch and don't show me how to connect the three I have. Does anyone have a step-by-step instruction of how to do this?
Any help would be greatly appreciated

First Create a class called PageViewController, drag a UIPageViewController in storyboard.For now lets set it as initial view controller from attributes inspector. Also from identity inspector set PageViewController as Class.
Call your three view controller for example StepZero,StepOne,StepTwo Also give them identifier in storyboard.
lets deep into coding now, so in PageViewController should subclass UIPageVIewController:
import UIKit
class PageViewController : UIPageViewController,UIPageViewControllerDataSource {
var selectedIndex = 1
override func viewDidLoad() {
dataSource = self
view.backgroundColor = UIColor.darkGrayColor()
// This is the starting point. Start with step zero.
setViewControllers([getStepOne()], direction: .Forward, animated: false, completion: nil)
}
func getStepZero() -> StepZero {
return storyboard!.instantiateViewControllerWithIdentifier("StepZero") as! StepZero
}
func getStepOne() -> StepOne {
return storyboard!.instantiateViewControllerWithIdentifier("StepOne") as! StepOne
}
func getStepTwo() -> StepTwo {
return storyboard!.instantiateViewControllerWithIdentifier("StepTwo") as! StepTwo
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
if viewController.isKindOfClass(StepTwo) {
// 2 -> 1
return getStepOne()
} else if viewController.isKindOfClass(StepOne) {
// 1 -> 0
return getStepZero()
} else {
// 0 -> end of the road
return nil
}
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
if viewController.isKindOfClass(StepZero) {
// 0 -> 1
return getStepOne()
} else if viewController.isKindOfClass(StepOne) {
// 1 -> 2
return getStepTwo()
} else {
// 2 -> end of the road
return nil
}
}
// Enables pagination dots
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return 3
}
// This only gets called once, when setViewControllers is called
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return selectedIndex
}
}
Let's say in Storyboard you have three viewControllers, you should set identifier for them from identity inspector as StepZero StepOne StepTwo for example and when you instantiate them you do :
func getStepZero() -> StepZero {
return storyboard!.instantiateViewControllerWithIdentifier("StepZero") as! StepZero
}
func getStepOne() -> StepOne {
return storyboard!.instantiateViewControllerWithIdentifier("StepOne") as! StepOne
}
func getStepTwo() -> StepTwo {
return storyboard!.instantiateViewControllerWithIdentifier("StepTwo") as! StepTwo
}
The selected index is the index you want to start with which is number 1. And to start with second view controller call getStepOne() in setViewControllers. if you want to start with view controller 3 use selected index 2 and call getStepTwo()...etc
Download Updated Sample : https://mega.nz/#!EQEFhbwS!0yoy5RvAliQNnjRevWo05wPWk7P08e8DVetRZdjg-ro

You can connect your view controllers by navigation controller . Just select one of your view controllers -> Editor(on the top bar of mac) -> Embed in -> Navigation controller.
Also if you want to swipe you can use a scroll view and only on view controller . Scroll view with content size of 3 view controllers can help you do the same.
Thank you

Follow as below image
Give storyboard identifier as shown in below image
On button click push new viewcontroller
var viewControllerObj=self.storyboard!.instantiateViewControllerWithIdentifier("your storyboard identifier")
self.navigationController!.pushViewController(viewControllerObj, animated: true)

Related

Find which tab bar item is selected

I'm looking for a way to find which tab bar item is select on my tab bar controller.
I've got 5 items and for one of them, I would like to show a "registration view" if the user is not logged in.
I've got all my verifications but I don't find the good way to check if the user tapped the fourth item on my tab bar.
Any ideas ? Thanks
self.tabBarController?.delegate = UIApplication.shared.delegate as? UITabBarControllerDelegate
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if viewController is CalculatorViewController {
print("Redirect to register view")
}
return true
}
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
guard let index = tabBarController.viewControllers?.firstIndex(where: {$0 === viewController}) else {
return false
}
if index == 3 && !IS_LOGGED_IN{
/*** show registration ***/
return false //if you want to disable transition to the associated viewController against that tab
}
return true
}
You can try to use such thing (if you are using navigation controller, for sure)
override func viewDidLoad() {
super.viewDidLoad()
if let index = self.tabBarController?.selectedIndex, index == 3 {
// do things here
}
}
UPD.
Or even like so
override func viewDidLoad() {
super.viewDidLoad()
if !userLogedIn {
self.tabBarController?.selectedIndex = index // index is your tab bar item with login view
}
}

Update current UIViewController UI upon tap of TabBar

I am trying to show an activity indicator when a user taps a certain TabBar item. My problem, I think, lies in the fact that the UI Main thread is frozen.
When a user taps the TabBar I prepare a big data list that takes about six seconds. I get the activity to show everywhere but when they tap the TabBar.
It seems as though the indicator is "running" because when the segued uitableviewcontroller shows, it is showing the indicator. But this is too late and dispatch doesn't seem to do anything either.
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if let visibleViewCtrl = UIApplication.shared.keyWindow?.visibleViewController {
// do whatever you want with your `visibleViewCtrl`
print (String.init(describing: visibleViewCtrl.classForCoder))
DispatchQueue.main.async{
let aprogressView = ProgressView(Message: "Filtering...",
Theme:.Dark,
IsModal:true);
visibleViewCtrl.view.addSubview(aprogressView)
aprogressView.show()
}
}
return true
}
OK, So the problem is that it is going to a tableview controller, which tries to get rows in section almost immediatetly, which then first the fetchrequest which blocks everything as it is on the main thread.
SOLVED:
Added a boolean:
var loadData = false
override func viewDidAppear(_ animated: Bool) {
loadData = true
self.tableView.reloadData()
}
Then the delegates:
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
if loadData == false {
return nil
}
return searchFetchedResultsController.sectionIndexTitles
}
func numberOfSections(in tableView: UITableView) -> Int {
if loadData == false {
return 0
}
Now it will transition to the new controller immediately and thus I can now show feedback from viewWillAppear :
appDelegate.progressView = ProgressView(Message: "Filtering Brands", Theme:.Dark, IsModal:true);
appDelegate.progressView!.show()
This will NOT work with programatical display
self.navigationController?.pushViewController(vc, animated: false)

Transition to ViewController Tapping a TableViewCell

How would one go about making a transition to a view controller by tapping on a table view cell? My current structure is:
I use a navigation controller as my only way of navigating through my app atm.
I use view models of type struct to represent individual cells. These view models conform to a custom protocol, which consists of a method I call cellSelected.
My question is then: How can I transition to a view controller by tapping on a cell using my cellSelected function? Is this possible?
Custom protocol:
protocol CellRepresentable
{
var reuseIdentier: String { get }
func registerCell(tableView: UITableView)
func cellInstance(of tableView: UITableView, for indexPath: IndexPath) -> UITableViewCell
func cellSelected()
}
Example of a view model:
struct ProfileNameViewModel
{
}
extension ProfileNameViewModel: TextPresentable
{
var text: String
{
return "Name"
}
}
extension ProfileNameViewModel: CellRepresentable
{
var reuseIdentier: String
{
get
{
return ProfileTableViewCell<ProfileNameViewModel>.reuseIdentifier
}
}
func registerCell(tableView: UITableView)
{
tableView.register(ProfileTableViewCell<ProfileNameViewModel>.self, forCellReuseIdentifier: ProfileTableViewCell<ProfileNameViewModel>.reuseIdentifier)
}
func cellInstance(of tableView: UITableView, for indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentier, for: indexPath) as! ProfileTableViewCell<ProfileNameViewModel>
cell.configure(withDelegate: self)
return cell
}
func cellSelected()
{
// ??
}
}
I guess you have a couple of options, you can either pass a reference of your navigation controller to the view model, or you can define a closure that gets executed on cellSelected.
Option 1:
struct ProfileNameViewModel {
private let navigationController: UINavigationController
init(withNavigationController navigationController: UINavigationController) {
self.navigationController = navigationController
}
func cellSelected() {
navigationController.pushViewController(...)
}
}
Option 2:
struct ProfileNameViewModel {
private let selectedAction: () -> Void
init(withSelectedAction selectedAction: #escaping () -> Void) {
self.selectedAction = selectedAction
}
func cellSelected() {
selectedAction()
}
}
and on your view controller:
let vm = ProfileNameViewModel(withSelectedAction: { [weak self] in
self?.navigationContoller?.pushViewController(...)
})
1- Go to Storyboard click on your viewController and in the Utility Area which is on the left side of xcode, and give it a storyboard identifier.
2- go back to you code and execute this in cellSelected()
let say your viewController is called MusicListVC
func cellSelected() {
if let viewController = storyboard?.instantiateViewController(withIdentifier: "your Identifier") as? MusicListVC
{
navigationController?.pushViewController(viewController , animated: true)
}
}

UIPageViewController in swift while Scrolling forward and backward, Index is not printing Correct

Here I have programmed the UIPageViewController in Swift. It is working. But when I swipe/scroll the page forward or backward, the index is not printing correctly. The desired page is opening correctly. However, the value of index of this page is not printing correctly in the console. Also, sometimes it is not printing the index on scrolling, while the page is opening correctly. Please help me on how I can get correct index for the page while scrolling. The code is given below.....
var pageViewController: UIPageViewController!
let pages = ["TabOneVc","TwoVC","ThreeVc","FourVc"]
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
{
if let index = pages.indexOf(viewController.restorationIdentifier!){
if index > 0{
print(index)
return viewcontrollerAtIndex(index - 1)
}
}
return nil
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController?
{
if let index = pages.indexOf(viewController.restorationIdentifier!){
if index < pages.count - 1{
print(index)
return viewcontrollerAtIndex(index + 1)
}
}
return nil
}
func viewcontrollerAtIndex(index: Int) -> UIViewController?
{
let vc = storyboard?.instantiateViewControllerWithIdentifier(pages[index])
print("index path",index)
return vc
}
(When you swipe through and land on a view controller it will also load the next view controller so it is ready and this will therefore call viewControllerAtIndex again and print the next index from the one you are currently on, causing the issue of the wrong index.)
The correct way to achieve the current index is to implement this delegate method:
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool){
Check if transitionCompleted is true (for when a page has fully changed) and then use this to get the current index based off your current code:
let viewController = pageViewController.viewControllers![0] as! ViewController
let index = pages.indexOf(viewController.restorationIdentifier!)
This gets the first view controller in the pageViewController array and uses your current code to get its index from restortationIdentifier.
Hope this helps.

Delete Image from UIPageViewController

I have a PageViewController set up so that I can add photos from camera or library and the pageviewcontroller resets itself every time the imagepickercontroller is called so the pageviewcontrol updates and shows. It works perfectly (i think). What i know want to do is have the option to delete one of the photos then call the same reset.
The problem is, i can not get an accurate index value of the current image being displayed in the pageviewcontroller. I try to grab the index from the viewControllerBeforeViewController or AfterViewController or the pagecontrolleratindex... but the index does not follow correctly the way the pagecontrol is designed. I read about willtransitiontoviewcontrollers and didfinsihanimating, but those are not being called, i am thinking because i am getting the images from an array and not new view controllers.
My method to delete works, but the index is not accurate so even though the second image is shown the first image could be the one deleted.
Is there a good way to get the current displayed array index so i can delete the image from array? //this array holds images from imagepicker, i call the restartviewcontrollers evertime there is an imagepicked to update the number of pages and this works.
UPDATE
var pageViewController:UIPageViewController!
var pageImages: [UIImage]? = [] //this array holds images from imagepicker, i call the restartviewcontrollers evertime there is an imagepicked to update the number of pages and this works.
var deletePageIndex: Int = 0
func pageViewControllerAtIndex(index: Int) -> PhotoContentPageViewController {
if (self.pageImages!.count == 0 || index >= self.pageImages!.count) {
return PhotoContentPageViewController()
}
var vc = self.storyboard?.instantiateViewControllerWithIdentifier("PhotoPageContentStoryBoard") as! PhotoContentPageViewController
vc.imageFileName = pageImages![index] as! UIImage
vc.pageIndex = index
return vc
}
func restartViewController() {
var startVc = self.pageViewControllerAtIndex(0) as PhotoContentPageViewController
var viewControllers = NSArray(object: startVc)
self.pageViewController.setViewControllers(viewControllers as? [UIViewController], direction: .Forward, animated: true, completion: nil)
}
func startViewControllers() {
self.pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PhotoPageViewControllerStoryBoard") as! UIPageViewController
self.pageViewController.dataSource = self
pageViewController.delegate = self
var startVc = self.pageViewControllerAtIndex(0) as PhotoContentPageViewController
var viewControllers = NSArray(object: startVc)
self.pageViewController.setViewControllers(viewControllers as? [UIViewController], direction: .Forward, animated: true, completion: nil)
self.pageViewController.view.frame = CGRectMake(0, -10, self.view.frame.size.width, self.view.frame.size.height-30)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! PhotoContentPageViewController
var index = vc.pageIndex as Int
if (index == 0 || index == NSNotFound) {
return nil
}
index--
return self.pageViewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! PhotoContentPageViewController
var index = vc.pageIndex as Int
if (index == NSNotFound) {
return nil
}
index++
if (index == self.pageImages!.count) {
return nil
}
return self.pageViewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, willTransitionToViewControllers pendingViewControllers: [UIViewController]) {
}
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if (!completed) {
return
}
}
`
If you actually are using a UIPageViewControllerDelegate, you can use willtransitiontoviewcontrollers but that method only works if you set the delegate property of the page view controller to your view controller. That way, you can simply get the index of the current view controller in page view controller and use it for deletion.
myPageViewController.delegate = self
And make sure your view controller implements UIPageViewControllerDelegate
UPDATE
Assuming that you have an array of you view controllers, you could implement your data source like this:
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
if let index = pages.indexOf(viewController)?.advancedBy(1) where index < pages.count {
return pages[index]
}
return nil
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
if let index = pages.indexOf(viewController)?.advancedBy(-1) where index > -1 {
return pages[index]
}
return nil
}