Okan Kocyigit
Okan Kocyigit

Reputation: 13441

InstantiateViewController calls ViewDidLoad

I have a classic master-detail logic and trying to create instance of detail UIViewController with InstantiateViewController inside a click event.

Here is my code,

MasterViewController.cs

detailButton += delegate {
   UIStoryboard Storyboard = UIStoryboard.FromName ("Main", null);
   Console.WriteLine("InstantiateViewController() started");
   var profileController = Storyboard.InstantiateViewController("ProfileController") as ProfileController; 
   Console.WriteLine("InstantiateViewController() ended");
   profileController.Id = 5;  
};

ProfileViewController.cs

public partial class ProfileController : UIViewController
{
    public int Id { get; set; }
    public override void ViewDidLoad() 
    { 
        Console.WriteLine("ViewDidLoad() called");
    }
}

When I click the button output is,

InstantiateViewController() started
ViewDidLoad() called
InstantiateViewController() ended

This means profileController.Id is set after ViewDidLoad() which means I can't load data by Id in ViewDidload event beacuse Id is null.

So my question is why ViewDidLoad() called by InstantiateViewController(), in which method should I load data by Id?

Thanks.

Upvotes: 2

Views: 674

Answers (2)

SushiHangover
SushiHangover

Reputation: 74174

I would do this via a custom segue.

1) Create a custom segue that can be re-used throughout your app:

public class CustomSeque : UIStoryboardSegue // or UIStoryboardPopoverSegue depending upon UI design so you can "POP" controller
{
    public CustomSeque(String identifier, UIViewController source, UIViewController destination) : base (identifier, source, destination) { }

    public override void Perform()
    {
        if (Identifier == "StackOverflow")
        {
            // Are you using a NavigationController?
            if (SourceViewController.NavigationController != null)
                SourceViewController.NavigationController?.PushViewController(DestinationViewController, animated: true);
            else 
                SourceViewController.ShowViewController(DestinationViewController, this);
        } else
            base.Perform();
    }
}

2) Then you can:

UIStoryboard Storyboard = UIStoryboard.FromName("Main", null);
Console.WriteLine("InstantiateViewController() started");
var profileController = Storyboard.InstantiateViewController("ProfileController") as ProfileController;
var seque = new CustomSeque($"StackOverflow", this, profileController);
profileController.Id = 5;
profileController.PrepareForSegue(seque, this); // instead of *this*, you can pass any NSObject that contains data for your controller
seque.Perform();
Console.WriteLine("InstantiateViewController() ended");

If your ProfileController looks like this:

public partial class ProfileController : UIViewController
{

    public ProfileController (IntPtr handle) : base (handle)
    {
        Id = -99;
    }

    public int Id { get; set; }

    public override bool ShouldPerformSegue(string segueIdentifier, NSObject sender)
    {
        if (segueIdentifier == "StackOverflow")
            return true;
        return base.ShouldPerformSegue(segueIdentifier, sender);
    }

    [Export("prepareForSegue:sender:")]
    public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
    {
        base.PrepareForSegue(segue, sender);
        Console.WriteLine("ProfileController.PrepareForSegue()");
        Console.WriteLine($"  - ID = {Id}");
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        Console.WriteLine("ProfileController.ViewDidLoad()");
        Console.WriteLine($"  - ID = {Id}");
    }

    public override void ViewWillAppear(bool animated)
    {
        base.ViewWillAppear(animated);
        Console.WriteLine("ProfileController.ViewWillAppear()");
        Console.WriteLine($"  - ID = {Id}");
    }
}

Your sequenced output would be:

InstantiateViewController() started
ProfileController.PrepareForSegue()
- ID = 5
ProfileController.ViewDidLoad()
- ID = 5
InstantiateViewController() ended
ProfileController.ViewWillAppear()
- ID = 5 

Upvotes: 1

jzeferino
jzeferino

Reputation: 7850

VoewDidLoad is called when the ViewController is loaded into memory.

So, the correct place to get the data is on ViewDidAppear. ViewDidAppear notifies the ViewController that its view was added to a view hierarchy.

UPDATE:

Based on the new information provided in comments you could do something like this:

public partial class ProfileController : UIViewController
{
    private int _id;

    public void SetupProfile (int id)
    {
        // Save the Id if necessary.
        _id = id;
        // Add event with this id related.
    }
    public override void ViewDidLoad() 
    { 
        Console.WriteLine("ViewDidLoad() called");
    }
}

In alternative if you still want to do the event setup in ViewDidAppear you could use this approach with the events:

yourClass.Event -= MyHandler;
yourClass.Event += MyHandler;

Upvotes: 1

Related Questions