Improve UI and remove potential for duplicate
beacons
diff --git a/HiBeacons/Main.storyboard b/HiBeacons/Main.storyboard
index 4b675f8..63388de 100644
--- a/HiBeacons/Main.storyboard
+++ b/HiBeacons/Main.storyboard
@@ -16,30 +16,34 @@
                         <rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                         <subviews>
-                            <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Cds-q7-g9T">
-                                <rect key="frame" x="251" y="504" width="51" height="31"/>
-                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
-                            </switch>
-                            <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" placeholderIntrinsicWidth="177" placeholderIntrinsicHeight="21" text="Enable beacon ranging" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="U3w-jj-HVl">
-                                <rect key="frame" x="20" y="509" width="177" height="21"/>
-                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
-                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
-                                <nil key="highlightedColor"/>
-                            </label>
-                            <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="8Ua-PS-yVS">
-                                <rect key="frame" x="251" y="465" width="51" height="31"/>
-                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
-                            </switch>
-                            <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" placeholderIntrinsicWidth="203" placeholderIntrinsicHeight="21" text="Enable beacon advertising" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MnH-3s-6S0">
-                                <rect key="frame" x="20" y="470" width="203" height="21"/>
-                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
-                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
-                                <nil key="highlightedColor"/>
-                            </label>
                             <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="10" sectionFooterHeight="10" translatesAutoresizingMaskIntoConstraints="NO" id="CS9-7M-geh">
-                                <rect key="frame" x="0.0" y="64" width="320" height="393"/>
+                                <rect key="frame" x="0.0" y="64" width="320" height="504"/>
                                 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                 <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
+                                <prototypes>
+                                    <tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="OperationCell" id="ed8-eW-a13">
+                                        <rect key="frame" x="0.0" y="55" width="320" height="44"/>
+                                        <autoresizingMask key="autoresizingMask"/>
+                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ed8-eW-a13" id="8Lt-Vb-O3A">
+                                            <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
+                                            <autoresizingMask key="autoresizingMask"/>
+                                            <subviews>
+                                                <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="8Ua-PS-yVS">
+                                                    <rect key="frame" x="251" y="6" width="51" height="31"/>
+                                                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                                </switch>
+                                            </subviews>
+                                            <constraints>
+                                                <constraint firstAttribute="trailing" secondItem="8Ua-PS-yVS" secondAttribute="trailing" constant="20" symbolic="YES" id="JPb-oj-TFB"/>
+                                                <constraint firstItem="8Ua-PS-yVS" firstAttribute="centerY" secondItem="8Lt-Vb-O3A" secondAttribute="centerY" id="Pyg-Uy-VDb"/>
+                                            </constraints>
+                                        </tableViewCellContentView>
+                                        <connections>
+                                            <outlet property="accessoryView" destination="8Ua-PS-yVS" id="NLf-S0-v40"/>
+                                        </connections>
+                                    </tableViewCell>
+                                </prototypes>
+                                <sections/>
                                 <connections>
                                     <outlet property="dataSource" destination="vXZ-lx-hvc" id="Czn-Td-yrn"/>
                                     <outlet property="delegate" destination="vXZ-lx-hvc" id="ue4-SY-ipJ"/>
@@ -55,29 +59,17 @@
                         </subviews>
                         <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
                         <constraints>
-                            <constraint firstAttribute="centerX" secondItem="Cds-q7-g9T" secondAttribute="centerX" constant="-115.5" id="4kH-LM-eLD"/>
                             <constraint firstItem="YTO-vF-9qf" firstAttribute="leading" secondItem="CS9-7M-geh" secondAttribute="leading" id="4zl-zx-S2j"/>
-                            <constraint firstItem="U3w-jj-HVl" firstAttribute="centerY" secondItem="Cds-q7-g9T" secondAttribute="centerY" id="6es-J3-bPP"/>
-                            <constraint firstItem="U3w-jj-HVl" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" priority="250" constant="20" symbolic="YES" id="9Vr-p8-IMt"/>
-                            <constraint firstItem="8Ua-PS-yVS" firstAttribute="top" secondItem="CS9-7M-geh" secondAttribute="bottom" constant="8" symbolic="YES" id="ArV-qK-yKz"/>
-                            <constraint firstItem="TQk-42-Sf6" firstAttribute="top" secondItem="U3w-jj-HVl" secondAttribute="bottom" constant="38" id="Av2-TH-mco"/>
-                            <constraint firstItem="U3w-jj-HVl" firstAttribute="leading" secondItem="MnH-3s-6S0" secondAttribute="leading" id="AwR-qf-gC3"/>
                             <constraint firstItem="CS9-7M-geh" firstAttribute="top" secondItem="fQI-hj-WaS" secondAttribute="bottom" constant="44" id="DDy-X3-nyF"/>
