While the AWFWeatherMapViewController
class handles all of the setup necessary to get a fully-functional weather map up and running without any work, there may be times when you want more control over your own weather map.
This example includes the various phases of setting up a weather map in much of the same way as AWFWeatherMapViewController
. Depending on your own implementation, you may choose to only use parts of this setup.
The primary steps include:
AWFWeatherMap
instance and assign its delegate
.AWFTimelineView
(or alternative custom view) which will be used to show animation progress and allow the user to control playback of the animation. You must also set the timeline view’s delegate
to receive time change events from the view.toggleAnimation
method that will handle starting or stopping the animation based on the animation’s current state. Set the timeline view’s playback control action to call this method when tapped.AWFTimelineViewDelegate
methods to respond to time changes from the timeline view and update the weather map accordingly.AWFWeatherMapDelegate
methods to respond to animation state changes from the weather map and update the timeline view accordingly.
#import <UIKit/UIKit.h>
#import <AerisMapKit/AerisMapKit.h>
@interface MapViewController : UIViewController
@property (nonatomic, readonly) AWFWeatherMap *weatherMap;
@property (nonatomic, readonly) AWFTimelineView *timelineView;
@end
#import "MapViewController.h"
@interface MapViewController () <AWFWeatherMapDelegate, AWFTimelineViewDelegate>
@property (nonatomic, strong) AWFWeatherMap *weatherMap;
@property (nonatomic, strong) AWFTimelineView *timelineView;
@end
@implementation MapViewController
- (void)viewDidLoad {
[super viewDidLoad];
// setup the weather map config
AWFWeatherMapConfig *config = [AWFWeatherMapConfig config];
// make any changes to your configuration before creating the weather map...
// create the weather map instance
AWFWeatherMap *weatherMap = [[AWFWeatherMap alloc] initWithMapType:AWFWeatherMapTypeApple config:config];
weatherMap.delegate = self;
[self.view addSubview:weatherMap.weatherMapView];
self.weatherMap = weatherMap;
// setup the timeline view that will be used to control the weather map's animation
AWFTimelineView *timelineView = [[AWFTimelineView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 50.0)];
timelineView.delegate = self;
timelineView.startDate = weatherMap.timeline.fromTime;
timelineView.endDate = weatherMap.timeline.toTime;
timelineView.currentTime = weatherMap.timeline.fromTime;
[self.view addSubview:timelineView];
self.timelineView = timelineView;
// layout the map view container
weatherMap.weatherMapView.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[[weatherMap.weatherMapView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
[weatherMap.weatherMapView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor],
[weatherMap.weatherMapView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor],
[weatherMap.weatherMapView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]]];
// layout the timeline view
CGFloat timelineHeight = 50.0;
timelineView.translatesAutoresizingMaskIntoConstraints = NO;
timelineView.preservesSuperviewLayoutMargins = YES;
[NSLayoutConstraint activateConstraints:@[[timelineView.topAnchor constraintEqualToAnchor:self.view.layoutMarginsGuide.bottomAnchor constant:-timelineHeight],
[timelineView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor],
[timelineView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor],
[timelineView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]]];
// add action handler for timeline's playback control
[self.timelineView.playButton addTarget:self action:@selector(toggleAnimation:) forControlEvents:UIControlEventTouchUpInside];
// start the weather map at the current time
[self.weatherMap goToTime:[NSDate date]];
}
#pragma mark - Control Handlers
- (void)toggleAnimation:(id)target {
UIButton *btn = (UIButton *)target;
if (self.weatherMap.isAnimating || self.weatherMap.isLoadingAnimation) {
btn.selected = NO;
[self.weatherMap stopAnimating];
} else {
btn.selected = YES;
[self.weatherMap startAnimating];
}
}
#pragma mark - AWFTimelineViewDelegate
// Observe change events on the timelineView so the weather map can be updated accordingly, such as when the user pans
// the timeline or taps the "Now" control to go to the current time/date.
- (void)timelineView:(AWFTimelineView *)timelineView didPanToDate:(NSDate *)date {
if (self.weatherMap.config.timelineScrubbingEnabled) {
[self.weatherMap pauseAnimation];
[self.weatherMap goToTime:date];
}
}
- (void)timelineView:(AWFTimelineView *)timelineView didSelectDate:(NSDate *)date {
[self.weatherMap stopAnimating];
[self.weatherMap goToTime:date];
}
#pragma mark - AWFWeatherMapDelegate
// Updates to the timelineView are handled by handling several AWFWeatherMapDelegate methods that notify our controller about changes to the weather map's
// timeline, such as start/end date changes and when the map begins or stops animating.
- (void)weatherMap:(AWFWeatherMap *)weatherMap didUpdateTimelineRangeFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate {
self.timelineView.startDate = fromDate;
self.timelineView.endDate = toDate;
self.timelineView.currentTime = self.weatherMap.timeline.currentTime;
}
- (void)weatherMapDidStartAnimating:(AWFWeatherMap *)weatherMap {
self.timelineView.playButton.selected = YES;
}
- (void)weatherMapDidStopAnimating:(AWFWeatherMap *)weatherMap {
self.timelineView.playButton.selected = NO;
}
- (void)weatherMapDidResetAnimation:(AWFWeatherMap *)weatherMap {
[self.timelineView setProgress:0.0 animated:YES];
}
- (void)weatherMap:(AWFWeatherMap *)weatherMap animationDidUpdateToDate:(NSDate *)date {
self.timelineView.currentTime = date;
}
- (void)weatherMapDidStartLoadingAnimationData:(AWFWeatherMap *)weatherMap {
self.timelineView.playButton.selected = YES;
[self.timelineView showLoading:YES];
}
- (void)weatherMapDidFinishLoadingAnimationData:(AWFWeatherMap *)weatherMap {
[self.timelineView setProgress:1.0 animated:YES];
[self.timelineView showLoading:NO];
}
- (void)weatherMapDidCancelLoadingAnimationData:(AWFWeatherMap *)weatherMap {
self.timelineView.playButton.selected = NO;
[self.timelineView setProgress:0.0 animated:YES];
[self.timelineView showLoading:NO];
}
- (void)weatherMap:(AWFWeatherMap *)weatherMap didUpdateAnimationDataLoadingProgress:(NSInteger)totalLoaded total:(NSInteger)total {
[self.timelineView setProgress:((CGFloat)totalLoaded / (CGFloat)total) animated:YES];
}
@end
class MapViewController: UIViewController {
var weatherMap: AWFWeatherMap!
var timelineView: AWFTimelineView!
override func viewDidLoad() {
super.viewDidLoad()
// setup the weather map config
let config = AWFWeatherMapConfig()
// make any changes to your configuration before creating the weather map instance...
// create the weather map instance
weatherMap = AWFWeatherMap(mapType: .apple, config: config)
weatherMap.delegate = self
view.addSubview(weatherMap.weatherMapView)
timelineView = AWFTimelineView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 50.0))
timelineView.delegate = self
timelineView.startDate = weatherMap.timeline.fromTime
timelineView.endDate = weatherMap.timeline.toTime
timelineView.currentTime = weatherMap.timeline.fromTime
view.addSubview(timelineView)
// layout the map view container
weatherMap.weatherMapView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([weatherMap.weatherMapView.topAnchor.constraint(equalTo: view.topAnchor),
weatherMap.weatherMapView.leftAnchor.constraint(equalTo: view.leftAnchor),
weatherMap.weatherMapView.rightAnchor.constraint(equalTo: view.rightAnchor),
weatherMap.weatherMapView.bottomAnchor.constraint(equalTo: view.bottomAnchor)])
// layout the timeline view
let timelineHeight: CGFloat = 50.0
timelineView.translatesAutoresizingMaskIntoConstraints = false
timelineView.preservesSuperviewLayoutMargins = true
NSLayoutConstraint.activate([timelineView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, constant: -timelineHeight),
timelineView.leftAnchor.constraint(equalTo: view.leftAnchor),
timelineView.rightAnchor.constraint(equalTo: view.rightAnchor),
timelineView.bottomAnchor.constraint(equalTo: view.bottomAnchor)])
// add an action handler for the timeline view's playback control
timelineView.playButton.addTarget(self, action: #selector(toggleAnimation(sender:)), for: .touchUpInside)
// start the weather map at the current time
weatherMap.go(toTime: Date())
}
// MARK: Control Handlers
@objc func toggleAnimation(sender: Any) {
if let btn = sender as? UIButton {
if weatherMap.isAnimating || weatherMap.isLoadingAnimation {
btn.isSelected = false
weatherMap.stopAnimating()
} else {
btn.isSelected = true
weatherMap.startAnimating()
}
}
}
}
// Observe change events on the timelineView so the weather map can be updated accordingly, such as when the user pans
// the timeline or taps the "Now" control to go to the current time/date.
extension MapViewController: AWFTimelineViewDelegate {
func timelineView(_ timelineView: AWFTimelineView!, didPanTo date: Date!) {
if weatherMap.config.timelineScrubbingEnabled {
weatherMap.pauseAnimation()
weatherMap.go(toTime: date)
}
}
func timelineView(_ timelineView: AWFTimelineView!, didSelect date: Date!) {
weatherMap.stopAnimating()
weatherMap.go(toTime: date)
}
}
// Updates to the timelineView are handled by handling several AWFWeatherMapDelegate methods that notify our controller about changes to the weather map's
// timeline, such as start/end date changes and when the map begins or stops animating.
extension MapViewController: AWFWeatherMapDelegate {
func weatherMap(_ weatherMap: AWFWeatherMap, didUpdateTimelineRangeFrom fromDate: Date, to toDate: Date) {
timelineView.startDate = fromDate
timelineView.endDate = toDate
timelineView.currentTime = weatherMap.timeline.currentTime
}
func weatherMapDidStartAnimating(_ weatherMap: AWFWeatherMap) {
timelineView.playButton.isSelected = true
}
func weatherMapDidStopAnimating(_ weatherMap: AWFWeatherMap) {
timelineView.playButton.isSelected = false
}
func weatherMapDidResetAnimation(_ weatherMap: AWFWeatherMap) {
timelineView.setProgress(0, animated: true)
}
func weatherMap(_ weatherMap: AWFWeatherMap, animationDidUpdateTo date: Date) {
timelineView.currentTime = date
}
func weatherMapDidStartLoadingAnimationData(_ weatherMap: AWFWeatherMap) {
timelineView.playButton.isSelected = true
timelineView.showLoading(true)
}
func weatherMapDidFinishLoadingAnimationData(_ weatherMap: AWFWeatherMap) {
timelineView.setProgress(1.0, animated: true)
timelineView.showLoading(false)
}
func weatherMapDidCancelLoadingAnimationData(_ weatherMap: AWFWeatherMap) {
timelineView.playButton.isSelected = false
timelineView.setProgress(0, animated: true)
timelineView.showLoading(false)
}
func weatherMap(_ weatherMap: AWFWeatherMap, didUpdateAnimationDataLoadingProgress totalLoaded: Int, total: Int) {
timelineView.setProgress(CGFloat(totalLoaded) / CGFloat(total), animated: true)
}
}
Last modified: July 30, 2020