Brian Long Consultancy & Training Services
            Ltd.
        March 2011
        Accompanying source files available through this 
            download link
        
    
        The iPhone’s Proximity Sensor is used to turn the phone’s display off when you answer
        a call and move the phone next to your face. It can doubtless be employed in various
        other useful scenarios and so it is good to see how we can be notified of proximity
        state changes. The mechanism is simple; proximity to something is either detected
        or not and there will be a state change notification when the situation changes.
        This code is in ViewDidAppear():
UIDevice.CurrentDevice.ProximityMonitoringEnabled =  true;
        if (UIDevice.CurrentDevice.ProximityMonitoringEnabled)
        NSNotificationCenter.DefaultCenter.AddObserver(UIDevice.ProximityStateDidChangeNotification,
    (n) => { proximityLabel.Text = 
        UIDevice.CurrentDevice.ProximityState ? "Proximity detected" : "Proximity not detected"; });
else
            proximityLabel.Text = "Proximity sensor not available";
    Not all devices have a proximity sensor (the simulator doesn’t, for example) so the advice is to turn proximity monitoring on and then check to see if it did in fact successfully turned on. If not there is no proximity sensor.
        If you have the appropriate hardware then you need to arrange to respond to state
        changes. This is one of the cases that do not use the familiar delegate object (or
        event property alternative) approach. Instead it uses notifications orchestrated
        from a notification centre that requires observer methods to notice them. Every
        application has a notification centre accessible with NSNotificationCenter.DefaultCenter
        and of the various overloads AddObserver() offers the simplest one
        takes the notification identifier and a delegate that is passed an NSNotification
        object (which we ignore in the code). The declaration in the MonoTouch documentation
        looks like this:
public NSObject AddObserver (string aName, Action<NSNotification> notify)
        Action<T> is a standard .NET generic delegate type declared in
        the System namespace. It represents a function that returns no value but takes a
        parameter of type T. In Objective-C the notification identifiers are
        literal strings and you could very well use this as the first parameter to AddObserver():
new NSString("UIDeviceProximityStateDidChangeNotification")
        However the UIDevice class has a number of these notification identifiers
        set up as properties for your convenience, for example: UIDevice.OrientationDidChangeNotification,
        UIDevice.BatteryLevelDidChangeNotification, UIDevice.BatteryStateDidChangeNotification.
        Don’t forget that we enabled proximity state monitoring so in ViewDidDisappear()
        it is appropriate to turn it off:
if (UIDevice.CurrentDevice.ProximityMonitoringEnabled)
            UIDevice.CurrentDevice.ProximityMonitoringEnabled = false;
    The battery status monitoring operates quite similar to the proximity state monitoring. Not all devices support battery status monitoring (for example the iPhone Simulator does not) and to see if it’s supported you again enable monitoring and then check whether monitoring is still enabled. If not, then it’s not supported.
Whilst battery status monitoring can be done with notifications, that is only appropriate if you want monitoring to be on all the time. To be a bit more battery-friendly we can instead just check once every so often, say once a minute or two. Each time we want to check we turn battery monitoring on, if possible, check the battery level and battery state and then turn monitoring off. This is the method that does the checking:
private void ReadBatteryStatus()
{
        var dev = UIDevice.CurrentDevice;
    dev.BatteryMonitoringEnabled = true;
            if (dev.BatteryMonitoringEnabled)
                try
                {
        batteryLabel.Text = string.Format("{0}% - {1}", Math.Round(dev.BatteryLevel * 100), dev.BatteryState);
}
        finally
                {
                    dev.BatteryMonitoringEnabled = false;
        }
    else
            {
                batteryLabel.Text = "Battery level monitoring not available";
        UpdateBatteryStatusTimer.Invalidate();
}
}
To run this code at fixed intervals we need a scheduled repeating timer. Timers only work when they are scheduled on a run loop (the iOS equivalent of a Windows message loop) and need to be repeating to fire more than once.
        In ViewDidAppear() the timer is set up to trigger every 60 seconds
        and the battery check code executed an initial time:
    
UpdateBatteryStatusTimer = NSTimer.CreateRepeatingScheduledTimer(
    60, new NSAction(ReadBatteryStatus));
        ReadBatteryStatus();
        NSAction is rather like Action<T> described earlier,
        but is a Mono delegate type that represents a function that returns no value and
        takes no parameters. This looks a little different to the anonymous method we passed
        in when setting up the proximity state notification, but we could make it look more
        similar by writing it like this:
UpdateBatteryStatusTimer = NSTimer.CreateRepeatingScheduledTimer(
    60, () => { ReadBatteryStatus(); });
        ReadBatteryStatus();
However, it can be made more intuitive as:
UpdateBatteryStatusTimer = NSTimer.CreateRepeatingScheduledTimer(
60, ReadBatteryStatus);
ReadBatteryStatus();
        Note that in the timer event handler earlier if battery monitoring is not available
        then the timer is cancelled using its Invalidate() method. It is also
        important to remember to cancel the timer in ViewDidDisappear() by
        calling the same method.
Go back to the top of this page
Go back to start of this article