+                            <constraint firstItem="TQk-42-Sf6" firstAttribute="top" secondItem="CS9-7M-geh" secondAttribute="bottom" id="Fe8-x4-2Zl"/>
                             <constraint firstItem="YTO-vF-9qf" firstAttribute="trailing" secondItem="CS9-7M-geh" secondAttribute="trailing" id="IPO-G2-Cfs"/>
                             <constraint firstItem="YTO-vF-9qf" firstAttribute="bottom" secondItem="CS9-7M-geh" secondAttribute="top" id="M1b-hE-8Mu"/>
                             <constraint firstItem="CS9-7M-geh" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" id="M8a-U0-Br2"/>
-                            <constraint firstItem="Cds-q7-g9T" firstAttribute="top" secondItem="8Ua-PS-yVS" secondAttribute="bottom" constant="8" symbolic="YES" id="MQi-80-NxX"/>
                             <constraint firstAttribute="trailing" secondItem="CS9-7M-geh" secondAttribute="trailing" id="U0b-9i-hL0"/>
-                            <constraint firstItem="U3w-jj-HVl" firstAttribute="top" secondItem="MnH-3s-6S0" secondAttribute="bottom" constant="18" id="VPr-k1-4Dt"/>
-                            <constraint firstItem="8Ua-PS-yVS" firstAttribute="leading" secondItem="MnH-3s-6S0" secondAttribute="trailing" constant="28" id="gAQ-Re-p14"/>
-                            <constraint firstItem="Cds-q7-g9T" firstAttribute="leading" secondItem="U3w-jj-HVl" secondAttribute="trailing" constant="54" id="jjn-kL-ZL9"/>
-                            <constraint firstItem="8Ua-PS-yVS" firstAttribute="leading" secondItem="Cds-q7-g9T" secondAttribute="leading" id="qOJ-88-znE"/>
                         </constraints>
                     </view>
                     <connections>
-                        <outlet property="advertisingSwitch" destination="8Ua-PS-yVS" id="Xji-pb-0Zv"/>
                         <outlet property="beaconTableView" destination="CS9-7M-geh" id="hb4-1u-FP4"/>
-                        <outlet property="rangingSwitch" destination="Cds-q7-g9T" id="kMu-Vm-wZg"/>
                     </connections>
                 </viewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
diff --git a/HiBeacons/NTViewController.h b/HiBeacons/NTViewController.h
index 18531d9..1ee4e2d 100644
--- a/HiBeacons/NTViewController.h
+++ b/HiBeacons/NTViewController.h
@@ -30,8 +30,6 @@
 @interface NTViewController : UIViewController <CLLocationManagerDelegate, CBPeripheralManagerDelegate,
     UITableViewDataSource, UITableViewDelegate>
 
-@property (nonatomic, weak) IBOutlet UISwitch *advertisingSwitch;
-@property (nonatomic, weak) IBOutlet UISwitch *rangingSwitch;
 @property (nonatomic, weak) IBOutlet UITableView *beaconTableView;
 
 @end
diff --git a/HiBeacons/NTViewController.m b/HiBeacons/NTViewController.m
index 9b7c08e..73304df 100644
--- a/HiBeacons/NTViewController.m
+++ b/HiBeacons/NTViewController.m
@@ -28,7 +28,29 @@
 
 static NSString * const kUUID = @"00000000-0000-0000-0000-000000000000";
 static NSString * const kIdentifier = @"SomeIdentifier";
-static NSString * const kCellIdentifier = @"BeaconCell";
+
+static NSString * const kOperationCellIdentifier = @"OperationCell";
+static NSString * const kBeaconCellIdentifier = @"BeaconCell";
+
+static NSString * const kAdvertisingOperationTitle = @"Advertising";
+static NSString * const kRangingOperationTitle = @"Ranging";
+static NSUInteger const kNumberOfSections = 2;
+static NSUInteger const kNumberOfAvailableOperations = 2;
+static CGFloat const kOperationCellHeight = 44;
+static CGFloat const kBeaconCellHeight = 52;
+static NSString * const kBeaconSectionTitle = @"Looking for beacons...";
+static CGPoint const kActivityIndicatorPosition = (CGPoint){205, 12};
+static NSString * const kBeaconsHeaderViewIdentifier = @"BeaconsHeader";
+
+typedef NS_ENUM(NSUInteger, NTSectionType) {
+    NTOperationsSection,
+    NTDetectedBeaconsSection
+};
+
+typedef NS_ENUM(NSUInteger, NTOperationsRow) {
+    NTAdvertisingRow,
+    NTRangingRow
+};
 
 @interface NTViewController ()
 
