2

I have a view controller A where I retrieve some values. Under certain circumstances the user has to select a value if multiple are available. So I present another view controller B (UITableView) where I let the user select the entry. How can I call a method from A when I'm currently on B? (to avoid duplicate code) And how can I pass some params in that method?

I read about delegates/protocols, events (.NET style), and notification center. What is the best way to pass a string and call the method from A? Could you provide an example? In the example from Xamarin there is no example of how to define a custom protocol.

Or should I make a field from A public and pass this field to view controller B where I work with the data?

testing
  • 19,681
  • 50
  • 236
  • 417

1 Answers1

3

Events

I managed to get the event approch working. My information was based on the following links:

In my UITableViewController (view controller B) I added the following:

// class variable  
public event EventHandler<BLevelSelectedEventArgs> BLevelSelected;

// event handler
public class BLevelSelectedEventArgs : EventArgs
{
    public BLevelItem bLevel { get; set; }

    public BLevelSelectedEventArgs(BLevelItem bLevel) : base()
    { 
        this.bLevel = bLevel;
    }
}

Here the event as well as the custom event handler is defined. You can define the properties you want to transport the data with the event e.g. string. Here a custom BLevelItem is used. The name of the event handler and the name of the event should be changed for your application. In this UITableViewController class I also have the UITableViewSource defined.

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
    tableView.DeselectRow (indexPath, true); // iOS convention is to remove the highlight

    if (this.controller.BLevelSelected != null) {
        this.controller.BLevelSelected (this, new BLevelSelectedEventArgs (controller.bLevelList[indexPath.Row]));
    }

}

Here the event is raised and the parameter are passed with the event. Now only one listener is missing which reacts on the event. This goes on view controller A:

bLevelController.BLevelSelected += (object sender, BLevelController.BLevelSelectedEventArgs e) => {
    System.Diagnostics.Debug.WriteLine(e.bLevel.bLevelName);
};

bLevelController is an instance of my view controller B. You can access the defined event as seen above, but you have to be aware that you use the class name (BLevelController) to access BLevelSelectedEventArgs.

It would be nice if one you could also add the other possibilities to pass the data between view controllers (Notification center, Protocol/Delegates, ...).


Delegate

A kind of delegate method is used Part 5 - Working with Tables in the iOS Designer. You define a Delegate in your UITableViewController:

public MasterViewController Delegate {get;set;} // will be used to Save, Delete later

When you instantiate the DetailViewController in the MasterViewController you set the Delegate property. Example:

public override void PrepareForSegue (UIStoryboardSegue segue, NSObject sender)
{
    base.PrepareForSegue (segue, sender);

    var detailViewController = segue.DestinationViewController as DetailViewController;

    if (detailViewController != null) {
        detailViewController.Delegate = this;
    }
}

In the DetailViewController you can call any method from the MasterViewController:

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
    {
    tableView.DeselectRow (indexPath, true); // iOS convention is to remove the highlight
    if (this.controller.Delegate != null) {
        this.controller.Delegate.SetItems (this.controller.items[indexPath.Row]);
    }
}

// a method in MasterViewController
public void SetItems(string items){
    this.items = items;
    this.NavigationController.PopViewControllerAnimated (true);
    // do something here
}

This works because I pass the DetailViewController to the DetailViewSource:

// in ViewDidLoad of DetailViewController
this.TableView.Source = new DetailViewSource (this);

// in the constructor of DetailViewSource
private CostCentreController controller;

public CostCentreListSource (CostCentreController controller)
{
    this.controller = controller;
}

NSNotificationCenter

My information are based on xamarin/monotouch-samples and Xamarin NSNotificatioCenter: How can I get the NSObject being passed?.

Simple example without data transfer:

// class level variable
NSObject observer;

// register as observer
public override void ViewWillAppear (bool animated)
{
    base.ViewWillAppear (animated);
    observer = NSNotificationCenter.DefaultCenter.AddObserver ((NSString)UIDevice.OrientationDidChangeNotification, OrientationChanged);
}

// deregister as observer
public override void ViewDidDisappear (bool animated)
{
    base.ViewDidDisappear (animated);
    if (observer != null) { 
        NSNotificationCenter.DefaultCenter.RemoveObserver (observer);
        observer = null;
    }
}

// function which should do something when notification is received
public void OrientationChanged(NSNotification notification){
    Console.WriteLine ("test");
    // perhaps you can do the following as in the linked SO question: NSObject myObject = notification.Object;
}
Community
  • 1
  • 1
testing
  • 19,681
  • 50
  • 236
  • 417