Home > Articles > Mobile Application Development & Programming

  • Print
  • + Share This
This chapter is from the book

Recipe: Checking Device Proximity and Battery States

The UIDevice class offers APIs that enable you to keep track of device characteristics including the states of the battery and proximity sensor. Recipe 1-1 demonstrates how you can enable and query monitoring for these two technologies. Both provide updates in the form of notifications, which you can subscribe to so your application is informed of important updates.

Enabling and Disabling the Proximity Sensor

Proximity is an iPhone-specific feature at this time. The iPod touch and iPad do not offer proximity sensors. Unless you have some pressing reason to hold an iPhone against body parts (or vice versa), using the proximity sensor accomplishes little.

When enabled, it has one primary task. It detects whether there’s a large object right in front of it. If so, it switches the screen off and sends a general notification. Move the blocking object away and the screen switches back on. This prevents you from pressing buttons or dialing the phone with your ear when you are on a call. Some poorly designed protective cases keep the iPhone’s proximity sensors from working properly.

Siri uses this feature. When you hold the phone up to your ear, it records your query, sending it to be interpreted. Siri’s voice interface does not depend on a visual GUI to operate.

Recipe 1-1 also demonstrates how to work with proximity sensing on the iPhone. Its code uses the UIDevice class to toggle proximity monitoring and subscribes to UIDeviceProximityStateDidChangeNotification to catch state changes. The two states are on and off. When the UIDevice proximityState property returns YES, the proximity sensor has been activated.

Monitoring the Battery State

You can programmatically keep track of the battery and device state. These APIs enable you to know the level to which the battery is charged and whether the device is plugged into a charging source. The battery level is a floating-point value that ranges between 1.0 (fully charged) and 0.0 (fully discharged). It provides an approximate discharge level that you can use to query before performing operations that put unusual strain on the device.

For example, you might want to caution your user about performing a large series of mathematical computations and suggest that the user plug in to a power source. You retrieve the battery level via this UIDevice call. The value returned is produced in 5% increments:

NSLog(@"Battery level: %0.2f%",
    [UIDevice currentDevice].batteryLevel * 100);

The charge state has four possible values. The unit can be charging (that is, connected to a power source), full, unplugged, and a catchall “unknown.” Recover the state using the UIDevice batteryState property:

NSArray *stateArray = @[
    @"Battery state is unknown",
    @"Battery is not plugged into a charging source",
    @"Battery is charging",
    @"Battery state is full"];

NSLog(@"Battery state: %@",
    stateArray[[UIDevice currentDevice].batteryState]);

Don’t think of these choices as persistent states. Instead, think of them as momentary reflections of what is actually happening to the device. They are not flags. They are not OR’ed together to form a general battery description. Instead, these values reflect the most recent state change.

You can easily monitor state changes by responding to notifications that the battery state has changed. In this way, you can catch momentary events, such as when the battery finally recharges fully, when the user has plugged in to a power source to recharge, and when the user disconnects from that power source.

To start monitoring, set the batteryMonitoringEnabled property to YES. During monitoring, the UIDevice class produces notifications when the battery state or level changes. Recipe 1-1 subscribes to both notifications. Please note that you can also check these values directly, without waiting for notifications. Apple provides no guarantees about the frequency of level change updates, but as you can tell by testing this recipe, they arrive in a fairly regular fashion.

Recipe 1-1. Monitoring Proximity and Battery

// View the current battery level and state
- (void) peekAtBatteryState
{
    NSArray *stateArray = [NSArray arrayWithObjects:
                           @"Battery state is unknown",
                           @"Battery is not plugged into a charging source",
                           @"Battery is charging",
                           @"Battery state is full", nil];

    NSString *status = [NSString stringWithFormat:
        @"Battery state: %@, Battery level: %0.2f%%",
        [stateArray objectAtIndex:[UIDevice currentDevice].batteryState],
        [UIDevice currentDevice].batteryLevel * 100];

    NSLog(@"%@", status);
}

// Show whether proximity is being monitored
- (void) updateTitle
{
    self.title = [NSString stringWithFormat:@"Proximity %@",
       [UIDevice currentDevice].proximityMonitoringEnabled ? @"On" : @"Off"];
}

// Toggle proximity monitoring off and on
- (void) toggle: (id) sender
{
    // Determine the current proximity monitoring and toggle it
    BOOL isEnabled = [UIDevice currentDevice].proximityMonitoringEnabled;
    [UIDevice currentDevice].proximityMonitoringEnabled = !isEnabled;
    [self updateTitle];
}

- (void) loadView
{
    [super loadView];

    // Enable toggling and initialize title
    self.navigationItem.rightBarButtonItem =
        BARBUTTON(@"Toggle", @selector(toggle:));
    [self updateTitle];

    // Add proximity state checker
    [[NSNotificationCenter defaultCenter]
        addObserverForName:UIDeviceProximityStateDidChangeNotification
        object:nil queue:[NSOperationQueue mainQueue]
        usingBlock:^(NSNotification *notification) {
            // Sensor has triggered either on or off
            NSLog(@"The proximity sensor %@",
                [UIDevice currentDevice].proximityState ?
                @"will now blank the screen" : @"will now restore the screen");
    }];

    // Enable battery monitoring
    [[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];

    // Add observers for battery state and level changes
    [[NSNotificationCenter defaultCenter]
        addObserverForName:UIDeviceBatteryStateDidChangeNotification
        object:nil queue:[NSOperationQueue mainQueue]
        usingBlock:^(NSNotification *notification) {
            // State has changed
            NSLog(@"Battery State Change");
            [self peekAtBatteryState];
    }];

    [[NSNotificationCenter defaultCenter]
        addObserverForName:UIDeviceBatteryLevelDidChangeNotification
        object:nil queue:[NSOperationQueue mainQueue]
        usingBlock:^(NSNotification *notification) {
            // Level has changed
            NSLog(@"Battery Level Change");
            [self peekAtBatteryState];
    }];
}

Detecting Retina Support

In recent years, Apple introduced the Retina display on its flagship devices. Its pixel density is, according to Apple, high enough so the human eye cannot distinguish individual pixels. Apps shipped with higher-resolution art take advantage of this improved display quality.

The UIScreen class offers an easy way to check whether the current device offers a built-in Retina display. Check the screen scale property, which provides the factor that converts from the logical coordinate space (points, approximately 1/160th of an inch) into a device coordinate space (pixels). It is 1.0 for standard displays, so one point corresponds to one pixel. It is 2.0 for Retina displays (4 pixels per point):

- (BOOL) hasRetinaDisplay
{
    return ([UIScreen mainScreen].scale == 2.0f);
}

The UIScreen class also offers two useful display-size properties. The bounds returns the screen’s bounding rectangle, measured in points. This gives you the full size of the screen, regardless of any onscreen elements such as status bars, navigation bars, or tab bars. The applicationFrame property, also measured in points, excludes the status bar, providing the frame for your application’s initial window size.

  • + Share This
  • 🔖 Save To Your Account