[Maps] Add MapClicked event (#5317)
authorjcmanke <jmanke1227@gmail.com>
Wed, 24 Apr 2019 00:57:22 +0000 (19:57 -0500)
committerSamantha Houts <samhouts@users.noreply.github.com>
Wed, 24 Apr 2019 00:57:22 +0000 (17:57 -0700)
* Add MapClicked event to Map

* Android MapClicked implementation

* iOS MapClicked implementation

* UWP implementation of MapClicked

* Null check before sending MapClicked event from renderers

* Rewrote MapGallery page in XAML

Xamarin.Forms.Controls/GalleryPages/MapGallery.cs [deleted file]
Xamarin.Forms.Controls/GalleryPages/MapGallery.xaml [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/MapGallery.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj
Xamarin.Forms.Maps.Android/MapRenderer.cs
Xamarin.Forms.Maps.UWP/MapRenderer.cs
Xamarin.Forms.Maps.iOS/MapRenderer.cs
Xamarin.Forms.Maps/Map.cs
Xamarin.Forms.Maps/MapClickedEventArgs.cs [new file with mode: 0644]

diff --git a/Xamarin.Forms.Controls/GalleryPages/MapGallery.cs b/Xamarin.Forms.Controls/GalleryPages/MapGallery.cs
deleted file mode 100644 (file)
index d1e088a..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Xamarin.Forms.Maps;
-
-namespace Xamarin.Forms.Controls
-{
-       public class MapGallery : ContentPage
-       {
-               readonly StackLayout _stack;
-
-               public MapGallery ()
-               {
-                       if (Device.RuntimePlatform == Device.iOS && Device.Idiom == TargetIdiom.Tablet)
-                               Padding = new Thickness(0, 0, 0, 60);
-
-                       var map = MakeMap ();
-
-                       map.MoveToRegion (MapSpan.FromCenterAndRadius (new Position (41.890202, 12.492049), Distance.FromMiles (0.5)));
-
-                       var searchAddress = new SearchBar { Placeholder = "Search Address" };
-
-                       searchAddress.SearchButtonPressed += async (e, a) => {
-                               var addressQuery = searchAddress.Text;
-                               searchAddress.Text = "";
-                               searchAddress.Unfocus ();
-
-                               var positions = (await (new Geocoder ()).GetPositionsForAddressAsync (addressQuery)).ToList ();
-                               if (!positions.Any ())
-                                       return;
-
-                               var position = positions.First ();
-                               map.MoveToRegion (MapSpan.FromCenterAndRadius (position,
-                                       Distance.FromMeters (4000)));
-                               map.Pins.Add (new Pin {
-                                       Label = addressQuery,
-                                       Position = position,
-                                       Address = addressQuery
-                               });
-                       };
-
-                       var buttonAddressFromPosition = new Button { Text = "Address From Position" };
-                       buttonAddressFromPosition.Clicked += async (e, a) => {
-                               var addresses = (await (new Geocoder ()).GetAddressesForPositionAsync (new Position (41.8902, 12.4923))).ToList ();
-                               foreach (var ad in addresses)
-                                       Debug.WriteLine (ad);
-                       };
-
-                       var buttonZoomIn = new Button { Text = "Zoom In" };
-                       buttonZoomIn.Clicked += (e, a) => map.MoveToRegion (map.VisibleRegion.WithZoom (5f));
-
-                       var buttonZoomOut = new Button { Text = "Zoom Out" };
-                       buttonZoomOut.Clicked += (e, a) => map.MoveToRegion (map.VisibleRegion.WithZoom (1 / 3f));
-
-                       var mapTypeButton = new Button { Text = "Map Type" };
-                       mapTypeButton.Clicked += async (e, a) => {
-                               var result = await DisplayActionSheet ("Select Map Type", null, null, "Street", "Satellite", "Hybrid");
-                               switch (result) {
-                               case "Street":
-                                       map.MapType = MapType.Street;
-                                       break;
-                               case "Satellite":
-                                       map.MapType = MapType.Satellite;
-                                       break;
-                               case "Hybrid":
-                                       map.MapType = MapType.Hybrid;
-                                       break;
-                               }
-                       };
-
-                       var buttonHome = new Button { Text = "Home" };
-                       buttonHome.Clicked += (a, e) => {
-                               map.MoveToRegion (MapSpan.FromCenterAndRadius (new Position (41.890202, 12.492049), Distance.FromMiles (0.5)));
-                       };
-
-                       var buttonRemove = new Button { Text = "Remove Pin" };
-                       buttonRemove.Clicked += (a, e) =>
-                       {
-                               map.Pins.RemoveAt(0);
-                       };
-
-                       _stack = new StackLayout {
-                               Spacing = 0,
-                               Padding = new Thickness (30, 0)
-                       };
-                       //stack.SetRowSpacing (1, 0);
-
-                       Title = "Map Gallery";
-
-                       var buttonZoomPin = new Button { Text = "Zoom Pin" };
-                       buttonZoomPin.Clicked += (a, e) => {
-                               var pos = new Position (41.011995, -8.642995);
-                               map.Pins.Clear ();
-                               map.Pins.Add (new Pin { Position = pos, Label = "Rui" });
-                               map.MoveToRegion (MapSpan.FromCenterAndRadius (pos, Distance.FromMiles (0.5)));
-                       };
-
-                       var buttonEditPin = new Button { Text = "Edit Pin" };
-                       buttonEditPin.Clicked += (a, e) =>
-                       {
-                               var pin = map.Pins.First();
-
-                               pin.Label += " Edited";
-                               pin.Address = "Edited";
-
-                               var pos = new Position(pin.Position.Latitude + 1, pin.Position.Longitude + 1);
-                               pin.Position = pos;
-                               map.MoveToRegion(MapSpan.FromCenterAndRadius(pos, Distance.FromMiles(0.5)));
-                       };
-
-                       map.VerticalOptions = LayoutOptions.FillAndExpand;
-                       _stack.Children.Add (searchAddress);
-                       _stack.Children.Add (map);
-                       _stack.Children.Add (mapTypeButton);
-                       _stack.Children.Add (buttonZoomIn);
-                       _stack.Children.Add (buttonZoomOut);
-                       _stack.Children.Add (buttonAddressFromPosition);
-                       _stack.Children.Add (buttonHome);
-                       _stack.Children.Add (buttonZoomPin);
-                       _stack.Children.Add (buttonEditPin);
-                       _stack.Children.Add (buttonRemove);
-
-                       Content = _stack;
-               }
-
-               public static Map MakeMap ()
-               {
-                       Pin colosseum = null;
-                       Pin pantheon = null;
-                       Pin chapel = null;
-
-                       var map = new Map {
-                               IsShowingUser = false,
-                               Pins = {
-                                       (colosseum = new Pin {
-                                               Type = PinType.Place,
-                                               Position = new Position (41.890202, 12.492049),
-                                               Label = "Colosseum",
-                                               Address = "Piazza del Colosseo, 00184 Rome, Province of Rome, Italy"
-                                       }),
-                                       (pantheon = new Pin {
-                                               Type = PinType.Place,
-                                               Position = new Position (41.898652, 12.476831),
-                                               Label = "Pantheon",
-                                               Address = "Piazza della Rotunda, 00186 Rome, Province of Rome, Italy"
-                                       }),
-                                       (chapel = new Pin {
-                                               Type = PinType.Place,
-                                               Position = new Position (41.903209, 12.454545),
-                                               Label = "Sistine Chapel",
-                                               Address = "Piazza della Rotunda, 00186 Rome, Province of Rome, Italy"
-                                       })
-                               }
-                       };
-
-                       colosseum.Clicked += PinClicked;
-                       pantheon.Clicked += PinClicked;
-                       chapel.Clicked += PinClicked;
-                       return map;
-               }
-
-               static void PinClicked (object sender, EventArgs e)
-               {
-                       Pin pin = (Pin)sender;
-                       Application.Current.MainPage.DisplayAlert("Pin Click",
-                               $"You clicked the {pin.Label} pin, located at {pin.Address}, or coordinates ({pin.Position.Latitude}, {pin.Position.Longitude})",
-                               "OK");
-               }
-       }
-}
diff --git a/Xamarin.Forms.Controls/GalleryPages/MapGallery.xaml b/Xamarin.Forms.Controls/GalleryPages/MapGallery.xaml
new file mode 100644 (file)
index 0000000..39378ca
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<ContentPage
+    x:Class="Xamarin.Forms.Controls.MapGallery"
+    xmlns="http://xamarin.com/schemas/2014/forms"
+    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+    xmlns:maps="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps"
+    Title="Map Gallery">
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto" />
+            <RowDefinition Height="*" />
+            <RowDefinition Height="Auto" />
+            <RowDefinition Height="Auto" />
+            <RowDefinition Height="*" />
+        </Grid.RowDefinitions>
+
+        <SearchBar
+            Grid.Row="0"
+            Placeholder="Search Address"
+            SearchButtonPressed="SearchForAddress" />
+
+        <Label
+            x:Name="LastPinClickLabel"
+            Grid.Row="2" />
+        <Label
+            x:Name="LastMapClickLabel"
+            Grid.Row="3" />
+        <ScrollView Grid.Row="4">
+            <StackLayout>
+                <Button
+                    Clicked="MapTypeClicked"
+                    Text="Map Type" />
+                <Button
+                    Clicked="ZoomInClicked"
+                    Text="Zoom In" />
+                <Button
+                    Clicked="ZoomOutClicked"
+                    Text="Zoom Out" />
+                <Button
+                    Clicked="ReverseGeocodeClicked"
+                    Text="Address From Position" />
+                <Button
+                    Clicked="HomeClicked"
+                    Text="Home" />
+                <Button
+                    Clicked="ZoomPinClicked"
+                    Text="Zoom Pin" />
+                <Button
+                    Clicked="EditPinClicked"
+                    Text="Edit Pin" />
+                <Button
+                    Clicked="RemovePinClicked"
+                    Text="Remove Pin" />
+            </StackLayout>
+        </ScrollView>
+    </Grid>
+</ContentPage>
\ No newline at end of file
diff --git a/Xamarin.Forms.Controls/GalleryPages/MapGallery.xaml.cs b/Xamarin.Forms.Controls/GalleryPages/MapGallery.xaml.cs
new file mode 100644 (file)
index 0000000..570d7cf
--- /dev/null
@@ -0,0 +1,159 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Xamarin.Forms;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.Maps;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Controls
+{
+       [XamlCompilation(XamlCompilationOptions.Compile)]
+       public partial class MapGallery : ContentPage
+       {
+               readonly Geocoder _geocoder = new Geocoder();
+               readonly Map Map;
+
+               public MapGallery()
+               {
+                       InitializeComponent();
+
+                       Map = MakeMap();
+                       Map.Pins.ForEach(pin => pin.Clicked += PinClicked);
+                       Map.MapClicked += MapClicked;
+
+                       ((Grid)Content).Children.Add(Map, 0, 1);
+               }
+
+               public static Map MakeMap()
+               {
+                       return new Map(MapSpan.FromCenterAndRadius(new Position(41.890202, 12.492049), Distance.FromMiles(0.5)))
+                       {
+                               IsShowingUser = false,
+                               Pins =
+                               {
+                                       new Pin
+                                       {
+                                               Type = PinType.Place,
+                                               Position = new Position (41.890202, 12.492049),
+                                               Label = "Colosseum",
+                                               Address = "Piazza del Colosseo, 00184 Rome, Province of Rome, Italy"
+                                       },
+                                       new Pin
+                                       {
+                                               Type = PinType.Place,
+                                               Position = new Position (41.898652, 12.476831),
+                                               Label = "Pantheon",
+                                               Address = "Piazza della Rotunda, 00186 Rome, Province of Rome, Italy"
+                                       },
+                                       new Pin
+                                       {
+                                               Type = PinType.Place,
+                                               Position = new Position (41.903209, 12.454545),
+                                               Label = "Sistine Chapel",
+                                               Address = "Piazza della Rotunda, 00186 Rome, Province of Rome, Italy"
+                                       }
+                               }
+                       };
+               }
+
+               void PinClicked(object sender, EventArgs e)
+               {
+                       LastPinClickLabel.Text = $"Last Pin Clicked: {((Pin)sender).Label}";
+               }
+
+               async void SearchForAddress(object sender, EventArgs e)
+               {
+                       var searchAddress = (SearchBar)sender;
+                       var addressQuery = searchAddress.Text;
+                       searchAddress.Text = "";
+                       searchAddress.Unfocus();
+
+                       var positions = (await _geocoder.GetPositionsForAddressAsync(addressQuery)).ToList();
+                       if (!positions.Any())
+                               return;
+
+                       var position = positions.First();
+                       Map.MoveToRegion(MapSpan.FromCenterAndRadius(position, Distance.FromMeters(4000)));
+                       Map.Pins.Add(new Pin
+                       {
+                               Label = addressQuery,
+                               Position = position,
+                               Address = addressQuery
+                       });
+               }
+
+               void MapClicked(object sender, MapClickedEventArgs e)
+               {
+                       LastMapClickLabel.Text = $"Last MapClick: {e.Position.Latitude}, {e.Position.Longitude}";
+               }
+
+               async void MapTypeClicked(object sender, EventArgs e)
+               {
+                       var result = await DisplayActionSheet("Select Map Type", null, null, "Street", "Satellite", "Hybrid");
+                       switch (result)
+                       {
+                               case "Street":
+                                       Map.MapType = MapType.Street;
+                                       break;
+                               case "Satellite":
+                                       Map.MapType = MapType.Satellite;
+                                       break;
+                               case "Hybrid":
+                                       Map.MapType = MapType.Hybrid;
+                                       break;
+                       }
+               }
+
+               void ZoomInClicked(object sender, EventArgs e)
+               {
+                       Map.MoveToRegion(Map.VisibleRegion.WithZoom(5f));
+               }
+
+               void ZoomOutClicked(object sender, EventArgs e)
+               {
+                       Map.MoveToRegion(Map.VisibleRegion.WithZoom(1.0 / 3));
+               }
+
+               async void ReverseGeocodeClicked(object sender, EventArgs e)
+               {
+                       var addresses = await _geocoder.GetAddressesForPositionAsync(new Position(41.8902, 12.4923));
+                       foreach (var ad in addresses)
+                               Debug.WriteLine(ad);
+               }
+
+               void HomeClicked(object sender, EventArgs e)
+               {
+                       Map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(41.890202, 12.492049), Distance.FromMiles(0.5)));
+               }
+
+               void ZoomPinClicked(object sender, EventArgs e)
+               {
+                       var pos = new Position(41.011995, -8.642995);
+                       Map.Pins.Clear();
+                       Map.Pins.Add(new Pin { Position = pos, Label = "Rui" });
+                       Map.MoveToRegion(MapSpan.FromCenterAndRadius(pos, Distance.FromMiles(0.5)));
+               }
+
+               void EditPinClicked(object sender, EventArgs e)
+               {
+                       var pin = Map.Pins.First();
+
+                       pin.Label += " Edited";
+                       pin.Address = "Edited";
+
+                       var pos = new Position(pin.Position.Latitude + 1, pin.Position.Longitude + 1);
+                       pin.Position = pos;
+                       Map.MoveToRegion(MapSpan.FromCenterAndRadius(pos, Distance.FromMiles(0.5)));
+               }
+
+               void RemovePinClicked(object sender, EventArgs e)
+               {
+                       Map.Pins.RemoveAt(0);
+               }
+       }
+}
\ No newline at end of file
index df98d49..37d164e 100644 (file)
@@ -65,6 +65,9 @@
     <EmbeddedResource Update="GalleryPages\CollectionViewGalleries\DataTemplateSelectorGallery.xaml">
       <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
     </EmbeddedResource>
