Creating a Map Options View

The built-in AWFMapOptionsViewController provides a quick and easy way to control the map layers and various options for an AWFWeatherMap instance. However, you may want to provide a custom map options view within your own applications instead of the built-in version.

The basic approach is to use an UITableView to display the available options, which can then be divided into sections as needed based on any particular grouping of options. This is what the built-in AWFMapOptionsViewController automatically does for you.

The following is a basic implementation of setting up a custom map options view using a UITableView, which can then be customized further using custom UITableViewCell instances as required by your application. Since the example below posts a notification when weather layers are ready to be added and/or removed based on the option selections, you'll need to observe this notification where appropriate so you can then add and remove the layers on your weather map instance.

Objective-C
Swift
#import <UIKit/UIKit.h>
#import <AerisMapKit/AerisMapKit.h>

NS_ASSUME_NONNULL_BEGIN

FOUNDATION_EXPORT NSString * const MapLayersDidChange;

@interface MapOptionsViewController : UIViewController

@property (nonatomic, readonly) UITableView *tableView;

// currently active layers on the map, set before this controller is presented
@property (nonatomic, strong) NSArray<AWFMapLayer> *activeLayers;

@end

NS_ASSUME_NONNULL_END
#import "MapOptionsViewController.h"

@interface MapOptionsViewController () <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) UITableView *tableView;

// array to store the supported layer options to display in the table view
@property (nonatomic, strong) NSArray *layerOptions;

// keep track of layer selection/deselection so we know what needs to be added/removed
// when this controller is dismissed
@property (nonatomic, strong) NSMutableArray *layersToAdd;
@property (nonatomic, strong) NSMutableArray *layersToRemove;
@end

NSString * const MapLayersDidChange = @"MapLayersDidChange";

@implementation MapOptionsViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
    tableView.allowsMultipleSelection = YES;
    tableView.dataSource = self;
    tableView.delegate = self;
    tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
    [self.view addSubview:tableView];
    
    [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"LayerCell"];
    
    self.tableView = tableView;
    
    // array of layer options to display in the table view
    self.layerOptions = @[AWFMapLayerRadar, AWFMapLayerAdvisories];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    [[NSNotificationCenter defaultCenter] postNotificationName:MapLayersDidChange object:self userInfo:@{ @"add": self.layersToAdd, @"remove": self.layersToRemove }];
}

#pragma mark - UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.layerOptions count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"LayerCell"];
    AWFMapLayer layer = self.layerOptions[indexPath.row];
    BOOL selected = [self isLayerTypeActive:layer];
    
    cell.textLabel.text = [AWFWeatherLayer nameForLayerType:layer];
    
    if (selected) {
        [tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
    } else {
        [tableView deselectRowAtIndexPath:indexPath animated:NO];
    }
    
    return cell;
}

#pragma mark - UITableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    AWFMapLayer layer = self.layerOptions[indexPath.row];
    
    if ([self.layersToAdd containsObject:layer] == NO) {
        [self.layersToAdd addObject:layer];
    }
    
    [tableView reloadData];
}

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
    AWFMapLayer layer = self.layerOptions[indexPath.row];
    
    if ([self.layersToAdd containsObject:layer]) {
        [self.layersToAdd removeObject:layer];
    }
    
    if ([self.layersToRemove containsObject:layer] == NO) {
        [self.layersToRemove addObject:layer];
    }
    
    [tableView reloadData];
}

#pragma mark - Private Methods

- (BOOL)isLayerTypeActive:(AWFMapLayer)layerType {
    return ([self.activeLayers containsObject:layerType] && ![self.layersToRemove containsObject:layerType]) || [self.layersToAdd containsObject:layerType];
}

@end
import UIKit
import AerisMapKit

class MapOptionsViewController: UIViewController {
    static let LayersDidChange = NSNotification.Name("MapLayersDidChange")
    
    let tableView = UITableView()
    
    // array to store the supported layer options to display in the table view
    var layerOptions = [AWFMapLayer]()
    
    // currently active layers on the map, set before this controller is presented
    var activeLayers = [AWFMapLayer]()
    
    // keep track of layer selection/deselection so we know what needs to be added/removed
    // when this controller is dismissed
    var layersToAdd = [AWFMapLayer]()
    var layersToRemove = [AWFMapLayer]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.dataSource = self
        tableView.delegate = self
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "LayerCell")
        view.addSubview(tableView)
        
        NSLayoutConstraint.activate([tableView.topAnchor.constraint(equalTo: view.topAnchor),
                                     tableView.leftAnchor.constraint(equalTo: view.leftAnchor),
                                     tableView.rightAnchor.constraint(equalTo: view.rightAnchor),
                                     tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)])
        
        // array of layer options to display in the table view
        layerOptions = [.radar, .advisories]
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        NotificationCenter.default.post(name: MapOptionsViewController.LayersDidChange, object: self, userInfo: ["add": layersToAdd, "remove": layersToRemove])
    }
    
    fileprivate func isLayerActive(layer: AWFMapLayer) -> Bool {
        return (activeLayers.contains(layer) && layersToRemove.contains(layer) == false) || layersToAdd.contains(layer)
    }
}

extension MapOptionsViewController: UITableViewDataSource {
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return layerOptions.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "LayerCell", for: indexPath)
        let layer = layerOptions[indexPath.row]
        let selected = isLayerActive(layer: layer)
        
        cell.textLabel?.text = AWFWeatherLayer.name(forLayerType: layer)
        
        if selected {
            tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
        } else {
            tableView.deselectRow(at: indexPath, animated: false)
        }
        
        return cell
    }
}

extension MapOptionsViewController: UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let layer = layerOptions[indexPath.row]
        if !layersToAdd.contains(layer) {
            layersToAdd.append(layer)
        }
    }
    
    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        let layer = layerOptions[indexPath.row]
        if let index = layersToRemove.index(of: layer) {
            layersToRemove.remove(at: index)
        }
    }
}

Last modified: May 21, 2020