AVCapture session freezes/stuck when locking/unlocking the iPhone - ios

I'm implementing scanner functionality in my xamarin.forms app and for that I'm using iOS native AVCaptureSession. But my problem is while scanning or while capture session is active and the device is being locked, then after unlocking the device freezes the capture session which is quite odd.
I've tried handling it using UIApplication.DidEnterBackgroundNotification|UIApplication.WillEnterForegroundNotification where I stops and starts the capture session again. But the freeze is still occurring.
using System;
using AVFoundation;
using CoreFoundation;
using CoreGraphics;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
namespace MarginPointApp.iOS
{
public class ScannerViewController : UIViewController
{
AVCaptureDevice captureDevice;
AVCaptureVideoPreviewLayer videoPreviewLayer;
AVCaptureSession captureSession;
UIView viewfinderView;
MetadataObjectsDelegate metadataObjectsDelegate;
public event EventHandler<String> OnScanSuccess;
UIView redLineCenter;
UIView overlayView;
UIView overlay;
UIView bottomBarView;
UIButton flashLightButton;
UIButton cancelButton;
UIButton cameraButton;
UILabel bottomTextLabel;
bool isCameraDismissed;
NSObject interuptStartNoti, interuptEndNoti;
AVMetadataObjectType metaTypes = AVMetadataObjectType.Code128Code |
AVMetadataObjectType.Code39Code | AVMetadataObjectType.Code39Mod43Code |
AVMetadataObjectType.DataMatrixCode | AVMetadataObjectType.EAN13Code | AVMetadataObjectType.EAN8Code |
AVMetadataObjectType.Interleaved2of5Code | AVMetadataObjectType.PDF417Code |
AVMetadataObjectType.QRCode | AVMetadataObjectType.UPCECode;
public override void ViewDidLoad()
{
base.ViewDidLoad();
UIView statusBar = UIApplication.SharedApplication.ValueForKey(new NSString("statusBar")) as UIView;
if (statusBar.RespondsToSelector(new ObjCRuntime.Selector("setBackgroundColor:")))
{
statusBar.BackgroundColor = UIColor.Black;
}
NavigationItem.Title = "Scanner";
this.View.BackgroundColor = UIColor.White;
if (marginpoint.im.iOS.AppDelegate.camPosition)
{
captureDevice = GetCameraDevice(AVCaptureDevicePosition.Front);
}
else
{
captureDevice = GetCameraDevice(AVCaptureDevicePosition.Back);
}
CameraSetup();
}
public void CameraSetup()
{
NSError error = null;
if (captureDevice != null)
{
try
{
var input = new AVCaptureDeviceInput(captureDevice, out error);
captureSession = new AVCaptureSession();
if (captureSession == null) { return; }
if (captureSession.CanAddInput(input))
captureSession.AddInput(input);
var captureMetadataOutput = new AVCaptureMetadataOutput();
if (captureSession.CanAddOutput(captureMetadataOutput))
{
captureSession.AddOutput(captureMetadataOutput);
// captureMetadataOutput.MetadataObjectTypes = captureMetadataOutput.AvailableMetadataObjectTypes;
captureMetadataOutput.MetadataObjectTypes = metaTypes;
}
var metadataQueue = new DispatchQueue("com.AVCam.metadata");
metadataObjectsDelegate = new MetadataObjectsDelegate
{
DidOutputMetadataObjectsAction = DidOutputMetadataObjects
};
captureMetadataOutput.SetDelegate(metadataObjectsDelegate, metadataQueue);
videoPreviewLayer = new AVCaptureVideoPreviewLayer(session: captureSession);
videoPreviewLayer.VideoGravity = AVLayerVideoGravity.ResizeAspectFill;
videoPreviewLayer.Frame = View.Layer.Bounds;
View.Layer.AddSublayer(videoPreviewLayer);
}
catch (Exception e)
{
//Console.WriteLine("error device input" + e.ToString());
}
}
// Prepare device for configuration
captureDevice.LockForConfiguration(out error);
if (error != null)
{
// There has been an issue, abort
//Console.WriteLine("Error: {0}", error.LocalizedDescription);
captureDevice.UnlockForConfiguration();
return;
}
addOverlayOnScreen();
/*
string reason = string.Empty;
if (interuptStartNoti == null)
{
interuptStartNoti = AVCaptureSession.Notifications.ObserveWasInterrupted((sender, e) =>
{
reason = e.Notification.UserInfo?.ValueForKey(new NSString("AVCaptureSessionInterruptionReasonKey"))?.ToString();
if (captureSession != null && !reason.Equals(string.Empty) && !reason.Equals("3"))
{
captureSession.StopRunning();
//captureSession.Dispose();
//captureSession = null;
}
});
}
if (interuptEndNoti == null)
{
interuptEndNoti = AVCaptureSession.Notifications.ObserveInterruptionEnded((sender, e) =>
{
Device.BeginInvokeOnMainThread(() =>
{
if (marginpoint.im.iOS.AppDelegate.camPosition)
{
captureDevice = GetCameraDevice(AVCaptureDevicePosition.Front);
}
else
{
captureDevice = GetCameraDevice(AVCaptureDevicePosition.Back);
}
if (!reason.Equals(string.Empty) && !reason.Equals("3"))
{
//CameraSetup();
captureSession.StartRunning();
}
});
});
}
*/
}
NSObject didEnterBackgroundNoti, willEnterForegroundNoti;
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (didEnterBackgroundNoti == null)
{
didEnterBackgroundNoti = NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.DidEnterBackgroundNotification, (obj) =>
{
Device.BeginInvokeOnMainThread(() =>
{
AddBlurEffect();
captureSession?.StopRunning();
});
});
}
willEnterForegroundNoti = NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.WillEnterForegroundNotification, (obj) =>
{
Device.BeginInvokeOnMainThread(() =>
{
RemoveBlurEffect();
CameraSetup();
});
});
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
NSNotificationCenter.DefaultCenter.RemoveObserver(didEnterBackgroundNoti);
NSNotificationCenter.DefaultCenter.RemoveObserver(willEnterForegroundNoti);
}
private AVCaptureDevice GetCameraDevice(AVCaptureDevicePosition position)
{
AVCaptureDevice captureDevice = null;
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
captureDevice = AVCaptureDevice.GetDefaultDevice(AVCaptureDeviceType.BuiltInWideAngleCamera, AVMediaType.Video, position);//AVCaptureDevice.GetDefaultDevice(AVMediaTypes.Video);
}
else
{
var devices = AVCaptureDevice.DevicesWithMediaType(AVMediaType.Video);
foreach (var device in devices)
{
if (device.Position == position)
{
captureDevice = device;
}
}
}
return captureDevice;
}
UIVisualEffectView blurView;
/// <summary>
/// Adds the blur effect to camera preview.
/// </summary>
void AddBlurEffect()
{
if (blurView == null)
{
var blur = UIBlurEffect.FromStyle(UIBlurEffectStyle.Light);
blurView = new UIVisualEffectView(blur);
blurView.Frame = View.Frame;
blurView.AutoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth;
View.AddSubview(blurView);
}
}
void RemoveBlurEffect()
{
if (blurView != null)
{
blurView.RemoveFromSuperview();
blurView = null;
}
}
void addOverlayOnScreen()
{
overlayView = new UIView();
overlayView.Frame = new CGRect(x: 0, y: 0, width: View.Frame.Width, height: View.Frame.Height);
View.AddSubview(overlayView);
View.BringSubviewToFront(overlayView);
var overlayWidth = Application.Current.MainPage.Width * 0.7;
overlay = new UIView();
overlay.Layer.BorderColor = UIColor.Green.CGColor;
overlay.Layer.BorderWidth = 4;
overlay.Frame = new CGRect(x: View.Center.X - overlayWidth / 2, y: View.Center.Y - overlayWidth / 2, width: overlayWidth, height: overlayWidth);
overlayView.AddSubview(overlay);
overlayView.BringSubviewToFront(overlay);
redLineCenter = new UIView();
redLineCenter.BackgroundColor = UIColor.Red;
redLineCenter.Alpha = 0.5f;
redLineCenter.Frame = new CGRect(x: overlay.Frame.X + 4, y: overlay.Center.Y - 2, width: overlay.Frame.Width - 9, height: 4);
overlayView.AddSubview(redLineCenter);
overlayView.BringSubviewToFront(redLineCenter);
// to find Qr code
viewfinderView = new UIView();
viewfinderView.Frame = new CGRect(x: overlay.Frame.X, y: overlay.Center.Y - 50, width: overlay.Frame.Width, height: 100);
overlayView.AddSubview(viewfinderView);
overlayView.BringSubviewToFront(viewfinderView);
bottomBarView = new UIView();
bottomBarView.BackgroundColor = UIColor.White;
bottomBarView.Frame = new CGRect(x: 0, y: View.Frame.Height - 50, width: View.Frame.Width, height: 50);
overlayView.AddSubview(bottomBarView);
overlayView.BringSubviewToFront(bottomBarView);
var centerPoint = (bottomBarView.Frame.Top - overlay.Frame.Bottom) / 2 - 15;
bottomTextLabel = new UILabel
{
Frame = new CGRect(x: View.Frame.X, y: overlay.Frame.Bottom + centerPoint, width: View.Frame.Width, height: 30),
Text = AppResources.ScanAutomatically,
TextColor = UIColor.White,
Font = UIFont.FromName("TitilliumWeb-Regular", 17),
TextAlignment = UITextAlignment.Center
};
View.AddSubview(bottomTextLabel);
View.BringSubviewToFront(bottomTextLabel);
if (captureDevice.Position == AVCaptureDevicePosition.Back)
{
flashLightButton = new UIButton();
flashLightButton.SetImage(new UIImage(filename: "flash_white_light.png"), UIControlState.Normal);
flashLightButton.Frame = new CGRect(x: 0, y: 0, width: 50, height: 50);
bottomBarView.AddSubview(flashLightButton);
flashLightButton.TouchUpInside += async (object sender, EventArgs e) =>
{
NSError error = null;
if (captureDevice == null) return;
captureDevice.LockForConfiguration(out error);
if (error != null)
{
captureDevice.UnlockForConfiguration();
return;
}
else
{
if (!captureDevice.TorchAvailable)
{
var alert = new UIAlertView
{
Title = AppResources.MarginPoint,
Message = AppResources.CameraFlash
};
alert.AddButton(AppResources.OkButtonTitle);
alert.Show();
return;
}
if (captureDevice.TorchMode != AVCaptureTorchMode.On)
{
captureDevice.TorchMode = AVCaptureTorchMode.On;
}
else
{
captureDevice.TorchMode = AVCaptureTorchMode.Off;
}
captureDevice.UnlockForConfiguration();
}
};
}
string blueColor = "#1273B6";
cancelButton = new UIButton();
cancelButton.SetTitleColor(Color.FromHex(blueColor).ToUIColor(), UIControlState.Normal);
cancelButton.SetTitle(AppResources.PickerCancelLabel, UIControlState.Normal);
cancelButton.Font = UIFont.FromName("TitilliumWeb-Regular", 18);
cancelButton.Frame = new CGRect(x: bottomBarView.Center.X - 50, y: 0, width: 100, height: 50);
bottomBarView.AddSubview(cancelButton);
cancelButton.TouchUpInside += (object sender, EventArgs e) =>
{
if (captureSession != null)
captureSession.StopRunning();
DismissViewController(true, null);
};
Device.BeginInvokeOnMainThread(() =>
{
if (captureSession != null)
captureSession.StartRunning();
});
cameraButton = new UIButton();
cameraButton.SetImage(new UIImage(filename: "camera.png"), UIControlState.Normal);
cameraButton.Frame = new CGRect(x: bottomBarView.Frame.Width - 50, y: 0, width: 50, height: 50);
bottomBarView.AddSubview(cameraButton);
cameraButton.TouchUpInside += (object sender, EventArgs e) =>
{
if (captureDevice.Position == AVCaptureDevicePosition.Back)
{
if (captureDevice.TorchAvailable)
captureDevice.TorchMode = AVCaptureTorchMode.Off;
captureDevice = GetCameraDevice(AVCaptureDevicePosition.Front);
marginpoint.im.iOS.AppDelegate.camPosition = true;
}
else
{
if (captureDevice.TorchAvailable)
captureDevice.TorchMode = AVCaptureTorchMode.Off;
captureDevice = GetCameraDevice(AVCaptureDevicePosition.Back);
marginpoint.im.iOS.AppDelegate.camPosition = false;
}
if (captureDevice != null)
{
try
{
NSError error;
var input = new AVCaptureDeviceInput(captureDevice, out error);
captureSession = new AVCaptureSession();
if (captureSession == null) { return; }
if (captureSession.CanAddInput(input))
captureSession.AddInput(input);
var captureMetadataOutput = new AVCaptureMetadataOutput();
if (captureSession.CanAddOutput(captureMetadataOutput))
{
captureSession.AddOutput(captureMetadataOutput);
// captureMetadataOutput.MetadataObjectTypes = captureMetadataOutput.AvailableMetadataObjectTypes;
captureMetadataOutput.MetadataObjectTypes = metaTypes;
}
var metadataQueue = new DispatchQueue("com.AVCam.metadata");
metadataObjectsDelegate = new MetadataObjectsDelegate
{
DidOutputMetadataObjectsAction = DidOutputMetadataObjects
};
captureMetadataOutput.SetDelegate(metadataObjectsDelegate, metadataQueue);
captureSession.StartRunning();
videoPreviewLayer = new AVCaptureVideoPreviewLayer(session: captureSession);
videoPreviewLayer.VideoGravity = AVLayerVideoGravity.ResizeAspectFill;
videoPreviewLayer.Frame = View.Layer.Bounds;
View.Layer.AddSublayer(videoPreviewLayer);
}
catch (Exception ex)
{
// Console.WriteLine("error device input" + ex.ToString());
}
addOverlayOnScreen();
}
};
}
public void DidOutputMetadataObjects(AVCaptureOutput captureOutput,
AVMetadataObject[] metadataObjects,
AVCaptureConnection connection)
{
Device.BeginInvokeOnMainThread(() =>
{
if (metadataObjects != null && metadataObjects.Length == 0)
{
//codeLabel.Text = "No Data";
//displayScanResult(string.Empty);
return;
}
var metadataObject = metadataObjects[0] as AVMetadataMachineReadableCodeObject;
if (metadataObject == null) { return; }
var visualCodeObject = videoPreviewLayer.GetTransformedMetadataObject(metadataObject);
if (metadataObject.Type == AVMetadataObjectType.QRCode)
{
if (viewfinderView.Frame.Contains(visualCodeObject.Bounds))
{
captureSession.StopRunning();
displayScanResult(metadataObject.StringValue);
}
}
else
{
captureSession.StopRunning();
displayScanResult(metadataObject.StringValue);
}
});
}
private async void displayScanResult(string metadataObjectVal)
{
OnScanSuccess?.Invoke(this, string.IsNullOrWhiteSpace(metadataObjectVal) ? string.Empty : metadataObjectVal as String);
captureSession.StopRunning();
DismissViewController(true, null);
//codeLabel.Text = metadataObject.StringValue;
}
}
class MetadataObjectsDelegate : AVCaptureMetadataOutputObjectsDelegate
{
public Action<AVCaptureMetadataOutput, AVMetadataObject[], AVCaptureConnection> DidOutputMetadataObjectsAction;
public override void DidOutputMetadataObjects(AVCaptureMetadataOutput captureOutput, AVMetadataObject[] metadataObjects, AVCaptureConnection connection)
{
if (DidOutputMetadataObjectsAction != null)
DidOutputMetadataObjectsAction(captureOutput, metadataObjects, connection);
}
}
}
Has anyone faced this issue and found a fix for it?