+    <EmbeddedResource Update="GalleryPages\MapGallery.xaml">
+      <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+    </EmbeddedResource>
     <EmbeddedResource Update="GalleryPages\MapWithItemsSourceGallery.xaml">
       <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
     </EmbeddedResource>
index f6f51aa..d21d89b 100644 (file)
@@ -82,13 +82,14 @@ namespace Xamarin.Forms.Maps.Android
                                }
 
                                if (NativeMap != null)
-                               {
-                                       NativeMap.MyLocationEnabled = false;
-                                       NativeMap.SetOnCameraMoveListener(null);
-                                       NativeMap.InfoWindowClick -= MapOnMarkerClick;
-                                       NativeMap.Dispose();
+                               {
+                                       NativeMap.MyLocationEnabled = false;
+                                       NativeMap.SetOnCameraMoveListener(null);
+                                       NativeMap.InfoWindowClick -= MapOnMarkerClick;
+                                       NativeMap.MapClick -= OnMapClick;
+                                       NativeMap.Dispose();
                                        NativeMap = null;
-                                }
+                               }
 
                                Control?.OnDestroy();
                        }
@@ -123,6 +124,7 @@ namespace Xamarin.Forms.Maps.Android
                                {
                                        NativeMap.SetOnCameraMoveListener(null);
                                        NativeMap.InfoWindowClick -= MapOnMarkerClick;
+                                       NativeMap.MapClick -= OnMapClick;
                                        NativeMap = null;
                                }
 
