Xamarin Forms Maps - location button is not visible

Viewed 313

Im using Xamarin Forms Maps and I implemented location button: Map.IsShowingUser = true; Also I added Permissions (Xamarin Essentials nuget) and implemented like this in my view model:

public class MapViewModel : BaseViewModel
{
    public MapViewModel(CustomMap map)
    {
        Map = map;
        GetPermissions();

        Task.Run(async () => { await GetImage(); });

    }

    public void SetPins()
    {
        if (App.FilterPins != null && App.FilterPins.Count > 0)
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                Map.CustomPins.Clear();
                Map.CustomPins.AddRange(App.FilterPins);

                foreach (var item in App.FilterPins)
                {
                    Map.Pins.Add(item);
                    App.SelectedPin = item;
                    item.InfoWindowClicked += Pin_InfoWindowClicked;

                }

                Map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(App.SelectedPin.Position.Latitude, App.SelectedPin.Position.Longitude), Distance.FromMiles(1.0)));

                OnPropertyChanged(nameof(Map.CustomPins));
                OnPropertyChanged(nameof(Map));

            });
        }
        else
        {
            GetMapDefaultData();
        }
    }

    public void SetMapToSelectedPin()
    {
        Device.BeginInvokeOnMainThread(() =>
        {
            if (App.SelectedPin != null)
            {
                //gledam da li je u listi pinova App.FilterPins ili u DefaultPins (iz default servisa)
                if (Map.CustomPins.Any(x => x.Id == App.SelectedPin.Id))
                {
                    //focusOnPin
                    Map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(App.SelectedPin.Position.Latitude, App.SelectedPin.Position.Longitude), Distance.FromMiles(1.0)));
                }
                else
                {
                    //add pin to list -> Map.Pins.Add(pin);
                    //focusonpin
                    if (App.FilterPins != null && App.FilterPins.Count > 0)
                    {
                        Map.CustomPins.Add(App.SelectedPin);
                        Map.Pins.Add(App.SelectedPin);

                        Map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(App.SelectedPin.Position.Latitude, App.SelectedPin.Position.Longitude), Distance.FromMiles(1.0)));
                    }

                }
            }
            else
            {
                Map.MoveToRegion(MapSpan.FromCenterAndRadius(Constants.BelgradeCoordinates, Distance.FromMiles(1.0)));
            }
            OnPropertyChanged(nameof(Map));
        });

    }
    private async Task GetImage()
    {
        App.PinImages = new List<PinImageResponseModel>();

        var result = await ApiServiceProvider.GetPinImages();

        Device.BeginInvokeOnMainThread(() =>
        {
            App.PinImages = result;
        });
    }

    public CustomPin _pin { get; set; }

    public CustomPin Pin
    {
        get { return _pin; }
        set { _pin = value; OnPropertyChanged(); }
    }

    public ICommand PositionMapOnPinCommand { get; set; }
    private async void GetMapDefaultData()
    {
        try
        {

            DefaultRegionsAndClassesResponseModel regionsAndPinsResponse = new DefaultRegionsAndClassesResponseModel();

            #region Regions
            var regions = await ApiServiceProvider.GetRegionsQuery(null, 0, 5000);

            //if (regions != null && regions.Data != null)
            //{
            //    var polygon = new Polygon();
            //    foreach (var region in regions.Data.Where(x => x.Checked).ToList())
            //    {
            //        polygon.StrokeWidth = 8;
            //        polygon.StrokeColor = Color.Green;
            //        polygon.FillColor = Color.FromRgba(255, 0, 0, 64);


            //        foreach (var point in region.Points)
            //        {
            //            polygon.Geopath.Add(new Position(point.Latitude, point.Longitude));
            //        }

            //        Map.MapElements.Add(polygon);
            //    }
            //}
            #endregion

            #region Pins & Infoboxes

            regionsAndPinsResponse = await ApiServiceProvider.GetDefaultRegionsAndClasses(); 
            
            Device.BeginInvokeOnMainThread(() =>
            {
                if (regionsAndPinsResponse.Succeeded && regionsAndPinsResponse.Data != null && regionsAndPinsResponse.Data.Count > 0)
                {
                    foreach (var mainItem in regionsAndPinsResponse.Data)
                    {
                        CustomPin pin = new CustomPin();

                        if (mainItem != null && mainItem.Points.Count > 0)
                        {
                            pin.Id = mainItem.IdObjectInstance;
                            pin.ClassId = mainItem.ObjectClassIdObjectClass;
                            pin.Label = mainItem.ObjectClassName;

                            pin.IsFavorite = mainItem.IsFavorite.HasValue;

                            foreach (var item in App.PinImages)
                            {
                                if (mainItem.Status != null && mainItem.Status.ClassStatus.ClassStatusIcon != null && item.Name.Equals(mainItem.Status.ClassStatus.ClassStatusIcon))
                                {
                                    string base64 = item.Base64String.Substring(item.Base64String.IndexOf(',') + 1);
                                    base64 = base64.Trim('\0');

                                    pin.InfoBox.ImageString = base64;

                                    if (pin.InfoBox.ImageString != null)
                                    {
                                        var source = Convert.FromBase64String(pin.InfoBox.ImageString);
                                        pin.InfoBox.PinImageSource = ImageSource.FromStream(() => new MemoryStream(source));
                                    }
                                }
                            }

                            if (mainItem.Points != null)
                            {
                                pin.Position = new Position(mainItem.Points.FirstOrDefault().Latitude, mainItem.Points.FirstOrDefault().Longitude);
                            }
                            else
                            {
                                //add polygon
                            }

                            foreach (var item in mainItem.Strings)
                            {
                                pin.InfoBox.DetailsObjectInfos.Add(new Models.MapModels.DetailsObjectInfo { BoldLabelTitle = item.ClassParameterName + ": ", LabelValue = item.StringValue });
                            }
                            foreach (var item in mainItem.Integers)
                            {
                                pin.InfoBox.DetailsObjectInfos.Add(new Models.MapModels.DetailsObjectInfo { BoldLabelTitle = item.ClassParameterName + ": ", LabelValue = item.IntValue.ToString() });

                            }
                        }

                        pin.InfoWindowClicked += Pin_InfoWindowClicked;

                        Map.CustomPins.Add(pin);
                        Map.Pins.Add(pin);
                    }
                }
            });
            #endregion
        }
        catch (Exception ex)
        {
            await Shell.Current.DisplayAlert("Error", "Something went wrong with maps", "OK");
        }
    }

    private void Pin_InfoWindowClicked(object sender, PinClickedEventArgs e)
    {
        Device.BeginInvokeOnMainThread(async () =>
        {
            var pin = sender as CustomPin;
            App.SelectedPin = pin;
            await Shell.Current.Navigation.PushAsync(new ObjectParametersPage());
        });

    }
    private async void GetPermissions()
    {
        var status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();
        if (status != PermissionStatus.Granted)
        {
            status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
        }
        if (status == PermissionStatus.Granted)
        {
            Map.IsShowingUser = true;
        }
        else
        {
            await Shell.Current.DisplayAlert("Permission Denied", "We Need to access your Location. But it is not granted", "OK");
        }
    }
    public CustomMap Map { get; set; }

    public ICommand SetCameraToChosenPinCommand { get; set; }
}