@@ -36,23 +58,13 @@
 @property (nonatomic, strong) CLBeaconRegion *beaconRegion;
 @property (nonatomic, strong) CBPeripheralManager *peripheralManager;
 @property (nonatomic, strong) NSArray *detectedBeacons;
+@property (nonatomic, weak) UISwitch *advertisingSwitch;
+@property (nonatomic, weak) UISwitch *rangingSwitch;
 
 @end
 
 @implementation NTViewController
 
-- (void)viewDidLoad
-{
-    [super viewDidLoad];
-    
-    [self.advertisingSwitch addTarget:self
-                              action:@selector(changeAdvertisingState:)
-                    forControlEvents:UIControlEventValueChanged];
-    [self.rangingSwitch addTarget:self
-                              action:@selector(changeRangingState:)
-                    forControlEvents:UIControlEventValueChanged];
-}
-
 #pragma mark - Beacon ranging
 - (void)createBeaconRegion
 {
@@ -99,6 +111,8 @@
     self.locationManager = [[CLLocationManager alloc] init];
     self.locationManager.delegate = self;
     
+    self.detectedBeacons = [NSArray array];
+    
     [self turnOnRanging];
 }
 
@@ -110,13 +124,118 @@
     }
     
     [self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
+
+    NSIndexSet *deletedSections = [self deletedSections];
+    self.detectedBeacons = [NSArray array];
     
-    self.detectedBeacons = nil;
-    [self.beaconTableView reloadData];
+    [self.beaconTableView beginUpdates];
+    if (deletedSections)
+        [self.beaconTableView deleteSections:deletedSections withRowAnimation:UITableViewRowAnimationFade];
+    [self.beaconTableView endUpdates];
 
     NSLog(@"Turned off ranging.");
 }
 
+#pragma mark - Index path management
+- (NSArray *)indexPathsOfRemovedBeacons:(NSArray *)beacons
+{
+    NSMutableArray *indexPaths = nil;
+    
+    NSUInteger row = 0;
+    for (CLBeacon *existingBeacon in self.detectedBeacons) {
+        BOOL stillExists = NO;
+        for (CLBeacon *beacon in beacons) {
+            if ((existingBeacon.major.integerValue == beacon.major.integerValue) &&
+                (existingBeacon.minor.integerValue == beacon.minor.integerValue)) {
+                stillExists = YES;
+                break;
+            }
+        }
+        if (!stillExists) {
+            if (!indexPaths)
+                indexPaths = [NSMutableArray new];
+            [indexPaths addObject:[NSIndexPath indexPathForRow:row inSection:NTDetectedBeaconsSection]];
+        }
+        row++;
+    }
+    
+    return indexPaths;
+}
+
+- (NSArray *)indexPathsOfInsertedBeacons:(NSArray *)beacons
+{
+    NSMutableArray *indexPaths = nil;
+    
+    NSUInteger row = 0;
+    for (CLBeacon *beacon in beacons) {
+        BOOL isNewBeacon = YES;
+        for (CLBeacon *existingBeacon in self.detectedBeacons) {
+            if ((existingBeacon.major.integerValue == beacon.major.integerValue) &&
+                (existingBeacon.minor.integerValue == beacon.minor.integerValue)) {
+                isNewBeacon = NO;
+                break;
+            }
+        }
+        if (isNewBeacon) {
+            if (!indexPaths)
+                indexPaths = [NSMutableArray new];
+            [indexPaths addObject:[NSIndexPath indexPathForRow:row inSection:NTDetectedBeaconsSection]];
+        }
+        row++;
+    }
+    
+    return indexPaths;
+}
+
+- (NSArray *)indexPathsForBeacons:(NSArray *)beacons
+{
+    NSMutableArray *indexPaths = [NSMutableArray new];
+    for (NSUInteger row = 0; row < beacons.count; row++) {
+        [indexPaths addObject:[NSIndexPath indexPathForRow:row inSection:NTDetectedBeaconsSection]];
+    }
+    
+    return indexPaths;
+}
+
+- (NSIndexSet *)insertedSections
+{
+    if (self.rangingSwitch.on && [self.beaconTableView numberOfSections] == kNumberOfSections - 1) {
+        return [NSIndexSet indexSetWithIndex:1];
+    } else {
+        return nil;
+    }
+}
+
+- (NSIndexSet *)deletedSections
+{
+    if (!self.rangingSwitch.on && [self.beaconTableView numberOfSections] == kNumberOfSections) {
+        return [NSIndexSet indexSetWithIndex:1];
+    } else {
+        return nil;
+    }
+}
+
+- (NSArray *)filteredBeacons:(NSArray *)beacons
+{
+    // Filters duplicate beacons out; this may happen temporarily if the originating device changes its Bluetooth id
+    NSMutableArray *mutableBeacons = [beacons mutableCopy];
+    
+    NSMutableSet *lookup = [[NSMutableSet alloc] init];
+    for (int index = 0; index < [beacons count]; index++) {
+        CLBeacon *curr = [beacons objectAtIndex:index];
+        NSString *identifier = [NSString stringWithFormat:@"%@/%@", curr.major, curr.minor];
+        
+        // this is very fast constant time lookup in a hash table
+        if ([lookup containsObject:identifier]) {
+            [mutableBeacons removeObjectAtIndex:index];
+        } else {
+            [lookup addObject:identifier];
+        }
+    }
+    
+    return [mutableBeacons copy];
+}
+
 #pragma mark - Beacon ranging delegate methods
 - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
 {
@@ -125,7 +244,7 @@
         self.rangingSwitch.on = NO;
         return;
     }