@@ -203,6 +205,7 @@ namespace Xamarin.Forms.Maps.Android
 
                        map.SetOnCameraMoveListener(this);
                        map.InfoWindowClick += MapOnMarkerClick;
+                       map.MapClick += OnMapClick;
 
                        map.UiSettings.ZoomControlsEnabled = Map.HasZoomEnabled;
                        map.UiSettings.ZoomGesturesEnabled = Map.HasZoomEnabled;
@@ -301,6 +304,11 @@ namespace Xamarin.Forms.Maps.Android
                        targetPin?.SendTap();
                }
 
+               void OnMapClick(object sender, GoogleMap.MapClickEventArgs e)
+               {
+                       Map.SendMapClicked(new Position(e.Point.Latitude, e.Point.Longitude));
+               }
+
                void MoveToRegion(MapSpan span, bool animate)
                {
                        GoogleMap map = NativeMap;
index 9981b4a..54aa95a 100644 (file)
@@ -37,6 +37,7 @@ namespace Xamarin.Forms.Maps.UWP
                                        Control.MapServiceToken = FormsMaps.AuthenticationToken;
                                        Control.ZoomLevelChanged += async (s, a) => await UpdateVisibleRegion();
                                        Control.CenterChanged += async (s, a) => await UpdateVisibleRegion();
+                                       Control.MapTapped += OnMapTapped;
                                }
 
                                MessagingCenter.Subscribe<Map, MapSpan>(this, "MapMoveToRegion", async (s, a) => await MoveToRegion(a), mapModel);