My XAML:

</ContentPage>
    <ContentPage.Content>
        <renderers:CustomMap x:Name="map" IsShowingUser="True"/>
    </ContentPage.Content>
</ContentPage>

CodeBehind:

public partial class MapPage : ContentPage
{
    MapViewModel mapViewModel;

    public MapPage()
    {
        InitializeComponent();
        BindingContext = mapViewModel = new MapViewModel(map);
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();
        mapViewModel.SetPins();
        mapViewModel.SetMapToSelectedPin();
    }
}

my CustomMap:

public class CustomMap : Map
    {
        //public List<CustomPin> CustomPins { get; set; }

        public CustomMap()
        {
            CustomPins = new ObservableCollection<CustomPin>();
            CustomPins.CollectionChanged += PinsSourceOnCollectionChanged;
        }

        public ObservableCollection<CustomPin> CustomPins
        {
            get { return (ObservableCollection<CustomPin>)GetValue(PinsSourceProperty); }
            set { SetValue(PinsSourceProperty, value); OnPropertyChanged(nameof(CustomPins)); }
        }

        public static readonly BindableProperty PinsSourceProperty = BindableProperty.Create(
                                                         propertyName: "PinsSource",
                                                         returnType: typeof(ObservableCollection<CustomPin>),
                                                         declaringType: typeof(CustomMap),
                                                         defaultValue: null,
                                                         defaultBindingMode: BindingMode.TwoWay,
                                                         validateValue: null,
                                                         propertyChanged: PinsSourcePropertyChanged);


        public MapSpan MapSpan
        {
            get { return (MapSpan)GetValue(MapSpanProperty); }
            set { SetValue(MapSpanProperty, value); }
        }

        public static readonly BindableProperty MapSpanProperty = BindableProperty.Create(
                                                         propertyName: "MapSpan",
                                                         returnType: typeof(MapSpan),
                                                         declaringType: typeof(CustomMap),
                                                         defaultValue: null,
                                                         defaultBindingMode: BindingMode.TwoWay,
                                                         validateValue: null,
                                                         propertyChanged: MapSpanPropertyChanged);

        private static void MapSpanPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var thisInstance = bindable as CustomMap;
            var newMapSpan = newValue as MapSpan;

            thisInstance?.MoveToRegion(newMapSpan);
        }
        private static void PinsSourcePropertyChanged(BindableObject bindable, object oldvalue, object newValue)
        {
            var thisInstance = bindable as CustomMap;
            var newPinsSource = newValue as ObservableCollection<CustomPin>;

            if (thisInstance == null ||
                newPinsSource == null)
                return;

            UpdatePinsSource(thisInstance, newPinsSource);
        }
        private void PinsSourceOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            UpdatePinsSource(this, sender as IEnumerable<CustomPin>);
        }

        private static void UpdatePinsSource(CustomMap bindableMap, IEnumerable<CustomPin> newSource)
        {
            bindableMap.IsShowingUser = true;
            bindableMap.Pins.Clear();
            foreach (var pin in newSource)
                bindableMap.Pins.Add(pin);
        }
    }

So, I have a bug only on first time installing app on my phone - it doesnt show location button, because its async method. Each next tiem when I open the Map page there is a button.

So, my question would be: how to properly integrate permissions and map, in order to present location button on the first startup of application?

0 Answers
Related