skip to Main Content

I am looking to have an interactive push view controller. So if the user pans from the right edge of the screen, it will pop to the next view controller. I have found this CocoaPods: https://github.com/rickytan/RTInteractivePush, but it is written in Objective-C, so I am unsure how to use it. On my own I have been able to come up with a pan gesture that pushes a view, however it is not interactive:

swipeGesture = UIPanGestureRecognizer(target: self, action:#selector(swiped(_:)))
swipeGesture.delegate = self
view.addGestureRecognizer(swipeGesture)

@objc func swiped(_ gestureRecognizer: UIPanGestureRecognizer) {
    let newView = View()
    self.navigationController?.pushViewController(newView, animated: true)
}

Any help would be greatly appreciated!

2

Answers


  1. The current code is perfect it you have only one viewcontroller up next.

    But if you have to 2 or more viewController up next then interactive push is unuseful technique. Vies versa for the interactive pop controller we just have to pop top view form the navigation stack which make sense. Please have a look the the image below which describe the scenario for both push and pop.

    enter image description here

    Login or Signup to reply.
  2. You can do it programmatically with UIPageViewController:

    Set your UIPageViewController class:

    import UIKit
    
    class MyControllerContainer: UIPageViewController {
    
    // set UIPageViewController transition style
    override init(transitionStyle style: UIPageViewController.TransitionStyle, navigationOrientation: UIPageViewController.NavigationOrientation, options: [UIPageViewController.OptionsKey : Any]? = nil) {
        super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        print("init(coder:) has not been implemented")
    }
    
    var pages = [UIViewController]()
    var pageControl = UIPageControl()
    let initialPage = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        setup()
        style()
        layout()
     }
    }
    

    Now set style, setup, and layout func:

    extension MyControllerContainer {
    
    func setup() {
        dataSource = self
        delegate = self
        
        pageControl.addTarget(self, action: #selector(pageControlDragged(_:)), for: .valueChanged)
        
        // create an array of viewController
        let page1 = ViewController1()
        let page2 = ViewController2()
        let page3 = ViewController3()
        
        pages.append(page1)
        pages.append(page2)
        pages.append(page3)
        
        // set initial viewController to be displayed
        setViewControllers([pages[initialPage]], direction: .forward, animated: true, completion: nil)
    }
    
    func style() {
        pageControl.translatesAutoresizingMaskIntoConstraints = false
        pageControl.currentPageIndicatorTintColor = .white
        pageControl.pageIndicatorTintColor = UIColor(white: 1, alpha: 0.3)
        pageControl.numberOfPages = pages.count
        pageControl.currentPage = initialPage
    }
    
    func layout() {
        view.addSubview(pageControl)
        
        NSLayoutConstraint.activate([
            pageControl.widthAnchor.constraint(equalTo: view.widthAnchor),
            pageControl.heightAnchor.constraint(equalToConstant: 20),
            view.bottomAnchor.constraint(equalToSystemSpacingBelow: pageControl.bottomAnchor, multiplier: 1),
        ])
     }
    }
    

    set how we change controller when pageControl Dragged:

    extension MyControllerContainer {
    
    // How we change controller when pageControl Dragged.
    @objc func pageControlDragged(_ sender: UIPageControl) {
        setViewControllers([pages[sender.currentPage]], direction: .forward, animated: true, completion: nil)
     }
    }
    

    after that set UIPageViewController delegate and datasource:

    // MARK: - DataSources
    
    extension MyControllerContainer: UIPageViewControllerDataSource {
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        
        guard let currentIndex = pages.firstIndex(of: viewController) else { return nil }
        
        if currentIndex == 0 {
            return nil // stop presenting controllers when swipe from left to right in ViewController1
        } else {
            return pages[currentIndex - 1] // go previous
         }
        }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        
        guard let currentIndex = pages.firstIndex(of: viewController) else { return nil }
        
        if currentIndex == 2 {
            print("Last index...")
        }
        
        if currentIndex < pages.count - 1 {
            return pages[currentIndex + 1] // go next
        } else {
            return nil // stop presenting controllers when swipe from right to left in ViewController3
        }
     }
    }
    
    // MARK: - Delegates
    
    extension MyControllerContainer: UIPageViewControllerDelegate {
    
    // How we keep our pageControl in sync with viewControllers
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        
        guard let viewControllers = pageViewController.viewControllers else { return }
        guard let currentIndex = pages.firstIndex(of: viewControllers[0]) else { return }
        
        pageControl.currentPage = currentIndex
     }
    }
    

    Now add your viewControllers, in my case 3:

    // MARK: - ViewControllers
    
    class ViewController1: UIViewController {
    
    let mylabel1: UILabel = {
        let label = UILabel()
        label.text = "Controller 1"
        label.textAlignment = .center
        label.textColor = .white
        label.font = .systemFont(ofSize: 20, weight: .semibold)
        label.translatesAutoresizingMaskIntoConstraints = false
        
        return label
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemRed
        
        view.addSubview(mylabel1)
        mylabel1.heightAnchor.constraint(equalToConstant: 50).isActive = true
        mylabel1.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
        mylabel1.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        mylabel1.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
     }
    }
    
    class ViewController2: UIViewController {
    
    let mylabel2: UILabel = {
        let label = UILabel()
        label.text = "Controller 2"
        label.textAlignment = .center
        label.textColor = .white
        label.font = .systemFont(ofSize: 20, weight: .semibold)
        label.translatesAutoresizingMaskIntoConstraints = false
        
        return label
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemGreen
        
        view.addSubview(mylabel2)
        mylabel2.heightAnchor.constraint(equalToConstant: 50).isActive = true
        mylabel2.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
        mylabel2.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        mylabel2.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
     }
    }
    
    class ViewController3: UIViewController {
    
    let mylabel3: UILabel = {
        let label = UILabel()
        label.text = "Controller 3"
        label.textAlignment = .center
        label.textColor = .white
        label.font = .systemFont(ofSize: 20, weight: .semibold)
        label.translatesAutoresizingMaskIntoConstraints = false
        
        return label
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBlue
        
        view.addSubview(mylabel3)
        mylabel3.heightAnchor.constraint(equalToConstant: 50).isActive = true
        mylabel3.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
        mylabel3.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        mylabel3.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
     }
    }
    

    This is the result:

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search