Instead of creating a new captureSession everytime when when user enters foreground, try just starting the existing captureSession.
willEnterForegroundNoti =
NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.WillEnterForegroundNotification,
(obj) => {
Device.BeginInvokeOnMainThread(() => {
RemoveBlurEffect();
captureSession?.startRunning();
});
});

Related

Xamarin: How to avoid internal ListView from change its content size when showing keyboard on iOS

Im creating this content page:
Content = new StackLayout()
{
Spacing = 0,
Orientation = StackOrientation.Vertical,
Children = {
(listView = new ListView
{
HasUnevenRows = true,
SeparatorVisibility = SeparatorVisibility.None,
IsPullToRefreshEnabled = true,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
ItemsSource = listItems,
ItemTemplate = new MyDataTemplateSelector(userName),
BackgroundColor = Constants.Cor_ChatFundo
}),
(grid = new Grid
{
RowSpacing = 1,
ColumnSpacing = 2,
Padding = new Thickness(5),
BackgroundColor = Color.White,
VerticalOptions = LayoutOptions.End,
HorizontalOptions = LayoutOptions.FillAndExpand,
ColumnDefinitions =
{
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) }
},
RowDefinitions =
{
new RowDefinition { Height = new GridLength(40) }
}
})
}
};
grid.Children.Add(sendMessageEntry = new Entry
{
FontSize = 18,
HeightRequest = 30,
Placeholder = "Type here...",
Keyboard = Keyboard.Chat
}, 0, 0);
grid.Children.Add(buttonSend = new Button
{
Text = "Send"
}, 1, 0);
I'm using a modified version of the KeyboardOverlapRenderer to move the entire page UP when the keyboard is shown.
The modified version of the KeyboardOverlapRenderer is to handle the suggestion bar above the iOS8 keyboard... the original version doesn't handle that.
The KeyboardOverlapRenderer class:
using System;
using Xamarin.Forms.Platform.iOS;
using Foundation;
using UIKit;
using Xamarin.Forms;
using CoreGraphics;
using EficienciaEnergetica.iOS.KeyboardOverlap;
using System.Diagnostics;
using EficienciaEnergetica.ContentPages;
[assembly: ExportRenderer(typeof(Page), typeof(KeyboardOverlapRenderer))]
namespace EficienciaEnergetica.iOS.KeyboardOverlap
{
[Preserve(AllMembers = true)]
public class KeyboardOverlapRenderer : PageRenderer
{
Rectangle initialViewState;
NSObject _keyboardShowObserver;
NSObject _keyboardHideObserver;
private bool _pageWasShiftedUp;
private double _activeViewBottom;
private bool _isKeyboardShown;
public static void StaticInit()
{
var now = DateTime.Now;
Debug.WriteLine("Keyboard Overlap plugin initialized {0}", now);
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
var page = Element as ContentPage;
if (page != null)
{
var contentScrollView = page.Content as ScrollView;
if (contentScrollView != null)
return;
initialViewState = Element.Bounds;
RegisterForKeyboardNotifications();
}
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
UnregisterForKeyboardNotifications();
}
void RegisterForKeyboardNotifications()
{
if (_keyboardShowObserver == null)
_keyboardShowObserver = NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification, OnKeyboardShow);
if (_keyboardHideObserver == null)
_keyboardHideObserver = NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillHideNotification, OnKeyboardHide);
}
void UnregisterForKeyboardNotifications()
{
_isKeyboardShown = false;
if (_keyboardShowObserver != null)
{
NSNotificationCenter.DefaultCenter.RemoveObserver(_keyboardShowObserver);
_keyboardShowObserver.Dispose();
_keyboardShowObserver = null;
}
if (_keyboardHideObserver != null)
{
NSNotificationCenter.DefaultCenter.RemoveObserver(_keyboardHideObserver);
_keyboardHideObserver.Dispose();
_keyboardHideObserver = null;
}
}
protected virtual void OnKeyboardShow(NSNotification notification)
{
if (!IsViewLoaded)
return;
_isKeyboardShown = true;
var activeView = View.FindFirstResponder();
if (activeView == null)
return;
var keyboardFrame = UIKeyboard.FrameEndFromNotification(notification);
var isOverlapping = activeView.IsKeyboardOverlapping(View, keyboardFrame);
if (!isOverlapping)
return;
if (isOverlapping)
{
System.Diagnostics.Debug.WriteLine(keyboardFrame);
_activeViewBottom = activeView.GetViewRelativeBottom(View);
ShiftPageUp(keyboardFrame.Height, _activeViewBottom);
}
}
private void OnKeyboardHide(NSNotification notification)
{
if (!IsViewLoaded)
return;
_isKeyboardShown = false;
var keyboardFrame = UIKeyboard.FrameEndFromNotification(notification);
if (_pageWasShiftedUp)
ShiftPageDown(keyboardFrame.Height, _activeViewBottom);
}
private void ShiftPageUp(nfloat keyboardHeight, double activeViewBottom)
{
var pageFrame = initialViewState;// Element.Bounds;
var newY = pageFrame.Y + CalculateShiftByAmount(pageFrame.Height, keyboardHeight, activeViewBottom);
Element.LayoutTo(new Rectangle(pageFrame.X, newY,
pageFrame.Width, pageFrame.Height));
_pageWasShiftedUp = true;
}
private void ShiftPageDown(nfloat keyboardHeight, double activeViewBottom)
{
Element.LayoutTo(initialViewState);
_pageWasShiftedUp = false;
}
private double CalculateShiftByAmount(double pageHeight, nfloat keyboardHeight, double activeViewBottom)
{
return (pageHeight - activeViewBottom) - keyboardHeight;
}
}
}
The problem I have is when editing the entry. In iOS it shows the keyboard, but the content of the internal listview seems to allocate an empty space for the keyboard also, and it is possible to scroll down the list more than the elements that are inside it.
Is it possible to disable the listview keyboard notification behaviour in this situation? The ListView is not the main component in this page.
I found a solution for me!
Set lv.HeightRequest on the entry focus!
Seems to be

