在iOS上遇到自定义地图/渲染器的两个问题。
我有一个自定义地图类,其中的圆圈放在地图上。 滑块控件从可绑定属性调整圆的大小。
当滑块值更改时,圆的半径属性将使用所选值进行更新。但正如您所看到的,它并未在地图上更新半径,而是将圆圈移动到曲线内的新位置。
当圆圈移动到x像素之外时,它会消失或在可见边界外被切除。
这些是正在使用的类:
Page.xaml:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:CompanyName.Data.ViewModels.MapWithCircleSlider;assembly=CompanyName"
xmlns:local="clr-namespace:CompanyName.UI;assembly=CompanyName"
x:Class="CompanyName.UI.Pages.MapWithCircleSlider"
Title="{Binding Title}">
<ContentPage.BindingContext>
<vm:MapWithCircleSliderViewModel></vm:MapWithCircleSliderViewModel>
</ContentPage.BindingContext>
<ContentPage.Content>
<ScrollView>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<local:CircleMap Grid.Row="0" CircleRadius="{Binding CircleRadius}" Latitude="{Binding Latitude}" Longitude="{Binding Longitude}" MapRadius="{Binding MapRadius}" IsShowingUser="true" HasZoomEnabled="true" />
<!--<Image Grid.Row="0" HorizontalOptions="Center" VerticalOptions="Center" Source="ic_place_green_48dp.png" />-->
<StackLayout Grid.Row="1" Padding="32,16">
<Entry VerticalOptions="Start" Placeholder="navn *" Text="{Binding Name}">
<Entry.Style>
<OnPlatform x:TypeArguments="Style">
<On Platform="iOS" Value="{x:Static local:Styling.IosEntryStyle}" />
</OnPlatform>
</Entry.Style>
</Entry>
<Label VerticalOptions="Center" HorizontalOptions="Center" Text="{Binding CircleRadius, StringFormat='{0}m'}" />
<Slider VerticalOptions="End" Maximum="{Binding Maximum}" Minimum="{Binding Minimum}" Value="{Binding CircleRadius}" />
<!-- NB: Maximum must be set before Minimum, ref: https://bugzilla.xamarin.com/show_bug.cgi?id=23665 -->
</StackLayout>
</Grid>
</ScrollView>
</ContentPage.Content>
</ContentPage>
Pages ViewModel:
using System;
using CompanyName.ViewModels;
namespace CompanyName.Data.ViewModels.MapWithCircleSlider
{
public class MapWithCircleSliderViewModel : ViewModelBase
{
private string name;
private int circleRadius;
private float latitude;
private float longitude;
private int mapRadius;
public MapWithCircleSliderViewModel()
{
Name = "Labs";
CircleRadius = 200;
MapRadius = 200;
Latitude = 58.9698634f;
Longitude = 5.7331874f;
}
public int Maximum => 1000;
public int Minimum => 100;
public string Id { get; set; }
public bool IsEditMode { get; set; }
public string Title { get; set; }
public string Name
{
get => name;
set
{
if (name == value) return;
name = value;
OnPropertyChanged("Name");
}
}
public int CircleRadius
{
get => circleRadius;
set
{
if (circleRadius == value) return;
circleRadius = value;
OnPropertyChanged("CircleRadius");
}
}
public float Latitude
{
get => latitude;
set
{
if (Math.Abs(latitude - value) < float.Epsilon) return;
latitude = value;
OnPropertyChanged("Latitude");
}
}
public float Longitude
{
get => longitude;
set
{
if (Math.Abs(longitude - value) < float.Epsilon) return;
longitude = value;
OnPropertyChanged("Longitude");
}
}
public int MapRadius
{
get => mapRadius;
set
{
if (mapRadius == value) return;
mapRadius = value;
OnPropertyChanged("MapRadius");
}
}
}
}
CircleMap.cs
using System.Diagnostics;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
namespace CompanyName.UI
{
public class CircleMap : Map
{
private const int DefaultCircleRadius = 100;
private const float DefaultLatitude = 58.8523208f;
private const float DefaultLongitude = 5.7326743f;
private const int DefaultMapRadius = 150;
public static readonly BindableProperty CircleRadiusProperty = BindableProperty.Create("CircleRadius", typeof(int), typeof(CircleMap), DefaultCircleRadius, BindingMode.TwoWay, propertyChanged: OnCircleRadiusPropertyChanged);
public static readonly BindableProperty LatitudeProperty = BindableProperty.Create("Latitude", typeof(float), typeof(CircleMap), DefaultLatitude, BindingMode.TwoWay, propertyChanged: OnLatitudePropertyChanged);
public static readonly BindableProperty LongitudeProperty = BindableProperty.Create("Longitude", typeof(float), typeof(CircleMap), DefaultLongitude, BindingMode.TwoWay, propertyChanged: OnLongitudePropertyChanged);
public static readonly BindableProperty MapRadiusProperty = BindableProperty.Create("MapRadius", typeof(int), typeof(CircleMap), DefaultMapRadius, BindingMode.TwoWay, propertyChanged: OnMapRadiusPropertyChanged);
public CircleMap() : base(MapSpan.FromCenterAndRadius(new Position(DefaultLatitude, DefaultLongitude), Distance.FromMeters(DefaultMapRadius))) { }
public int CircleRadius
{
get => (int)GetValue(CircleRadiusProperty);
set => SetValue(CircleRadiusProperty, value);
}
public float Latitude
{
get => (float)GetValue(LatitudeProperty);
set => SetValue(LatitudeProperty, value);
}
public float Longitude
{
get => (float)GetValue(LongitudeProperty);
set => SetValue(LongitudeProperty, value);
}
public int MapRadius
{
get => (int)GetValue(MapRadiusProperty);
set => SetValue(MapRadiusProperty, value);
}
private static void OnCircleRadiusPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var circleMap = (CircleMap)bindable;
circleMap.CircleRadius = (int)newValue;
}
private static void OnLatitudePropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var circleMap = (CircleMap)bindable;
circleMap.Latitude = (float)newValue;
MoveToRegion(circleMap);
}
private static void OnLongitudePropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var circleMap = (CircleMap)bindable;
circleMap.Longitude = (float)newValue;
MoveToRegion(circleMap);
}
private static void OnMapRadiusPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var circleMap = (CircleMap)bindable;
circleMap.MapRadius = (int)newValue;
MoveToRegion(circleMap);
}
private static void MoveToRegion(CircleMap circleMap)
{
circleMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(circleMap.Latitude, circleMap.Longitude), Distance.FromMeters(circleMap.MapRadius)));
}
}
}
CustomMapRenderer.cs(iOS):
using CompanyName.UI;
using MapKit;
using ObjCRuntime;
using System;
using System.ComponentModel;
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Maps.iOS;
using Xamarin.Forms.Platform.iOS;
using CompanyName.iOS.Renderers.CustomRenderer;
using CompanyName.Utilities;
[assembly: ExportRenderer(typeof(CircleMap), typeof(CustomMapRenderer))]
namespace CompanyName.iOS.Renderers.CustomRenderer
{
/// <remarks>
/// https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/custom-renderer/map/circle-map-overlay/#Creating_the_Custom_Renderer_on_iOS
/// </remarks>
public class CustomMapRenderer : MapRenderer
{
private CircleMap circleMap;
private MKCircleRenderer circleRenderer;
private MKMapView NativeMap => Control as MKMapView;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
try
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
if (Control is MKMapView nativeMap)
{
nativeMap.RemoveOverlays(nativeMap.Overlays);
nativeMap.OverlayRenderer = null;
circleRenderer = null;
}
}
if (e.NewElement != null)
{
circleMap = (CircleMap)e.NewElement;
NativeMap.OverlayRenderer = GetOverlayRenderer;
AddOverlay();
}
}
catch (Exception ex)
{
//Logger.LogException(ex, GetType().Name);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (sender == null) return;
circleMap = (CircleMap)sender;
if (e.PropertyName == "VisibleRegion") OnVisibleRegionChanged();
if (e.PropertyName == CircleMap.CircleRadiusProperty.PropertyName) RedrawOverlay();
}
private MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper)
{
if (circleRenderer == null && !Equals(overlayWrapper, null))
{
var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as IMKOverlay;
circleRenderer = new MKCircleRenderer(overlay as MKCircle)
{
Alpha = 0.15f,
FillColor = CompanyName.Constants.Colors.Skobeloff500.ToUIColor(),
LineWidth = 1,
StrokeColor = CompanyName.Constants.Colors.Skobeloff500.ToUIColor()
};
}
return circleRenderer;
}
private void OnVisibleRegionChanged()
{
SetNewCoordinates();
RedrawOverlay();
}
private void SetNewCoordinates()
{
circleMap.Latitude = (float)circleMap.VisibleRegion.Center.Latitude;
circleMap.Longitude = (float)circleMap.VisibleRegion.Center.Longitude;
circleMap.MapRadius = (int)circleMap.VisibleRegion.Radius.Meters;
}
private void RedrawOverlay()
{
RemoveOverlays();
AddOverlay();
}
private void RemoveOverlays()
{
if (NativeMap?.Overlays == null) return;
if (NativeMap.Overlays.Any()) NativeMap.RemoveOverlays(NativeMap.Overlays);
}
private void AddOverlay()
{
var circleOverlay = MKCircle.Circle(new CoreLocation.CLLocationCoordinate2D(circleMap.Latitude, circleMap.Longitude), circleMap.CircleRadius);
NativeMap.AddOverlay(circleOverlay);
}
}
}
非常感谢任何反馈/建议!
答案 0 :(得分:1)
您可以尝试刷新circleRenderer
以达到以下效果:
private void RemoveOverlays()
{
if (NativeMap?.Overlays == null) return;
if (NativeMap.Overlays.Any())
{
NativeMap.RemoveOverlays(NativeMap.Overlays);
circleRenderer = null;
NativeMap.OverlayRenderer = GetOverlayRenderer;
}
}