@@ -295,5 +296,10 @@ namespace Xamarin.Forms.Maps.UWP
                {
                        Control.PanInteractionMode = Element.HasScrollEnabled ? MapPanInteractionMode.Auto : MapPanInteractionMode.Disabled;
                }
+
+               void OnMapTapped(MapControl sender, MapInputEventArgs args)
+               {
+                       Element?.SendMapClicked(new Position(args.Location.Position.Latitude, args.Location.Position.Longitude));
+               }
        }
 }
index e874223..7147d64 100644 (file)
@@ -27,6 +27,10 @@ namespace Xamarin.Forms.Maps.MacOS
                object _lastTouchedView;
                bool _disposed;
 
+#if __MOBILE__
+               UITapGestureRecognizer _mapClickedGestureRecognizer;
+#endif
+
                const string MoveMessageName = "MapMoveToRegion";
 
                public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
@@ -76,6 +80,10 @@ namespace Xamarin.Forms.Maps.MacOS
                                }
                                mkMapView.RemoveFromSuperview();
 #if __MOBILE__
+                               mkMapView.RemoveGestureRecognizer(_mapClickedGestureRecognizer);
+                               _mapClickedGestureRecognizer.Dispose();
+                               _mapClickedGestureRecognizer = null;
+
                                if (FormsMaps.IsiOs9OrNewer)
                                {
                                        // This renderer is done with the MKMapView; we can put it in the pool
@@ -138,6 +146,9 @@ namespace Xamarin.Forms.Maps.MacOS
 
                                        mapView.GetViewForAnnotation = GetViewForAnnotation;
                                        mapView.RegionChanged += MkMapViewOnRegionChanged;
+#if __MOBILE__
+                                       mapView.AddGestureRecognizer(_mapClickedGestureRecognizer = new UITapGestureRecognizer(OnMapClicked));
+#endif
                                }
 
                                MessagingCenter.Subscribe<Map, MapSpan>(this, MoveMessageName, (s, a) => MoveToRegion(a), mapModel);
