0

I am creating an app for my personal project using programmaticUI and storyboard for the UI part, but i found an issue when i tried to performSegue from my "SecondViewController" to my "ThirdViewController" , i added the "identifier" in my segue like usual: enter image description here

And then i called the "performSegue" from my SecondViewController:

import UIKit

class SecondViewController: UIViewController {
    
    private var myItem = [SecondItem]()
    lazy var myTableView : UITableView = {
       let myTable = UITableView()
        myTable.translatesAutoresizingMaskIntoConstraints = false
       return myTable
    }()

    private let myContentView : UIView = {
        let view = UIView()
        view.backgroundColor = .gray
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    lazy var label : UILabel = {
        let myLabel = UILabel()
        myLabel.text = "Hello"
        return myLabel
    }()
    
    private let unameTextField : UITextField = {
       let txtField = UITextField()
        txtField.backgroundColor = .white
        txtField.placeholder = "Username"
        txtField.borderStyle = .roundedRect
        txtField.translatesAutoresizingMaskIntoConstraints = false
       return txtField
    }()
    
    private let pwordTxtField : UITextField = {
        let txtField = UITextField()
        txtField.placeholder = "Password"
        txtField.borderStyle = .roundedRect
        txtField.translatesAutoresizingMaskIntoConstraints = false
        return txtField
    }()
    
    private let loginBtn : UIButton = {
        let btn = UIButton(type: .system)
        btn.backgroundColor = .blue
        btn.setTitle("Login", for: .normal)
        btn.tintColor = .white
        btn.layer.cornerRadius = 5
        btn.clipsToBounds = true
        btn.translatesAutoresizingMaskIntoConstraints = false
        btn.addTarget(self, action: #selector(btnPressed), for: .touchUpInside)
        return btn
    }()
    
//I called the "performSegue" here
    @objc func btnPressed() {
        performSegue(withIdentifier: "gotoBla", sender: self)
        print("button pressed")
    }
    
    lazy var imageView : UIImageView = {
       let image = UIImage(named: "image_4")
       let imageView = UIImageView(image: image)
       imageView.translatesAutoresizingMaskIntoConstraints = false
       return imageView
    }()
    
    
    
    
    func setAutoLayout(){
        
        let guide = view.safeAreaLayoutGuide
   
        myContentView.anchor(top: guide.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: view.frame.height / 3, enableInsets: true)
        
        imageView.anchor(top: myContentView.topAnchor, left: nil , bottom: nil , right: nil , paddingTop: 10, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 80, height: 80, enableInsets: true)
        imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

        unameTextField.anchor(top: imageView.bottomAnchor, left: myContentView.leftAnchor, bottom: nil, right: myContentView.rightAnchor, paddingTop: 10, paddingLeft: 20, paddingBottom: 5, paddingRight: 20, width: 0, height: 40, enableInsets: true)
        
        pwordTxtField.anchor(top: unameTextField.bottomAnchor, left: myContentView.leftAnchor, bottom: nil, right: myContentView.rightAnchor, paddingTop: 30, paddingLeft: 20, paddingBottom: 0, paddingRight: 20, width: 0, height: 40, enableInsets: true)
        
        loginBtn.anchor(top: pwordTxtField.bottomAnchor, left: myContentView.leftAnchor, bottom: nil, right: myContentView.rightAnchor , paddingTop: 20, paddingLeft: 20, paddingBottom: 0, paddingRight: 20, width: 0, height: 40, enableInsets: true)

        //TableView
        myTableView.topAnchor.constraint(equalTo: myContentView.bottomAnchor).isActive = true
        myTableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        myTableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        myTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        
        myItem.append(SecondItem(text: "first"))
        myItem.append(SecondItem(text: "Second"))
        myItem.append(SecondItem(text: "Third"))

        
        view.backgroundColor = .white
        
        view.addSubview(myContentView)
        myContentView.addSubview(unameTextField)
        myContentView.addSubview(pwordTxtField)
        myContentView.addSubview(loginBtn)
        myContentView.addSubview(imageView)

        myTableView.register(SecondTableViewCell.self, forCellReuseIdentifier: K.SecondTableViewCell.identifier)
        myTableView.delegate = self
        myTableView.dataSource = self

        view.addSubview(myTableView)
        
        setAutoLayout()

        
    }
}

extension SecondViewController : UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print(indexPath.row)
    }
}

