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