0

I need to pass some data that I grab when the app comes back into the foreground, I have managed to trigger the method but I can't figure out how to trigger it in the existing instance of my ViewController rather than making a new instance.

Map.cs

 public delegate void beginRefreshMapLine(ReturnRouteTaken returnRouteTaken);

  public void updateRouteList(ReturnRouteTaken returnRouteData)
    {
        coordList = new List<CLLocationCoordinate2D>();
        foreach(GPSData point in returnRouteData.GPSData)
        {
            coordList.Add(new CLLocationCoordinate2D { Latitude = double.Parse(point.Lat), Longitude = double.Parse(point.Lng) });

            updateMap(this, new EventArgs());
        }
    }

this is the method I need to trigger in the current instance from AppDelegate.cs

AppDelegate.cs

if (GlobalVar.BoolForKey("trackMe"))
        {
            ReturnRouteTaken returnRouteData = webtools.GetRouteTaken(new ReturnRouteTaken() { TestDriveID = GlobalVar.IntForKey("routeTrackedID") });
            if (returnRouteData.GPSData.Count > 0)
            {

            }
        }

Here is where I am stuck, I have tried looking into delegates and invoking the method that way but I cannot get my head around how to implement it. Any help would be appreciated

SushiHangover
  • 73,120
  • 10
  • 106
  • 165
geolaw
  • 412
  • 1
  • 12
  • 26
  • Possible duplicate of [How to get all the application's ViewControllers in iOS?](http://stackoverflow.com/questions/14757639/how-to-get-all-the-applications-viewcontrollers-in-ios) – jgoldberger - MSFT Mar 18 '17 at 01:26
  • I added a Swift answer before I realised you were asking about c#. Anyway, look at having your view controller subscribe to the UIapplicationWillEnterForeground notification – Paulw11 Mar 18 '17 at 05:56

2 Answers2

1

I flagged this as a possible dupe, but that thread is in Obj-C, however the same concept can easily be applied using Xamarin.iOS.

Just create a Singleton class with an array or List of UIViewControllers as a property in that class and every time you instantiate a new ViewController, add it to the array orList, but also make sure you remove a view controller from the array or List when the view controller is disposed.

e.g. your singleton could look like:

public class ViewControllerHolder
{
    // make constructor private to force use of Instance property
    // to create and get the instance.
    private ViewControllerHolder()
    {
    }

    private static ViewControllerHolder _instance;
    public static ViewControllerHolder Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new ViewControllerHolder();
                _instance.Controllers = new List<UIViewController>();
            }
            return _instance;
        }
    }

    public List<UIViewController> Controllers { get; private set; }

}

And then you can always get access to your List of view controllers with ViewControllerHolder.Instance.Controllers and perform any add or remove operations on it.

And if you are really only interested in the one view controller, then just add that one to the List when instantiated, but do remove it when the view controller is no longer needed so you don't try to access a disposed view controller and also so that the view controller can be garbage collected when it is no longer in use.

Community
  • 1
  • 1
jgoldberger - MSFT
  • 5,978
  • 2
  • 20
  • 44
  • Oh wow, I never thought of this, this is a really cool idea, let me have a go and I will get back to you – geolaw Mar 18 '17 at 01:55
  • Well it didn't crash! which is promising. I need to go for a drive to test it, will update and accept if it works, thanks – geolaw Mar 18 '17 at 02:34
1

Creating a singleton array that holds all the living UIViewControllers works, personally I like to keep things decoupled as much as I can and do not like holding and maintaining a list of objects for no real reason...

You can pass data around via:

  • Selector
  • NoticationCenter

In any UIViewController that you need to "talk" to, you can subscribe to notifications and/or register Selectors.

In your UIViewController register for which Notifications you wish to receive...

public override void ViewDidLoad()
{
    base.ViewDidLoad();
    NSNotificationCenter.DefaultCenter.AddObserver(this, new Selector(Const.StartRefresh), new NSString(Const.StartRefresh), null);
}

Still in your UIViewController, implement the selector that the notification center will perform a send_msg to:

[Export(Const.StartRefresh)]
void LocalStartRefresh(NSNotification notification)
{
    if (notification.Name == Const.StartRefresh)
        Console.WriteLine("StartRefresh from NotificationCenter:" + notification.Object);
}

In your UIApplicationDelegate, use the notification center to publish a new NSNotification to every active UIViewController that has subscribed:

public override void WillEnterForeground(UIApplication application)
{
    NSNotificationCenter.DefaultCenter.PostNotificationName(Const.StartRefresh, new NSString("some custom data"));
}

Or, skip notifications and directly invoke the Selector:

In your UIViewController, implement the selector/method to call:

[Export(Const.StopRefresh)]
void LocalStopRefresh()
{
    Console.WriteLine("StopRefresh from Selector");
}

In your UIApplicationDelegate, send an action to all instanced view controller instances that accept this Selector:

public override void DidEnterBackground(UIApplication application)
{
    var vc = UIApplication.SharedApplication?.KeyWindow?.RootViewController;
    while (vc != null)
    {
        if (vc.RespondsToSelector(new Selector(Const.StopRefresh)))
            UIApplication.SharedApplication.SendAction(new Selector(Const.StopRefresh), vc, this, new UIEvent());
        vc = vc.PresentedViewController;
    }
}
SushiHangover
  • 73,120
  • 10
  • 106
  • 165
  • This is really a better answer than mine. :-) – jgoldberger - MSFT Mar 20 '17 at 18:14
  • @jgoldberger Not such "better", but "different", I've seen the singleton array of ViewControllers approach done many times and it is a valid style, It really depends upon your programming "style" I guess. I prefer "eventing and subscribing" either through the `NSNotificationCenter` (or writing custom C# Event if needed), as is uses the existing iOS framework (or DotNet framework) as it was designed to be used... but again, there are many ways to skin that cat ;-) It also works well in an MVVM or VIPER approach. – SushiHangover Mar 20 '17 at 18:23
  • I always forget about NSNotificationCenter (except when needed to get system notifications like for the keyboard appearing, etc) or just testing for RespondsToSelector. I like your "style" better. :-) – jgoldberger - MSFT Mar 20 '17 at 22:21