Search code examples
iosswiftuicollectionviewsegue

Performing Segue from within a View


I have a custom class named MenuBar which is a UIView used to present a UICollectionView to a view controller. The problem I'm having is using a segue to another view controller when a UICollectionViewCell is selected. Since MenuBar is not a view controller, I can't seem to call performSegue(). I created a segue in my storyboard between the two view controllers with an identifier. How can I properly trigger a segue from within my custom view class? How would this normally be done? I tried making a new instance of my view controller so that I could us the .performSegue() from within the didSelectItemAt collectionView function but I don't think this is the proper way to do it at all. example:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let vc = UARTModuleViewController()
    vc.performSegue(withIdentifier: "colorSelection", sender: nil)
}

Any suggestions on how to perform the segue from within this function would be greatly appreciated. Below I will supply the key components of my existing code so that you can see what I'm doing. There is a lot more code in both classes but I think this should be enough for the general idea of what I'm trying to accomplish.

view controller:

class UARTModuleViewController: UIViewController, CBPeripheralManagerDelegate {
    override func viewDidLoad() {
        setupMenuBar()
    }

    let menuBar: MenuBar = {
        let mb = MenuBar()
        return mb
    }()

    private func setupMenuBar() {
        view.addSubview(menuBar)

        menuBar.translatesAutoresizingMaskIntoConstraints = false
        menuBar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
        menuBar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
        menuBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
        menuBar.heightAnchor.constraint(equalToConstant: 80).isActive = true
    }
}

MenuBar.swift:

class MenuBar : UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

    lazy var collectionView : UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
        cv.backgroundColor = UIColor.clear
        cv.dataSource = self
        cv.delegate = self
        return cv
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupCollectionView()
    }

    private func setupCollectionView() {
        self.addSubview(collectionView)
        collectionView.register(MenuCell.self, forCellWithReuseIdentifier: "MenuCell")

        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.topAnchor.constraint(equalTo: topAnchor).isActive = true
        collectionView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
        collectionView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
        collectionView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
    }

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let vc = UARTModuleViewController() // I want to perform a segue from here
        vc.performSegue(withIdentifier: "colorSelection", sender: nil)
    }

}

Thanks in advance to anyone who can provide helpful advice and suggestions!


Solution

  • This

    let vc = UARTModuleViewController()
    

    Creates another object on the fly that's independent of the presented one and may cause a crash if the vc isn't programmatically created , instead you need a delegate

    lazy var menuBar: MenuBar = {
        let mb = MenuBar()
        mb.delegate = self
        return mb
    }()
    

    And add this var inside MenuBar

    weak var delegate:UARTModuleViewController?
    

    Then use it like this

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 
        delegate?.performSegue(withIdentifier: "colorSelection", sender: nil)
    }
    

    Should say that a better approach for reusability is to create a protocol

    protocol ColManager { 
       func navigate()
    }
    

    With

    weak var delegate:ColManager?
    

    And any class that wants to use should conform to the protocol and implement the navigate method