extension SecondViewController : UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        myItem.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: K.SecondTableViewCell.identifier, for: indexPath) as! SecondTableViewCell
        cell.second = myItem[indexPath.row]
        cell.selectionStyle = .none
        
        return cell
    }
}

And for the Third View Controller, i am not yet adding some code in there

import UIKit

class ThirdViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }
    
}

But everytime i run the app and click the login button,it always gave me this error: enter image description here

This is what my app looks like: enter image description here

Do i miss something here? I am a beginner by the way, i hope you guys can help me. Thank you

2 Answers2

0

When posting questions here, it would be good for you to take a few minutes to review How to Ask

However, based on the little bit of info you've provided...

Almost certainly the problem is that you are adding View Controllers in Storyboard and then improperly trying to use them via code.

For example, I'm guessing that you have code in your "first" view controller to load and display SecondViewController like this:

@objc func showSecondTapped(_ sender: Any) {
    let vc = SecondViewController()
    navigationController?.pushViewController(vc, animated: true)
}

and then in SecondViewController you're trying to use the Storyboard associated segue with this:

@objc func btnPressed(_ sender: Any) {
    performSegue(withIdentifier: "gotoBla", sender: self)
    print("button pressed")
}

However, that segue doesn't exist as part of SecondViewController code ... it is part of the Storyboard object.

Back in your first view controller, if you load and push to SecondViewController like this:

@objc func showSecondTapped(_ sender: Any) {
    if let vc = storyboard?.instantiateViewController(withIdentifier: "secondVC") as? SecondViewController {
        navigationController?.pushViewController(vc, animated: true)
    }
}

you will then be able to call performSegue because you loaded it from the Storyboard.

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • Hi @DonMag thank you for your respond, i just updated my question to make it more clear and easier to read. So, if i am not mistaken, i need to add "showSecondTapped" function as a condition to use the "performSegue"? – Novando Santosa Mar 01 '22 at 04:01
  • No, the thing, is that if `self.storyboard` is nil, as DonMag is guessting, then, it means that `SecondViewController` was iniitialized without linking it to the associated Storyboard where `gotoBla` segue exists. If you want to instantiate it with the storyboard, and get its segue, you need to instantiate first the storyboard, not doing `SecondViewController()`. So, how was `SecondViewController` created/shown? – Larme Mar 01 '22 at 09:48
  • @NovandoSantosa - you don't need to add *add "showSecondTapped" function* ... you (most likely) need to change the way you're currently loading `SecondViewController` to the method shown in that function. Although, I suppose the real question should be: Why are you trying to setup and use Segues in Storyboard, when you're doing all the rest of it in code? – DonMag Mar 01 '22 at 12:00
  • @DonMag okay, i think i got it now, I just changed my performSegue to self.present and I instantiate my Third ViewController with the storyboard, now it working well. So, the reason i mixed up a storyboard and programmatic UI is that i thought it will be a great idea if someday I want to build the UI faster, i go with the storyboard, and if i want to build some complex UI, i will switch to code. Is that a bad idea? – Novando Santosa Mar 01 '22 at 14:12
  • @NovandoSantosa - there are benefits and drawbacks to both approaches. The important thing is to understand that anything done in Storyboard / Interface builder ***must*** be instantiated to be used. Similarly, if you add something like a `UILabel` in Storyboard and connect it via `@IBOutlet`, then try to use it *without* using `.instantiateViewController()`, that will crash. – DonMag Mar 01 '22 at 14:21
  • @DonMag okay noted. Thank you for your explanation – Novando Santosa Mar 01 '22 at 14:35
-1
        if let vc = storyboard?.instantiateViewController(withIdentifier: "secondVC") as? SecondViewController {
            
        navigationController?.pushViewController(vc, animated: true)


         // if navigationController?.pushViewController doesn't work you can try this
        present(vc, animated: true) {
            // anything you want to perform after presenting new screen
        }
// and try this to show in full screen with different transition effect
        
        vc.modalTransitionStyle = .crossDissolve
        vc.modalPresentationStyle = .fullScreen
        
        present(vc, animated: true) {
            // anything you want to perform after presenting new screen
           }
}
shyank_dev
  • 39
  • 1
  • 4
  • i already tried to use "pushViewController" and it was working fine, but what i want try to achieve here is using "performSegue" instead of "pushViewController" – Novando Santosa Mar 01 '22 at 04:04