@@ -280,6 +291,20 @@ namespace Xamarin.Forms.Maps.MacOS
                        targetPin.SendTap();
                }
 
+#if __MOBILE__
+               void OnMapClicked(UITapGestureRecognizer recognizer)
+               {
+                       if (Element == null)
+                       {
+                               return;
+                       }
+
+                       var tapPoint = recognizer.LocationInView(Control);
+                       var tapGPS = ((MKMapView)Control).ConvertPoint(tapPoint, Control);
+                       ((Map)Element).SendMapClicked(new Position(tapGPS.Latitude, tapGPS.Longitude));
+               }
+#endif
+
                void UpdateRegion()
                {
                        if (_shouldUpdateRegion)
index e1b8e5f..23c0fd2 100644 (file)
@@ -90,6 +90,11 @@ namespace Xamarin.Forms.Maps
                        get { return (DataTemplateSelector)GetValue(ItemTemplateSelectorProperty); }
                        set { SetValue(ItemTemplateSelectorProperty, value); }
                }
+               
+               public event EventHandler<MapClickedEventArgs> MapClicked;
+               
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               public void SendMapClicked(Position position) => MapClicked?.Invoke(this, new MapClickedEventArgs(position));
 
                [EditorBrowsable(EditorBrowsableState.Never)]
                public void SetVisibleRegion(MapSpan value) => VisibleRegion = value;
diff --git a/Xamarin.Forms.Maps/MapClickedEventArgs.cs b/Xamarin.Forms.Maps/MapClickedEventArgs.cs
new file mode 100644 (file)
index 0000000..b298055
--- /dev/null
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Xamarin.Forms.Maps
+{
+       public class MapClickedEventArgs
+       {
+               public Position Position { get; }
+
+               public MapClickedEventArgs(Position position)
+               {
+                       Position = position;
+               }
+       }
+}