Darren Cheng
Darren Cheng

Reputation: 13

Swift4, How to present another view controller by click button which programmatically created inside current view controller's subview

I had programmatically created some UIButtons in UIStackView. The StackView is an subView of original view controller.

How could I present another view controller by click button?

I had try control drag to create segue from original to destination controller and try to use self.performSegue(withIdentifier: "showMealTable", sender: self) for navigate.

But it throw error at runtime said "..has no segue with identifier 'showMealTable''".

It's my StackView class

@IBDesignable class TimeLineHeadControl: UIStackView {
   //MARK: Properties
   private let timeLineControllerInstance = TimeLineController()
   ....
   button.addTarget(self.timeLineControllerInstance, action: #selector(TimeLineController.breakfastButtonTapped(button:)), for: .touchUpInside)
   ....
}

And my original view controller

class TimeLineController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        //self.performSegue(withIdentifier: "showMealTable", sender: self)
    }

    ...

    @objc func breakfastButtonTapped(button: UIButton) {
        print("at Time Line Controller")
        self.performSegue(withIdentifier: "showMealTable", sender: self)
    }
    ...

}

Created segue

error message here

Upvotes: 1

Views: 3376

Answers (3)

Sweeper
Sweeper

Reputation: 271660

Let's talk about the reasons for the error first.

Here:

private let timeLineControllerInstance = TimeLineController()

This VC you created with the above line is not the same VC that you are seeing in your app. This is another brand new instance of TimeLineController, not initialised through the storyboard. Therefore, it does not have the segue.

You need to actually set the VC that is currently presented as timeLineControllerInstance.

However, either way, you are violating MVC principles. TimeLineHeadControl is a view. Views should not be dependent on controllers. Views can send out messages (target-action/delegates) and controllers can choose to listen to these. Controllers choose what views to control, not the other way round.

You should really call addTarget in the view controller,

// in viewDidLoad
yourStackView.button.addTarget(self, action: #selector(breakfastButtonTapped(button:)), for: .touchUpInside)

or create a TimeLineHeadControlDelegate and use the delegate pattern.

Upvotes: 1

junaid
junaid

Reputation: 203

you can do this by using code...

 @objc func breakfastButtonTapped(button: UIButton) {
        print("at Time Line Controller")
            let vc = self.storyBoard.instantiateViewController(withIdentifier: "yourVCidentifier") as! YourVC
        self.present(vc,animated: true, completion: nil)
    }

if you want to do that with segue.. give storyBoard segue identifier to your segue and than call perform segue.

Upvotes: 0

Abhishek Jain
Abhishek Jain

Reputation: 888

By definition, a segue can't really exist independently of a storyboard. Even if you see the name of the class: UIStoryboardSegue. You cannot create segue programmatically but what you can do is set up the segue in storyboard and then have a function on that button and use the function to perform the action.

Moreover, you can even achieve this without using a segue. You can create a method in your common view controller (base class) that will transition to a new view controller. In your case, base class can be MealViewController and new view controller can be MealTableViewController.

- (IBAction)pushNewViewController
{
    NewViewController *newVC = [[NewViewController alloc] init];

    // do any setup you need for newVC like passing data.

    [self presentModalViewController:newVC animated:YES];
}

Now you can call this method when the appropriate button is clicked.

Upvotes: 0

Related Questions