how to expand customPinWinod in xamarin forms maps

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.
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 Labels .
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:

Bindable Property is not updating view on iOS

I am creating a cross platform app on xamarin.forms. I needed a custom renderer with gradient background and i've created it and it works well on both android and ios.
However, when i want to change the colors of the gradient background, it does not work on iOS.
Test.xaml.cs
namespace KiaiDay
{
public class TesteViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Color _startColor = Color.Green;
public Color SStartColor
{
get => _startColor;
set { _startColor = value; OnPropertyChanged(nameof(SStartColor)); }
}
private Color _endColor = Color.Blue;
public Color EEndColor
{
get => _endColor;
set { _endColor = value; OnPropertyChanged(nameof(EEndColor)); }
}
public ICommand Select
{
get => new Command(() =>
{
SStartColor = Color.Red;
EEndColor = Color.Brown;
});
}
#region INotifyPropertyChanged Implementation
void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged == null)
return;
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class teste : ContentPage
{
public teste()
{
BindingContext = new TesteViewModel();
InitializeComponent();
}
}
}
Test.xaml
<ContentPage.Content>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<local:ShadowFrame StartColor="{Binding SStartColor}" EndColor="{Binding EEndColor}" HorizontalOptions="Center" VerticalOptions="Center">
<Label Text="Teste"/>
<local:ShadowFrame.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Command="{Binding Select}"/>
</local:ShadowFrame.GestureRecognizers>
</local:ShadowFrame>
</StackLayout>
</ContentPage.Content>
Custom Renderer in PCL
public class ShadowFrame : Frame
{
public static BindableProperty ElevationProperty = BindableProperty.Create(nameof(Elevation), typeof(float), typeof(ShadowFrame), default(float));
public static readonly BindableProperty StartColorProperty = BindableProperty.Create(nameof(StartColor), typeof(Color), typeof(ShadowFrame), default(Color));
public static readonly BindableProperty EndColorProperty = BindableProperty.Create(nameof(EndColor), typeof(Color), typeof(ShadowFrame), default(Color));
public float Elevation
{
get { return (float)GetValue(ElevationProperty); }
set { SetValue(ElevationProperty, value); }
}
public Color StartColor
{
get { return (Color)GetValue(StartColorProperty); }
set { SetValue(StartColorProperty, value); }
}
public Color EndColor
{
get { return (Color)GetValue(EndColorProperty); }
set { SetValue(EndColorProperty, value); }
}
}
iOS Renderer(i think the problem is here)
[assembly: ExportRenderer(typeof(ShadowFrame), typeof(ShadowFrameRenderer))]
namespace KiaiDay.iOS.Renderers
{
public class ShadowFrameRenderer : FrameRenderer
{
CAGradientLayer gradientLayer;
private Color StartColor { get; set; }
private Color EndColor { get; set; }
CGRect rect;
public static void Initialize()
{
// empty, but used for beating the linker
}
public ShadowFrameRenderer() => gradientLayer = new CAGradientLayer();
public override void Draw(CGRect rect)
{
this.rect = rect;
var stack = (ShadowFrame)this.Element;
StartColor = stack.StartColor;
EndColor = stack.EndColor;
CGColor startColor = this.StartColor.ToCGColor();
CGColor endColor = this.EndColor.ToCGColor();
#region for Vertical Gradient
//var gradientLayer = new CAGradientLayer();
#endregion
#region for Horizontal Gradient
//var gradientLayer = new CAGradientLayer()
//{
// StartPoint = new CGPoint(0, 0.5),
// EndPoint = new CGPoint(1, 0.5)
//};
#endregion
gradientLayer.Frame = rect;
gradientLayer.Colors = new CGColor[] {
startColor,
endColor
};
NativeView.Layer.InsertSublayer(gradientLayer, 0);
base.Draw(rect);
}
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
var stack = (ShadowFrame)e.NewElement;
if (e.OldElement != null || Element == null)
{
return;
}
try
{
this.StartColor = stack.StartColor;
this.EndColor = stack.EndColor;
UpdateShadow();
Draw(rect);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("ERROR:", ex.Message);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var stack = (ShadowFrame) sender;
if (e.PropertyName == ShadowFrame.ElevationProperty.PropertyName)
{
UpdateShadow();
}
if (e.PropertyName == ShadowFrame.StartColorProperty.PropertyName)
{
this.StartColor = stack.StartColor;
}
if (e.PropertyName == ShadowFrame.EndColorProperty.PropertyName)
{
this.EndColor = stack.EndColor;
}
}
private void UpdateShadow()
{
var shadowFrame = (ShadowFrame)Element;
// Update shadow to match better material design standards of elevation
Layer.ShadowRadius = shadowFrame.Elevation;
Layer.ShadowColor = UIColor.Gray.CGColor;
Layer.ShadowOffset = new CGSize(2, 2);
Layer.ShadowOpacity = 0.80f;
Layer.ShadowPath = UIBezierPath.FromRect(Layer.Bounds).CGPath;
Layer.MasksToBounds = false;
}
}
}
I would like to update iOS view immediatly after i change the color through the command.
If you change a property and the view doesn't update the issue has to be in OnElementPropertyChanged.
You need to call UpdateShadow() and Draw().
After digging a lot i think i have found a solution:
iOS Custom Renderer :
public class GradientStackColorRenderer : VisualElementRenderer<Frame>
{
private Color StartColor { get; set; }
private Color EndColor { get; set; }
public override CGRect Frame
{
get
{
return base.Frame;
}
set
{
if (value.Width > 0 && value.Height > 0)
{
foreach (var layer in NativeView?.Layer.Sublayers.Where(layer => layer is CAGradientLayer))
layer.Frame = new CGRect(0, 0, value.Width, value.Height);
}
base.Frame = value;
}
}
protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
try
{
var stack = e.NewElement as GradientColorStack;
this.StartColor = stack.StartColor;
this.EndColor = stack.EndColor;
AdicionarGradiente();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(#"ERROR:", ex.Message);
}
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var stack = this.Element as GradientColorStack;
if(e.PropertyName == GradientColorStack.StartColorProperty.PropertyName)
{
this.StartColor = stack.StartColor;
AdicionarGradiente();
}
if (e.PropertyName == GradientColorStack.EndColorProperty.PropertyName)
{
this.EndColor = stack.EndColor;
AdicionarGradiente();
}
}
public void AdicionarGradiente()
{
var gradient = new CAGradientLayer();
gradient.CornerRadius = NativeView.Layer.CornerRadius = 5;
gradient.Colors = new CGColor[] { StartColor.ToCGColor(), EndColor.ToCGColor() };
var layer = NativeView?.Layer.Sublayers.LastOrDefault();
NativeView?.Layer.InsertSublayerBelow(gradient, layer);
}
public static CGColor ToCGColor(Color color)
{
return new CGColor(CGColorSpace.CreateSrgb(), new nfloat[] { (float)color.R, (float)color.G, (float)color.B, (float)color.A });
}
}