-     
+    
     if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorized) {
         NSLog(@"Couldn't turn on ranging: Location services not authorised.");
         self.rangingSwitch.on = NO;
@@ -138,14 +257,37 @@
 - (void)locationManager:(CLLocationManager *)manager
         didRangeBeacons:(NSArray *)beacons
                inRegion:(CLBeaconRegion *)region {
-    if ([beacons count] == 0) {
+    NSArray *filteredBeacons = [self filteredBeacons:beacons];
+    
+    if (filteredBeacons.count == 0) {
         NSLog(@"No beacons found nearby.");
     } else {
-        NSLog(@"Found %lu %@.", (unsigned long)[beacons count], [beacons count] > 1 ? @"beacons" : @"beacon");
+        NSLog(@"Found %lu %@.", (unsigned long)[filteredBeacons count],
+                [filteredBeacons count] > 1 ? @"beacons" : @"beacon");
     }
     
-    self.detectedBeacons = beacons;
-    [self.beaconTableView reloadData];
+    NSIndexSet *insertedSections = [self insertedSections];
+    NSIndexSet *deletedSections = [self deletedSections];
+    NSArray *deletedRows = [self indexPathsOfRemovedBeacons:filteredBeacons];
+    NSArray *insertedRows = [self indexPathsOfInsertedBeacons:filteredBeacons];
+    NSArray *reloadedRows = nil;
+    if (!deletedRows && !insertedRows)
+        reloadedRows = [self indexPathsForBeacons:filteredBeacons];
+    
+    self.detectedBeacons = filteredBeacons;
+    
+    [self.beaconTableView beginUpdates];
+    if (insertedSections)
+        [self.beaconTableView insertSections:insertedSections withRowAnimation:UITableViewRowAnimationFade];
+    if (deletedSections)
+        [self.beaconTableView deleteSections:deletedSections withRowAnimation:UITableViewRowAnimationFade];
+    if (insertedRows)
+        [self.beaconTableView insertRowsAtIndexPaths:insertedRows withRowAnimation:UITableViewRowAnimationFade];
+    if (deletedRows)
+        [self.beaconTableView deleteRowsAtIndexPaths:deletedRows withRowAnimation:UITableViewRowAnimationFade];
+    if (reloadedRows)
+        [self.beaconTableView reloadRowsAtIndexPaths:reloadedRows withRowAnimation:UITableViewRowAnimationNone];
+    [self.beaconTableView endUpdates];
 }
 
 #pragma mark - Beacon advertising
@@ -226,51 +368,131 @@
 }
 
 #pragma mark - Table view functionality
-- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+- (NSString *)detailsStringForBeacon:(CLBeacon *)beacon
 {
-    CLBeacon *beacon = self.detectedBeacons[indexPath.row];
-    
-    UITableViewCell *defaultCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
-                                                          reuseIdentifier:kCellIdentifier];
-    
-    defaultCell.textLabel.text = beacon.proximityUUID.UUIDString;
-
-    NSString *proximityString;
+    NSString *proximity;
     switch (beacon.proximity) {
         case CLProximityNear:
-            proximityString = @"Near";
+            proximity = @"Near";
             break;
         case CLProximityImmediate:
-            proximityString = @"Immediate";
+            proximity = @"Immediate";
             break;
         case CLProximityFar:
-            proximityString = @"Far";
+            proximity = @"Far";
             break;
         case CLProximityUnknown:
         default:
-            proximityString = @"Unknown";
+            proximity = @"Unknown";
             break;
     }
-    defaultCell.detailTextLabel.text = [NSString stringWithFormat:@"%@, %@ • %@ • %f • %li",
-        beacon.major.stringValue, beacon.minor.stringValue, proximityString, beacon.accuracy, (long)beacon.rssi];
-    defaultCell.detailTextLabel.textColor = [UIColor grayColor];
     
-    return defaultCell;
+    NSString *format = @"%@, %@ • %@ • %f • %li";
+    return [NSString stringWithFormat:format, beacon.major, beacon.minor, proximity, beacon.accuracy, beacon.rssi];
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    UITableViewCell *cell = nil;
+    switch (indexPath.section) {
+        case NTOperationsSection: {
+            cell = [tableView dequeueReusableCellWithIdentifier:kOperationCellIdentifier];
+            switch (indexPath.row) {
+                case NTAdvertisingRow:
+                    cell.textLabel.text = kAdvertisingOperationTitle;
+                    self.advertisingSwitch = (UISwitch *)cell.accessoryView;
+                    [self.advertisingSwitch addTarget:self
+                                               action:@selector(changeAdvertisingState:)
+                                     forControlEvents:UIControlEventValueChanged];
+                    break;
+                case NTRangingRow:
+                default:
+                    cell.textLabel.text = kRangingOperationTitle;
+                    self.rangingSwitch = (UISwitch *)cell.accessoryView;
+                    [self.rangingSwitch addTarget:self
+                                           action:@selector(changeRangingState:)
+                                 forControlEvents:UIControlEventValueChanged];
+                    break;
+            }
+        }
+            break;
+        case NTDetectedBeaconsSection:
+        default: {
+            CLBeacon *beacon = self.detectedBeacons[indexPath.row];
+
+            cell = [tableView dequeueReusableCellWithIdentifier:kBeaconCellIdentifier];
+            
+            if (!cell)
+                cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
+                                              reuseIdentifier:kBeaconCellIdentifier];
+            
+            cell.textLabel.text = beacon.proximityUUID.UUIDString;
+            cell.detailTextLabel.text = [self detailsStringForBeacon:beacon];
+            cell.detailTextLabel.textColor = [UIColor grayColor];
+        }
+            break;
+    }
+    
+    return cell;
 }
 
 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
 {
-    return 1;
+    if (self.rangingSwitch.on) {
+        return kNumberOfSections;       // All sections visible
+    } else {
+        return kNumberOfSections - 1;   // Beacons section not visible
+    }
 }
 
 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
 {
-    return self.detectedBeacons.count;
+    switch (section) {
+        case NTOperationsSection:
+            return kNumberOfAvailableOperations;
+        case NTDetectedBeaconsSection:
+        default:
+            return self.detectedBeacons.count;
+    }
 }
 
 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
 {
-    return @"Detected beacons";
+    switch (section) {
+        case NTOperationsSection:
+            return nil;
+        case NTDetectedBeaconsSection:
+        default:
+            return kBeaconSectionTitle;
+    }
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    switch (indexPath.section) {
+        case NTOperationsSection:
+            return kOperationCellHeight;
+        case NTDetectedBeaconsSection:
+        default:
+            return kBeaconCellHeight;
+    }
+}
+
+- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
+{
+    UITableViewHeaderFooterView *headerView =
+        [[UITableViewHeaderFooterView alloc] initWithReuseIdentifier:kBeaconsHeaderViewIdentifier];
+    
+    // Adds an activity indicator view to the section header
+    UIActivityIndicatorView *indicatorView =
+        [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
+    [headerView addSubview:indicatorView];
+
+    indicatorView.frame = (CGRect){kActivityIndicatorPosition, indicatorView.frame.size};
+    
+    [indicatorView startAnimating];
+    
+    return headerView;
 }
 
 @end
diff --git a/HiBeacons/main.m b/HiBeacons/main.m
index 07a55b3..67d24c9 100644
--- a/HiBeacons/main.m
+++ b/HiBeacons/main.m
@@ -6,8 +6,6 @@
 //  Copyright (c) 2013 Nick Toumpelis. All rights reserved.
 //
 
-#import <UIKit/UIKit.h>
-
 #import "NTHiBeaconsDelegate.h"
 
 int main(int argc, char * argv[])
diff --git a/screenshot.png b/screenshot.png
index 81a73f2..f328365 100644
--- a/screenshot.png
+++ b/screenshot.png
Binary files differ