Reputation: 1901
I have this custom renderer for iOS and Im uisng xamarin forms maps:
public class CustomMapRenderer : MapRenderer
{
UIView customPinView;
ObservableCollection<CustomPin> customPins;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
var nativeMap = Control as MKMapView;
nativeMap.GetViewForAnnotation = null;
nativeMap.CalloutAccessoryControlTapped -= OnCalloutAccessoryControlTapped;
nativeMap.DidSelectAnnotationView -= OnDidSelectAnnotationView;
nativeMap.DidDeselectAnnotationView -= OnDidDeselectAnnotationView;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
var nativeMap = Control as MKMapView;
customPins = formsMap.CustomPins;
nativeMap.GetViewForAnnotation = GetViewForAnnotation;
nativeMap.CalloutAccessoryControlTapped += OnCalloutAccessoryControlTapped;
nativeMap.DidSelectAnnotationView += OnDidSelectAnnotationView;
nativeMap.DidDeselectAnnotationView += OnDidDeselectAnnotationView;
}
}
protected override MKAnnotationView GetViewForAnnotation(MKMapView mapView, IMKAnnotation annotation)
{
MKAnnotationView annotationView = null;
if (annotation is MKUserLocation)
return null;
var customPin = GetCustomPin(annotation as MKPointAnnotation);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
annotationView = mapView.DequeueReusableAnnotation(customPin.Label);
if (annotationView == null)
{
annotationView = new CustomMKAnnotationView(annotation, customPin.Label);
annotationView.Image = UIImage.FromFile("pin.png");
annotationView.CalloutOffset = new CGPoint(0, 0);
annotationView.LeftCalloutAccessoryView = new UIImageView(UIImage.FromFile("monkey.png"));
annotationView.RightCalloutAccessoryView = UIButton.FromType(UIButtonType.DetailDisclosure);
((CustomMKAnnotationView)annotationView).Name = customPin.Label;
}
annotationView.CanShowCallout = true;
return annotationView;
}
void OnCalloutAccessoryControlTapped(object sender, MKMapViewAccessoryTappedEventArgs e)
{
CustomMKAnnotationView customView = e.View as CustomMKAnnotationView;
if (!string.IsNullOrWhiteSpace(customView.Url))
{
UIApplication.SharedApplication.OpenUrl(new Foundation.NSUrl(customView.Url));
}
}
void OnDidSelectAnnotationView(object sender, MKAnnotationViewEventArgs e)
{
CustomMKAnnotationView customView = e.View as CustomMKAnnotationView;
customPinView = new UIView();
var label = new UILabel();
label.Text = "Tutorial";
label.Font.WithSize(36);
customPinView.AddSubview(label);
customPinView.Frame = new CGRect(0, 0, 200, 84);
var image = new UIImageView(new CGRect(0, 0, 200, 84));
image.Image = UIImage.FromFile("xamarin.png");
customPinView.AddSubview(image);
customPinView.Center = new CGPoint(0, -(e.View.Frame.Height + 75));
e.View.AddSubview(customPinView);
}
void OnDidDeselectAnnotationView(object sender, MKAnnotationViewEventArgs e)
{
if (!e.View.Selected)
{
customPinView.RemoveFromSuperview();
customPinView.Dispose();
customPinView = null;
}
}
CustomPin GetCustomPin(MKPointAnnotation annotation)
{
var position = new Position(annotation.Coordinate.Latitude, annotation.Coordinate.Longitude);
foreach (var pin in customPins)
{
if (pin.Position == position)
{
return pin;
}
}
return null;
}
}
And I want to add some more things below, and I dont know why it doesnt resize this pin window?
Here is the outcome:
UPDATE: Here is Android renderer which works fine. From my CUstomPinModel, I need dynamically create labels:
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter
{
private LatLngBounds.Builder _builder;
private GoogleMap _map;
ObservableCollection<CustomPin> customPins { get; set; }
Context context;
public CustomMapRenderer(Context context) : base(context)
{
this.context = context;
customPins = new ObservableCollection<CustomPin>();
_builder = new LatLngBounds.Builder();
}
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
NativeMap.InfoWindowClick -= OnInfoWindowClick;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
}
}
protected override void OnMapReady(GoogleMap map)
{
base.OnMapReady(map);
NativeMap.InfoWindowClick += OnInfoWindowClick;
NativeMap.SetInfoWindowAdapter(this);
if (_map == null)
{
_map = map;
}
}
protected override MarkerOptions CreateMarker(Pin pin)
{
CustomPin customPin = (CustomPin)pin;
var marker = new MarkerOptions();
LatLng position = new LatLng(pin.Position.Latitude, pin.Position.Longitude);
marker.SetPosition(new LatLng(pin.Position.Latitude, pin.Position.Longitude));
marker.SetTitle(pin.Label);
marker.SetSnippet(pin.Address);
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
_builder.Include(position);
LatLngBounds bounds = _builder.Build();
CameraUpdate cu = CameraUpdateFactory.NewLatLngBounds(bounds, 20);
_map.MoveCamera(cu);
return marker;
}
void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var customPin = GetCustomPin(e.Marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
}
public Android.Views.View GetInfoContents(Marker marker)
{
var inflater = Android.App.Application.Context.GetSystemService(Context.LayoutInflaterService) as Android.Views.LayoutInflater;
if (inflater != null)
{
Android.Views.View view;
var customPin = GetCustomPin(marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
LinearLayout linearLayout = (LinearLayout)view.FindViewById(Resource.Id.InfoWindowProps);
if(customPin != null && customPin.InfoBox != null && customPin.InfoBox.DetailsObjectInfos.Count > 0)
{
for (int i = 0; i < customPin.InfoBox.DetailsObjectInfos.Count; i++)
{
TextView t1 = new TextView(context);
t1.Text = customPin.InfoBox.DetailsObjectInfos[i].BoldLabelTitle + customPin.InfoBox.DetailsObjectInfos[i].LabelValue;
linearLayout.AddView(t1);
}
}
return view;
}
return null;
}
public Android.Views.View GetInfoWindow(Marker marker)
{
return null;
}
CustomPin GetCustomPin(Marker annotation)
{
var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
foreach (var pin in customPins)
{
if (pin.Position == position)
{
return pin;
}
}
return null;
}
}
Also, I set in my layout correct naming in order to be able to find resource by id:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/InfoWindowImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:src="@drawable/monkey" />
<LinearLayout
android:id="@+id/InfoWindowProps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="10dp"
android:layout_marginRight="261.5dp" >
</LinearLayout>
</LinearLayout>
UPDATE #2: My model for CustomPin:
public class CustomPin : Pin, INotifyPropertyChanged
{
public string PinIcon { get; set; }
public Color Color { get; set; }
public InfoBoxMapModel InfoBox { get; set; } = new InfoBoxMapModel();
public int? Id { get; set; }
public int? ClassId { get; set; }
public string FavoriteLabel { get; set; }
public bool? _isFavorite { get; set; }
public bool? IsFavorite
{
get { return _isFavorite; }
set { _isFavorite = value; OnPropertyChanged(); }
}
}
and InfoBoxModel which is containing my string labels:
public class InfoBoxMapModel
{
//base64
public string ImageString { get; set; }
public ImageSource PinImageSource { get; set; }
public List<DetailsObjectInfo> DetailsObjectInfos { get; set; } = new List<DetailsObjectInfo>();
public int CountDetailsItemsRows { get; set; }
}
public class DetailsObjectInfo
{
public string BoldLabelTitle { get; set; }
public string LabelValue { get; set; }
}
UPDATE #3: Here is the example how it works in android:
All these rows of strings are concatenation of DetailsObjectInfo =>
BoldLabelTitle + ": " + LabelValue
So, I want to render DetailsObjectyInfos List in white callout pin cloud.
Upvotes: 0
Views: 302
Reputation: 12723
If need to add subviews dynamically , the one way that you need to calculate the Frame
of each Control (Label
).Such as follow:
for (int i = 0; i < 3; i++)
{
var label = new UILabel();
label.Frame = new CGRect(50*i, 0, 45, 84);
label.Text = "Tutorial:"+i;
label.Font.WithSize(36);
customPinView.AddSubview(label);
}
The another way is using UIStackView as RootView as follow:
void OnDidSelectAnnotationView(object sender, MKAnnotationViewEventArgs e)
{
CustomMKAnnotationView customView = e.View as CustomMKAnnotationView;
//customPinView = new UIView();
UIStackView customPinView = new UIStackView();
if (customView.Name.Equals("Xamarin"))
{
//var label = new UILabel(new CGRect(-50, 0, 50, 84));
for (int i = 0; i < 3; i++)
{
var label = new UILabel();
label.Text = "Tutorial:"+i;
label.Font.WithSize(36);
customPinView.AddArrangedSubview(label);
}
customPinView.Frame = new CGRect(0, 0, 300, 84);
customPinView.Axis = UILayoutConstraintAxis.Horizontal;
customPinView.Distribution = UIStackViewDistribution.EqualSpacing;
customPinView.Spacing = 10;
customPinView.Alignment = UIStackViewAlignment.Fill;
customPinView.Center = new CGPoint(0, -(e.View.Frame.Height + 75));
//customPinView.BackgroundColor = UIColor.Yellow;
e.View.AddSubview(customPinView);
}
}
The effect:
=============================Update #1===============================
MainPage declare the Lable
text of Pin
:
public partial class MapPage : ContentPage
{
public MapPage()
{
InitializeComponent();
CustomPin pin = new CustomPin
{
Type = PinType.Place,
Position = new Position(37.79752, -122.40183),
Label = "Xamarin San Francisco Office",
Address = "394 Pacific Ave, San Francisco CA",
Name = "Xamarin",
Url = "http://xamarin.com/about/"
};
customMap.CustomPins = new List<CustomPin> { pin };
customMap.Pins.Add(pin);
customMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(37.79752, -122.40183), Distance.FromMiles(1.0)));
}
}
=============================Update #2#3===============================
Your idea is right . I have found the solution that replace the adress string wtih my custom Label
s .
There is a DetailCalloutAccessoryView
of MKAnnotationView
, if we can use custome View for it , it will show the wants.
Modify it inside the GetViewForAnnotation
method :
protected override MKAnnotationView GetViewForAnnotation(MKMapView mapView, IMKAnnotation annotation)
{
MKAnnotationView annotationView = null;
if (annotation is MKUserLocation)
return null;
var customPin = GetCustomPin(annotation as MKPointAnnotation);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
annotationView = mapView.DequeueReusableAnnotation(customPin.Name);
if (annotationView == null)
{
annotationView = new CustomMKAnnotationView(annotation, customPin.Name);
annotationView.Image = UIImage.FromFile("pin.png");
annotationView.CalloutOffset = new CGPoint(0, 0);
//annotationView.LeftCalloutAccessoryView = new UIImageView(UIImage.FromFile("monkey.png"));
UIImageView uIImageView = new UIImageView(UIImage.FromFile("monkey.png"));
uIImageView.Frame = new CGRect(0, 0, 75, 100);
annotationView.LeftCalloutAccessoryView = uIImageView;
annotationView.RightCalloutAccessoryView = UIButton.FromType(UIButtonType.DetailDisclosure);
((CustomMKAnnotationView)annotationView).Name = customPin.Name;
((CustomMKAnnotationView)annotationView).Url = customPin.Url;
customPinView = new UIStackView();
for (int i = 0; i < 3; i++)
{
var label = new UILabel();
label.Text = "Tutorial: " + i;
label.BackgroundColor = UIColor.White;
label.Font.WithSize(36);
customPinView.AddArrangedSubview(label);
}
customPinView.Frame = new CGRect(0, 0, 300, 84);
customPinView.Axis = UILayoutConstraintAxis.Vertical;
customPinView.Distribution = UIStackViewDistribution.EqualSpacing;
customPinView.Spacing = 1;
customPinView.Alignment = UIStackViewAlignment.Fill;
annotationView.DetailCalloutAccessoryView = customPinView;
}
annotationView.CanShowCallout = true;
return annotationView;
}
This is the full renderer code :
public class CustomMapRenderer : MapRenderer
{
UIStackView customPinView;
List<CustomPin> customPins;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
var nativeMap = Control as MKMapView;
nativeMap.GetViewForAnnotation = null;
nativeMap.CalloutAccessoryControlTapped -= OnCalloutAccessoryControlTapped;
nativeMap.DidSelectAnnotationView -= OnDidSelectAnnotationView;
nativeMap.DidDeselectAnnotationView -= OnDidDeselectAnnotationView;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
var nativeMap = Control as MKMapView;
customPins = formsMap.CustomPins;
nativeMap.GetViewForAnnotation = GetViewForAnnotation;
nativeMap.CalloutAccessoryControlTapped += OnCalloutAccessoryControlTapped;
nativeMap.DidSelectAnnotationView += OnDidSelectAnnotationView;
nativeMap.DidDeselectAnnotationView += OnDidDeselectAnnotationView;
}
}
protected override MKAnnotationView GetViewForAnnotation(MKMapView mapView, IMKAnnotation annotation)
{
MKAnnotationView annotationView = null;
if (annotation is MKUserLocation)
return null;
var customPin = GetCustomPin(annotation as MKPointAnnotation);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
annotationView = mapView.DequeueReusableAnnotation(customPin.Name);
if (annotationView == null)
{
annotationView = new CustomMKAnnotationView(annotation, customPin.Name);
annotationView.Image = UIImage.FromFile("pin.png");
annotationView.CalloutOffset = new CGPoint(0, 0);
//annotationView.LeftCalloutAccessoryView = new UIImageView(UIImage.FromFile("monkey.png"));
UIImageView uIImageView = new UIImageView(UIImage.FromFile("monkey.png"));
uIImageView.Frame = new CGRect(0, 0, 75, 100);
annotationView.LeftCalloutAccessoryView = uIImageView;
annotationView.RightCalloutAccessoryView = UIButton.FromType(UIButtonType.DetailDisclosure);
((CustomMKAnnotationView)annotationView).Name = customPin.Name;
((CustomMKAnnotationView)annotationView).Url = customPin.Url;
customPinView = new UIStackView();
for (int i = 0; i < 3; i++)
{
var label = new UILabel();
label.Text = "Tutorial: " + i;
label.BackgroundColor = UIColor.White;
label.Font.WithSize(36);
customPinView.AddArrangedSubview(label);
}
customPinView.Frame = new CGRect(0, 0, 300, 84);
customPinView.Axis = UILayoutConstraintAxis.Vertical;
customPinView.Distribution = UIStackViewDistribution.EqualSpacing;
customPinView.Spacing = 1;
customPinView.Alignment = UIStackViewAlignment.Fill;
annotationView.DetailCalloutAccessoryView = customPinView;
}
annotationView.CanShowCallout = true;
return annotationView;
}
void OnCalloutAccessoryControlTapped(object sender, MKMapViewAccessoryTappedEventArgs e)
{
CustomMKAnnotationView customView = e.View as CustomMKAnnotationView;
if (!string.IsNullOrWhiteSpace(customView.Url))
{
UIApplication.SharedApplication.OpenUrl(new Foundation.NSUrl(customView.Url));
}
}
void OnDidSelectAnnotationView(object sender, MKAnnotationViewEventArgs e)
{
CustomMKAnnotationView customView = e.View as CustomMKAnnotationView;
//customPinView = new UIView();
if (customView.Name.Equals("Xamarin"))
{
//customPinView.Frame = new CGRect(0, 0, 200, 84);
//var image = new UIImageView(new CGRect(0, 0, 200, 84));
//image.Image = UIImage.FromFile("xamarin.png");
//customPinView.AddSubview(image);
//customPinView.Center = new CGPoint(0, -(e.View.Frame.Height + 75));
//e.View.AddSubview(customPinView);
}
}
void OnDidDeselectAnnotationView(object sender, MKAnnotationViewEventArgs e)
{
if (!e.View.Selected)
{
customPinView.RemoveFromSuperview();
customPinView.Dispose();
customPinView = null;
}
}
CustomPin GetCustomPin(MKPointAnnotation annotation)
{
var position = new Position(annotation.Coordinate.Latitude, annotation.Coordinate.Longitude);
foreach (var pin in customPins)
{
if (pin.Position == position)
{
return pin;
}
}
return null;
}
}
The effect:
Upvotes: 1