Logout Issue in MonoTouch.SlideoutNavigation in iOS(Xamarin)

I am newbie in iOS. I am creating SlideoutNavigation Menu using below Component of Xamarin. The Github link is below.
Link : https://github.com/thedillonb/MonoTouch.SlideoutNavigation
In this Component the whole thing is working fine. But I have a little Issue.
Initially my LoginViewController is looking like this.
Screenshot :
There is no Menu Button in the Left side.
Now When I login the New Open Screen with the Menu is look like this
Screenshot :
Now When open the SlideOut menu and select the Logout Option I want to start my LoginViewController it also working fine using below code.
Code :
var loginViewController = storyboard.InstantiateViewController("ViewController") as ViewController;
BizApplication.clearCredential();
StyledStringElement logout = new StyledStringElement("Logout", () => NavigationController.PushViewController(loginViewController, true)){ TextColor = UIColor.White, BackgroundColor = UIColor.Clear };
logout.Image = new UIImage("filter_icon.png");
But Now I am getting below Screen which is not want. I want to remove that Left Menu icon in the Navigation.
Screenshot :
Any Help be Appreciated.
Update :
push code for new Controller open :
if (BizApplication.getCredential() != null)
{
window = new UIWindow(UIScreen.MainScreen.Bounds);
Menu = new SlideoutNavigationController();
var webController2 = Storyboard.InstantiateViewController("SearchViewController") as SearchViewController;
NavigationController.PushViewController(webController2, true);
Menu.MainViewController = new MainNavigationController(webController2, Menu);
Menu.MenuViewController = new MenuNavigationController(new DummyControllerLeft(), Menu) { NavigationBarHidden = true };
window.RootViewController = Menu;
window.MakeKeyAndVisible();
loadingOverlay.Hide();
}
My Flow :
I have use only one StoryBoard for my Project. So all the ViewController are in the same StoryBoard.
Deployment Info :
AppDelegate.cs I am not changing anything in this file.
SplashViewController.cs
public partial class SplashViewController : UIViewController
{
UIWindow window;
UIViewController container;
UIStoryboard storyboard;
public SlideoutNavigationController Menu { get; private set; }
public SplashViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
if (Reachability.IsHostReachable("http://google.com"))
{
if (BizApplication.CheckCredential())
{
window = new UIWindow(UIScreen.MainScreen.Bounds);
Menu = new SlideoutNavigationController();
storyboard = UIStoryboard.FromName("Main", null);
var webController = storyboard.InstantiateViewController("SearchViewController") as SearchViewController;
NavigationController.PushViewController(webController, true);
Menu.MainViewController = new MainNavigationController(webController, Menu);
Menu.MenuViewController = new MenuNavigationController(new DummyControllerLeft(), Menu) { NavigationBarHidden = true };
window.RootViewController = Menu;
window.MakeKeyAndVisible();
}
else {
storyboard = UIStoryboard.FromName("Main", null);
var webController = storyboard.InstantiateViewController("ViewController") as ViewController;
this.NavigationController.PushViewController(webController, true);
}
}
}
public void pushMenu()
{
UINavigationController navMin = (UINavigationController)window.RootViewController;
Menu = new SlideoutNavigationController();
storyboard = UIStoryboard.FromName("Main", null);
var webController = storyboard.InstantiateViewController("SearchViewController") as SearchViewController;
Menu.MainViewController = new MainNavigationController(webController, Menu);
Menu.MenuViewController = new MenuNavigationController(new DummyControllerLeft(), Menu) { NavigationBarHidden = true };
navMin.PushViewController(Menu, true);
}
}
ViewController.cs (is My Login view Controller)
public partial class ViewController : UIViewController
{
LoadingOverlay loadingOverlay;
UIWindow window;
public SlideoutNavigationController Menu { get; private set; }
protected ViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
this.Title = "Log In";
UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.LightContent;
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
this.NavigationController.NavigationBar.TitleTextAttributes = new UIStringAttributes() { ForegroundColor = UIColor.White };
this.NavigationItem.SetHidesBackButton(true, false);
this.NavigationController.NavigationBar.BarTintColor = UIColor.Clear.FromHexString("#0072BA", 1.0f);
this.NavigationController.NavigationBarHidden = false;
txtfield_Username.Layer.BorderWidth = 1.0f;
txtfield_Username.Layer.BorderColor = UIColor.Clear.FromHexString("#000000", 1.0f).CGColor;
txtfield_password.Layer.BorderWidth = 1.0f;
txtfield_password.Layer.BorderColor = UIColor.Clear.FromHexString("#000000", 1.0f).CGColor;
lbl_forgetPassword.TextColor = UIColor.Clear.FromHexString("#0072BA", 1.0f);
btn_register.TouchUpInside += (sender, e) =>
{
var webController = Storyboard.InstantiateViewController("RegisterController") as RegisterController;
NavigationController.PushViewController(webController, true);
};
btn_login.TouchUpInside += async (sender, e) =>
{
loadingOverlay = new LoadingOverlay(UIScreen.MainScreen.Bounds);
View.Add(loadingOverlay);
Token token = await Authonicator.Authonicate(txtfield_Username.Text, txtfield_password.Text);
if (token != null)
{
AppCredentials credentials = new AppCredentials();
credentials.Token = token;
credentials.UserName = txtfield_Username.Text;
var userItem = await UserClient.GetUserInfo(token.Access_token);
if (userItem != null)
{
credentials.Id = userItem.Id;
credentials.Name = userItem.Name.FirstName + " " + userItem.Name.LastName;
credentials.Names.FirstName = userItem.Name.FirstName;
credentials.Names.MiddleName = userItem.Name.MiddleName;
credentials.Names.LastName = userItem.Name.LastName;
credentials.Role = userItem.Role;
credentials.Contact = userItem.Mobile;
}
BizApplication.setCredential(credentials);
if (BizApplication.getCredential() != null)
{
window = new UIWindow(UIScreen.MainScreen.Bounds);
Menu = new SlideoutNavigationController();
var webController2 = Storyboard.InstantiateViewController("SearchViewController") as SearchViewController;
Menu.MainViewController = new MainNavigationController(webController2, Menu);
Menu.MenuViewController = new MenuNavigationController(new DummyControllerLeft(), Menu) { NavigationBarHidden = true };
window.RootViewController = Menu;
window.MakeKeyAndVisible();
loadingOverlay.Hide();
}
}
else {
UIAlertController alert = UIAlertController.Create("Authorization", "Enter Valid Username and Password", UIAlertControllerStyle.Alert);
alert.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Cancel, Action => { }));
PresentViewController(alert, true, null);
loadingOverlay.Hide();
}
};
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
}
}
DummyControllerLeft.cs
using System;
using System.Drawing;
using System.Threading.Tasks;
using CoreAnimation;
using Foundation;
using Gargi.Business;
using MonoTouch.Dialog;
using UIKit;
namespace Gargi.iOS
{
public class DummyControllerLeft : DialogViewController
{
public static UIImageView profileImage;
public DummyControllerLeft(IntPtr handle) : base(handle)
{
}
public DummyControllerLeft()
: base(UITableViewStyle.Plain, new RootElement(""))
{
var storyboard = UIStoryboard.FromName("Main", null);
var webController = storyboard.InstantiateViewController("SearchViewController") as SearchViewController;
StyledStringElement search = new StyledStringElement("Search", () => NavigationController.PushViewController(webController, true)) { TextColor = UIColor.White, BackgroundColor = UIColor.Clear };
search.Image = new UIImage("filter_icon.png");
var appointController = storyboard.InstantiateViewController("AppointmentListController") as AppointmentListController;
StyledStringElement appointment = new StyledStringElement("Appointment", () => NavigationController.PushViewController(appointController, true)) { TextColor = UIColor.White, BackgroundColor = UIColor.Clear };
appointment.Image = new UIImage("filter_icon.png");
var caseHistoryController = storyboard.InstantiateViewController("CaseHistoryController") as CaseHistoryController;
StyledStringElement casehistory = new StyledStringElement("CaseHistory", () => NavigationController.PushViewController(caseHistoryController, true)){ TextColor = UIColor.White, BackgroundColor = UIColor.Clear };
casehistory.Image = new UIImage("filter_icon.png");
var accountController = storyboard.InstantiateViewController("AccountViewController") as AccountViewController;
StyledStringElement account = new StyledStringElement("Account", () => NavigationController.PushViewController(accountController, true)){ TextColor = UIColor.White, BackgroundColor = UIColor.Clear };
account.Image = new UIImage("filter_icon.png");
var securityController = storyboard.InstantiateViewController("SecurityViewController") as SecurityViewController;
StyledStringElement security = new StyledStringElement("Security", () => NavigationController.PushViewController(securityController, true)){ TextColor = UIColor.White, BackgroundColor = UIColor.Clear };
security.Image = new UIImage("filter_icon.png");
var workProfileController = storyboard.InstantiateViewController("WorkProfileViewController") as WorkProfileViewController;
StyledStringElement workProfile = new StyledStringElement("WorkProfile", () => NavigationController.PushViewController(workProfileController, true)){ TextColor = UIColor.White, BackgroundColor = UIColor.Clear };
workProfile.Image = new UIImage("filter_icon.png");
BizApplication.clearCredential();
StyledStringElement logout = new StyledStringElement("Logout",() => CallthisMethod()){ TextColor = UIColor.White, BackgroundColor = UIColor.Clear };
logout.Image = new UIImage("filter_icon.png");
Root.Add(new Section()
{
search,
appointment,
casehistory,
account,
security,
workProfile,
logout
} );
TableView.SeparatorStyle = UITableViewCellSeparatorStyle.None;
TableView.BackgroundColor = UIColor.Clear.FromHexString("#0072BA", 1.0f);
}
void CallthisMethod()
{
var vwControllers = NavigationController.ViewControllers;
foreach (UIViewController signiinVC in vwControllers)
{
if (signiinVC.GetType() == typeof(ViewController))
{
this.NavigationController.PopToViewController(signiinVC,true);
}
}
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
TableView.Frame = new RectangleF((float)TableView.Frame.Left, (float)(TableView.Frame.Top + 30), (float)TableView.Frame.Width, (float)(TableView.Frame.Height - 30));
UIView headerView = new UIView();
headerView.Frame = new CoreGraphics.CGRect(0, 0, TableView.Frame.Width, 140);
//headerView.BackgroundColor = UIColor.Clear.FromHexString("#004F80", 1.0f);
profileImage = new UIImageView();
profileImage.Frame = new CoreGraphics.CGRect(10, 10, 70, 70);
profileImage.Layer.CornerRadius = 35;
profileImage.ClipsToBounds = true;
profileImage.Image = UIImage.FromBundle("gargi_logo.png");
UILabel userName = new UILabel();
userName.Frame = new CoreGraphics.CGRect(10, 90, TableView.Frame.Width - 20, 20);
userName.Font = UIFont.FromName("Helvetica-Bold", 14f);
userName.TextColor = UIColor.White;
headerView.AddSubview(userName);
UILabel userRole = new UILabel();
userRole.Frame = new CoreGraphics.CGRect(10, 110, TableView.Frame.Width - 20, 20);
userRole.Font = UIFont.FromName("Helvetica-Bold", 14f);
userRole.TextColor = UIColor.White;
headerView.AddSubview(userRole);
headerView.AddSubview(profileImage);
TableView.TableHeaderView = headerView;
if (BizApplication.getCredential().Name != null)
{
userName.Text = BizApplication.getCredential().Name;
}
if (BizApplication.getCredential().Role != null)
{
userRole.Text = BizApplication.getCredential().Role;
}
var gradient = new CAGradientLayer();
gradient.Frame = headerView.Frame;
gradient.Colors = new CoreGraphics.CGColor[] { UIColor.Clear.FromHexString("#0072BA", 1.0f).CGColor,UIColor.Clear.FromHexString("#004f80",1.0f).CGColor};
headerView.Layer.InsertSublayer(gradient, 0);
var task = GetUserImage();
}
private async Task GetUserImage()
{
var userHeader = await UserClient.GetHeaderData();
if (!string.IsNullOrEmpty(userHeader.Image))
{
string trimbase = userHeader.Image.Trim('"');
try
{
var imageBytes = Convert.FromBase64String(trimbase);
var imageData = NSData.FromArray(imageBytes);
profileImage.BackgroundColor = UIColor.White;
profileImage.Layer.CornerRadius = 35;
profileImage.ClipsToBounds = true;
profileImage.Image = UIImage.LoadFromData(imageData);
}
catch (Exception ex)
{
string msg = ex.Message;
}
}
}
}
}
Update latest :
If I do this in the DummyLeftControllers.cs then nothing is happen :
StyledStringElement logout = new StyledStringElement("Logout",() => CallthisMethod(storyboard)){ TextColor = UIColor.White, BackgroundColor = UIColor.Clear };
logout.Image = new UIImage("filter_icon.png");
void CallthisMethod(UIStoryboard storyboard)
{
var vwControllers = NavigationController.ViewControllers;
foreach (UIViewController signiinVC in vwControllers)
{
if (signiinVC.GetType() == typeof(ViewController))
{
this.NavigationController.PopToViewController(signiinVC, true);
}
}
BizApplication.clearCredential();
NavigationController.PopToRootViewController(true);
}
You are pushing SignInView controller on stack on Logout--which is incorrect. Write your code in a way that It use previously pushed view from stack..
In Splashviewconrtroller change following in viewdidload method:
base.ViewDidUnload ();
storyboard = UIStoryboard.FromName ("Main", null);
var webController = storyboard.InstantiateViewController ("ViewController") as ViewController;
this.NavigationController.PushViewController (webController, false);
if (Reachability.IsHostReachable ("http://google.com")) {
if (BizApplication.CheckCredential ()) {
//window = new UIWindow (UIScreen.MainScreen.Bounds);
Menu = new SlideoutNavigationController ();
storyboard = UIStoryboard.FromName ("Main", null);
var webController = storyboard.InstantiateViewController ("SearchViewController") as SearchViewController;
NavigationController.PushViewController (webController, false);
Menu.MainViewController = new MainNavigationController (webController, Menu);
Menu.MenuViewController = new MenuNavigationController (new DummyControllerLeft (), Menu) { NavigationBarHidden = true };
this.NavigationController.PushViewController (Menu);
//window.RootViewController = Menu;
//window.MakeKeyAndVisible ();
}
}
On Logout..Call following:
void onLogOut (object sender, EventArgs e)
{
//Write your code to clear
var vwControllers = this.NavigationController.ViewControllers;
foreach(UIViewController signinVC in vwControllers) {
if (signinVC.GetType () == typeof (ViewController)) {
this.NavigationController.PopToViewController (ViewController);
}
}
}
i think u should present your view controller modally there is no need to push them in stack as u don't need back button.
instead of
navigationcontroller.pushviewcontroller()
use
presentviewcontroller()
you can use dismissviewcntroller() when logout and this will not change your navigationbar in login screen

How to select an Image from Gallery in Xamarin.ios

I want to load an Image from Gallery and display it on a UIImageView.
I tried to do it like in the sample in Xamarin Recipes, but all I get is null.
https://developer.xamarin.com/recipes/ios/media/video_and_photos/choose_a_photo_from_the_gallery/
My Code so far:
public partial class ViewController : UIViewController
{
UIImagePickerController imagePicker;
UIButton choosePhotoButton;
UIImageView imageView;
public ViewController(IntPtr handle) : base(handle)
{
}
public ViewController()
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
Title = "Wähle Bild aus:";
View.BackgroundColor = UIColor.White;
imageView = new UIImageView(new CGRect(10, 150, 300, 300));
Add(imageView);
choosePhotoButton = UIButton.FromType(UIButtonType.RoundedRect);
choosePhotoButton.Frame = new CGRect(10, 80, 100, 40);
choosePhotoButton.SetTitle("Picker", UIControlState.Normal);
choosePhotoButton.TouchUpInside += (s, e) =>
{
imagePicker = new UIImagePickerController();
imagePicker.SourceType = UIImagePickerControllerSourceType.PhotoLibrary;
imagePicker.MediaTypes = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.PhotoLibrary);
imagePicker.FinishedPickingMedia += Handle_FinishedPickingMedia;
imagePicker.Canceled += Handle_Canceled;
NavigationController.PresentModalViewController(imagePicker, true);
};
View.Add(choosePhotoButton);
}
private void Handle_Canceled(object sender, EventArgs e)
{
imagePicker.DismissModalViewController(true);
}
protected void Handle_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
{
bool isImage = false;
switch (e.Info[UIImagePickerController.MediaType].ToString())
{
case "public.image":
Console.WriteLine("Image selected");
isImage = true;
break;
case "public.video":
Console.WriteLine("Video selected");
break;
}
// get common info (shared between images and video)
NSUrl referenceURL = e.Info[new NSString("UIImagePickerControllerReferenceUrl")] as NSUrl;
if (referenceURL != null)
Console.WriteLine("Url:" + referenceURL.ToString());
// if it was an image, get the other image info
if (isImage)
{
// get the original image
UIImage originalImage = e.Info[UIImagePickerController.OriginalImage] as UIImage;
if (originalImage != null)
{
// do something with the image
imageView.Image = originalImage; // display
}
UIImage editedImage = e.Info[UIImagePickerController.EditedImage] as UIImage;
if (editedImage != null)
{
// do something with the image
Console.WriteLine("got the edited image");
imageView.Image = editedImage;
}
}
// dismiss the picker
imagePicker.DismissModalViewController(true);
}
I get null at this line:
NavigationController.PresentModalViewController(imagePicker, true);
What I'm doing wrong?
Appears that you are not using a UINavigationController, so change:
NavigationController.PresentModalViewController(imagePicker, true);
To:
PresentModalViewController(imagePicker, true);
Note: PresentModalViewController is deprecated since ios6, so you can change your code to use:
PresentViewControllerAsync(imagePicker, true);
Or:
PresentViewController(imagePicker, true, () => {
});

Resources