Presenting UIImagePickerController crashes app - ios

I am trying to present a UIImagePickerController via a Button action. My project crashes with error:
Got a SIGABRT while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
I only have a ViewController embedded in a NavigationController in the storyboard. Code snippets below:
UIImagePickerController imagePicker;
public override void ViewDidLoad()
{
base.ViewDidLoad();
this.setupImagePicker();
CapturePhotoButton.TouchUpInside += delegate
{
this.AddMedia();
};
}
public void setupImagePicker()
{
imagePicker = new UIImagePickerController();
imagePicker.SourceType = UIImagePickerControllerSourceType.Camera;
imagePicker.ModalPresentationStyle = UIModalPresentationStyle.Popover;
imagePicker.MediaTypes = UIImagePickerController.AvailableMediaTypes(
UIImagePickerControllerSourceType.Camera);
imagePicker.FinishedPickingMedia += HandleFinishedPickingMedia;
imagePicker.Canceled += (sender, e) => {
imagePicker.DismissModalViewController(true);
};
}
public void HandleFinishedPickingMedia(object sender,
UIImagePickerMediaPickedEventArgs e)
{
bool isImage = false;
switch (e.Info[UIImagePickerController.MediaType].ToString())
{
case "public.image":
isImage = true;
break;
case "public.video":
break;
}
if (isImage)
{
UIImage originalImage = e.Info[UIImagePickerController.OriginalImage] as UIImage;
if (originalImage != null)
{
PreviewImageView.Image = originalImage;
imagePicker.DismissViewController(true, null);
}
}
}
public void AddMedia()
{
//Crashes on this line
this.NavigationController.PresentViewController(imagePicker, true, null);
}

Added Privacy - Camera Usage Description to your info.plist and that resolved the issue

Add the following to your info.plist for camera use:
<key>NSCameraUsageDescription</key>
<string>This app needs access to the camera to take photos.</string>
for library:
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to photos.</string>

Related

ios image picker delegates not triggering in nativescript

I'm writing nativescript plugin for image picker. I'm finished with part of android. Now, I'm writing code for ios. It's showing image picker dialog, but assigned delegates are not getting triggered. Please check my code below.
import * as application from "tns-core-modules/application";
import * as frame from "tns-core-modules/ui/frame"
export class Nativemediapicker extends NSObject implements UIImagePickerControllerDelegate {
public static ObjCProtocols = [UIImagePickerControllerDelegate];
get() {
let version = NSBundle.mainBundle.objectForInfoDictionaryKey("CFBundleShortVersionString");
return version;
}
static new(): Nativemediapicker {
return <Nativemediapicker>super.new();
}
private _callback: (result?) => void;
private _errorCallback: (result?) => void;
public initWithCallbackAndOptions(callback: (result?) => void, errorCallback: (result?) => void, options?): Nativemediapicker {
this._callback = callback;
this._errorCallback = errorCallback;
if (options) {
// collect options
}
console.log('initWithCallbackAndOptions')
return this;
}
static registerFileProvider(provider) { }
static pickFiles(mimeType, onResult, onError) {
onError("ERROR: For ios this feature is comming soon.");
}
static takePicture(onResult, onError) {
// if (!UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)) {
// onError("ERROR: For ios simulator this feature is not supported.");
// return
// }
let imagePicker = UIImagePickerController.new()
imagePicker.delegate = Nativemediapicker.new().initWithCallbackAndOptions(onResult, onError, null)
imagePicker.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
imagePicker.allowsEditing = false
// imagePicker.showsCameraControls = true
let topMostFrame = frame.topmost();
if (topMostFrame) {
let viewController: UIViewController = topMostFrame.currentPage && topMostFrame.currentPage.ios;
if (viewController) {
while (viewController.parentViewController) {
// find top-most view controler
viewController = viewController.parentViewController;
}
while (viewController.presentedViewController) {
// find last presented modal
viewController = viewController.presentedViewController;
}
viewController.presentViewControllerAnimatedCompletion(imagePicker, true, null);
}
}
}
static recordVideo(onResult, onError) {
onError("ERROR: For ios this feature is comming soon.");
}
static recordAudio(onResult, onError) {
onError("ERROR: For ios this feature is comming soon.");
}
imagePickerControllerDidCancel(picker): void {
console.log("imagePickerControllerDidCancel")
this._errorCallback("ERROR: Image capturing cancelled.");
}
imagePickerControllerDidFinishPickingMediaWithInfo(picker, info): void {
console.log("imagePickerControllerDidCancel")
this._errorCallback("ERROR: Image capturing done.");
}
}
I'm not getting, what I'm doing wrong and where?
Please help me, guys...
I suspect the reason is that your delegate is being cleaned up by garbage collector. One important rule with iOS is, you must always keep a reference of native object in a JS variable to keep it alive.
Try,
private _delegate;
....
this._delegate = Nativemediapicker.new().initWithCallbackAndOptions(onResult, onError, null);
imagePicker.delegate = this._delegate;
After adding this line in takePicture function it worked.
imagePicker.modalPresentationStyle = UIModalPresentationStyle.CurrentContext;

UIImagePickerController crashesdue to memory issues in ios 10

I get random memory crashes with no memory warning. I have updated my ios to 10.0.2, my Xamarin to 6.2. I added camera usage description and photo library usage description, as suggested.
I have the following code in my FinishedPickingMedia callback
public async override void FinishedPickingMedia(UIImagePickerController picker, NSDictionary info)
{
try
{
// determine what was selected, video or image
bool isImage = false;
switch (info[UIImagePickerController.MediaType].ToString())
{
case "public.image":
isImage = true;
break;
case "public.video":
break;
}
if (isImage)
{
UIImage originalImage = info[UIImagePickerController.OriginalImage] as UIImage;
if (originalImage != null)
{
UIImageOrientation OrIn = originalImage.Orientation;
Debug.WriteLine("scaling image");
var originalImage1 = await Task.Run(() => ScaleAndRotateImage.ScaleAndRotateImageView(originalImage, OrIn));
if (originalImage1 != null)
{
var Data = originalImage1.AsJPEG(0.0f);
if (Data != null)
{
UIImage resizedImage = UIImage.LoadFromData(Data);
if (originalImage != null)
{
originalImage.Dispose();
originalImage = null;
}
originalImage1.Dispose();
originalImage1 = null;
Data.Dispose();
Data = null;
GC.Collect();
#if DEBUG
var Process1 = Process.GetCurrentProcess();
if (Process1 != null)
{
Debug.WriteLine(string.Format("memory allocated by FinishedPickingMedia = {0}", Process1.WorkingSet64));
}
#endif
camController.ShowPhoto(resizedImage);
}
if (info != null)
{
info.Dispose();
info = null;
}
GC.Collect();
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine("Exception Occured in FinishedPickingMedia method due to " + ex.Message);
}
finally
{
// dismiss the picker
picker.DismissModalViewController(true);
picker.Dispose();
picker = null;
GC.Collect();
}
}
This seams to happen randomly, my application needs to capture more than 200 photos (archive) I researched on the internet and this seams to be an issue with iOS 10 and this control

Showing "Import with Instagram" in UIActivityViewController

I am trying to add Instagram in "Share To" functionality in my app. I have seen the Instagram's iPhone hooks documents. I have created custom UIActivty which works fine but my question is, is there a way to add "Import with Instagram" functionality as it can be seen in iOS's Photos app iOS Photo App:
In my app for some reason, it does not show that "Import with Instagram". my app Share view :
I do not want to share only with Instagram so no ".igo"
EDIT: All of this is specifically for iOS versions < 10. For some reasons Instagram Share Extension works fine (for my app) in devices with iOS >= 10.
EDIT: I am trying to share image and video with ".jpeg" and ".mov" formats respectively
I have seen/read that Instagram added share extension in release 8.2, so technically all the apps should show "Instagram" in share tray, i.e. it can be seen in Google Photos app.
public void NativeShareImage(UIView sourceView, CGRect sourceRect,
UIImage image, string shareCaption, string emailSubject)
{
string filename = Path.Combine(FileSystemUtils.GetTemporaryDataPath(), "Image.jpg");
NSError err = null;
using(var imgData = image.AsJPEG(JpgImageQuality))
{
if(imgData.Save(filename, false, out err))
{
Logger.Information("Image saved before native share as {FileName}", filename);
}
else
{
Logger.Error("Image NOT saved before native share as to path {FileName}. {Error}", filename, err.Description);
return;
}
}
// this are the items that needs to be shared
// Instagram ignores the caption, that is known
var activityItems = new List<NSObject>
{
new NSString(shareCaption),
new NSUrl(new Uri(filename).AbsoluteUri)
};
// Here i add the custom UIActivity for Instagram
UIActivity[] applicationActivities =
{
new InstagramActivity(image, sourceRect, sourceView),
}
var activityViewController = new UIActivityViewController(activityItems.ToArray(), applicationActivities);
activityViewController.SetValueForKey(new NSString(emailSubject), new NSString("subject"));
activityViewController.CompletionWithItemsHandler = (activityType, completed, returnedItems, error) =>
{
UserSharedTo(activityType, completed);
};
// Hide some of the less used activity types so that Instagram shows up in the list. Otherwise it's pushed off the activity view
// and the user has to scroll to see it.
activityViewController.ExcludedActivityTypes = new[] { UIActivityType.AssignToContact, UIActivityType.CopyToPasteboard, UIActivityType.Print };
if(UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone)
{
PresentViewController(activityViewController, true, null);
}
else
{
activityViewController.ModalPresentationStyle = UIModalPresentationStyle.Popover;
PresentViewController(activityViewController, true, null);
// Get the popover presentation controller and configure it.
UIPopoverPresentationController presentationController = activityViewController.PopoverPresentationController;
presentationController.PermittedArrowDirections = UIPopoverArrowDirection.Down;
presentationController.SourceRect = sourceRect;
presentationController.SourceView = sourceView;
}
}
// when opening custom activity use ".igo" to only show instagram
public class InstagramActivity : UIActivity
{
public InstagramActivity(UIImage imageToShare, CGRect frame, UIView view, string shareCaption = "")
{
_ImageToShare = imageToShare;
_Frame = frame;
_View = view;
}
public override UIImage Image { get { return UIImage.FromBundle("Instagram"); } }
public override string Title { get { return "Instagram"; } }
public override NSString Type { get { return new NSString("PostToInstagram"); } }
public string Caption { get; set; }
public override bool CanPerform(NSObject[] activityItems)
{
return UIApplication.SharedApplication.CanOpenUrl(NSUrl.FromString("instagram://app"));
}
public override void Prepare(NSObject[] activityItems)
{
}
public override void Perform()
{
string filename = Path.Combine(FileSystemUtils.GetTemporaryDataPath(), "Image.igo");
NSError err = null;
using(var imgData = _ImageToShare.AsJPEG(JpgImageQuality))
{
if(imgData.Save(filename, false, out err))
{
Logger.Information("Instagram image saved as {FileName}", filename);
}
else
{
Logger.Error("Instagram image NOT saved as to path {FileName}. {Error}", filename, err.Description);
Finished(false);
return;
}
}
var url = NSUrl.FromFilename(filename);
_DocumentController = UIDocumentInteractionController.FromUrl(url);
_DocumentController.DidEndSendingToApplication += (o, e) => Finished(true);
_DocumentController.Uti = "com.instagram.exclusivegram";
if(!string.IsNullOrEmpty(ShareCaption))
{
_DocumentController.Annotation = NSDictionary.FromObjectAndKey(new NSString(ShareCaption), new NSString("InstagramCaption"));
}
_DocumentController.PresentOpenInMenu(_Frame, _View, true);
}
UIImage _ImageToShare;
CGRect _Frame;
UIView _View;
UIDocumentInteractionController _DocumentController;
}

UIImagePickerController is showing last clicked image. I am using Monotouch/Xamarin Studio

In my project, when I launch camera first time it works fine. when I launch camera second time, I see image last clicked in view finder. I am not sure what's causing this.
Can anyone please help me here?
following is code block to launch camera:
UIImagePickerController imagePicker = new UIImagePickerController();
// Handle media selected.
var documentsDirectory = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
imagePicker.FinishedPickingMedia += (sender, e) => {
UIImage image = (UIImage)e.Info.ObjectForKey(
new NSString("UIImagePickerControllerOriginalImage"));
if (image != null)
{
this.InvokeOnMainThread(() => {
this.clickedImage.Image = image;
image.SaveToPhotosAlbum(delegate(UIImage img, NSError err){
});
string pngFilename = System.IO.Path.Combine (documentsDirectory, "Photo.png"); // hardcoded filename, overwrites each time
NSData imgData = image.AsPNG();
NSError SaveErr = null;
if (imgData.Save(pngFilename, false, out SaveErr))
{
Console.WriteLine("saved as " + pngFilename);
} else {
Console.WriteLine("NOT saved as" + pngFilename + " because" + SaveErr.LocalizedDescription);
}
});
}
DismissViewController(true,null);
};
// Handle cancellation of picker.
imagePicker.Canceled += (sender, e) => {
DismissViewController(true,null);
};
btnCameraDisplay1.SetTitle("Take Picture", UIControlState.Normal);
btnCameraDisplay1.Font = UIFont.SystemFontOfSize(19);
btnCameraDisplay1.SetTitleColor(UIColor.Black, UIControlState.Normal);
btnCameraDisplay1.TouchUpInside += delegate(object sender, EventArgs e)
{
if(UIImagePickerController.IsSourceTypeAvailable(UIImagePickerControllerSourceType.Camera))
{
imagePicker.SourceType = UIImagePickerControllerSourceType.Camera;
imagePicker.AllowsEditing = false;
this.PresentViewController(imagePicker, true,null);
}
else{
alertView = new UIAlertView();
alertView.AddButton("OK");
alertView.Message = "No camera available in this device.";
alertView.AlertViewStyle = UIAlertViewStyle.Default;
alertView.Show();
}
};
I ran into the same Problem. Some searching here on Stackoverflow helped:
It might be a problem with Xamarins implementation of the FinishedPickingMedia Event. (from here https://stackoverflow.com/a/20503817/383658)
Solution:
Switch from the Event System to Delegates as explained here:
https://stackoverflow.com/a/20035698/383658
So basically move your code from:
imagePicker.FinishedPickingMedia += (sender, e) => {}
to your new UIImagePickerControllerDelegate delegates method:
public override void FinishedPickingMedia (UIImagePickerController picker, NSDictionary info)
{}

AVCaptureSession CMSampleBuffer issue

I have a problem with releasing memory from CMSampleBuffer in my AVCaptureSession camera.
This is my code for setting capture session. If I dispose imageDataSampleBuffer, app crashes.
using MonoTouch.CoreVideo;
using MonoTouch.CoreMedia;
using MonoTouch.AVFoundation;
using MonoTouch.ImageIO;
using MonoTouch.UIKit;
using MonoTouch.CoreFoundation;
using MonoTouch.Foundation;
using System.Drawing;
using System;
namespace myNamespace
{
public class AVFoundationCamera : UIViewController
{
public AVFoundationCamera (CameraController parView)
{
parentView = parView;
}
NSError error;
AVCaptureSession session;
AVCaptureDevice device;
AVCaptureDeviceInput input;
AVCaptureStillImageOutput output;
AVCaptureVideoPreviewLayer captureVideoPreviewLayer;
NSDictionary outputSettings;
AVCaptureConnection captureConnection;
UIButton buttCaptureImage;
public UIImageView imageV;
NSData imageData;
CameraController parentView;
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
CreateControls();
SetupSession();
}
public override void DidReceiveMemoryWarning ()
{
imageData.Dispose();
session.Dispose();
device.Dispose();
input.Dispose();
output.Dispose();
captureVideoPreviewLayer.Dispose();
base.DidReceiveMemoryWarning ();
}
private void CreateControls()
{
imageV = new UIImageView(new RectangleF(0, 0, UIScreen.MainScreen.ApplicationFrame.Width, UIScreen.MainScreen.ApplicationFrame.Height - UIApplication.SharedApplication.StatusBarFrame.Height));
View.AddSubview(imageV);
buttCaptureImage = UIButton.FromType(UIButtonType.RoundedRect);
buttCaptureImage.Frame = new RectangleF(0, 60, 150, 30);
buttCaptureImage.SetTitle("Take a photo", UIControlState.Normal);
buttCaptureImage.TouchUpInside += HandleButtCaptureImageTouchUpInside;
View.AddSubview(buttCaptureImage);
}
void HandleButtCaptureImageTouchUpInside (object sender, EventArgs e)
{
captureConnection = null;
foreach (AVCaptureConnection capConn in output.Connections)
{
foreach (AVCaptureInputPort port in capConn.inputPorts)
{
if (port.MediaType == AVMediaType.Video)
{
captureConnection = capConn;
break;
}
}
if (captureConnection != null)
break;
}
output.CaptureStillImageAsynchronously(captureConnection, HandleAVCaptureCompletionHandlercompletionHandler);
buttCaptureImage.Enabled = false;
}
void HandleAVCaptureCompletionHandlercompletionHandler (CMSampleBuffer imageDataSampleBuffer, NSError error)
{
try
{
using (var pool = new NSAutoreleasePool ()) {
imageData = AVCaptureStillImageOutput.JpegStillToNSData(imageDataSampleBuffer);
//imageDataSampleBuffer.Dispose();
parentView.DismissModalViewControllerAnimated(true);
parentView.HandlePickedImage(imageData);
session.StopRunning();
}
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
}
private void SetupSession()
{
session = new AVCaptureSession();
session.BeginConfiguration();
session.SessionPreset = AVCaptureSession.PresetPhoto;
captureVideoPreviewLayer = new AVCaptureVideoPreviewLayer(session);
captureVideoPreviewLayer.Frame = imageV.Bounds;
imageV.Layer.AddSublayer(captureVideoPreviewLayer);
device = AVCaptureDevice.DefaultDeviceWithMediaType(AVMediaType.Video);
input = new AVCaptureDeviceInput(device, out error);
session.AddInput(input);
output = new AVCaptureStillImageOutput();
output.OutputSettings = NSDictionary.FromObjectAndKey(new NSString("AVVideoCodecKey"), new NSString("AVVideoCodecJPEG"));
session.AddOutput(output);
session.CommitConfiguration();
session.StartRunning();
}
}
}
This is just a regular camera for taking photos. I tried with UIImagePickerController you posted here: https://github.com/migueldeicaza/TweetStation/blob/master/TweetStation/UI/Camera.cs which eliminates UIImagePickerController bug but whenever I click "Take a photo" button, preview window shows up which allocates memory. If I press "Retake", memory is being released, but in FinishedPiCkingMedia event handler I can't release it. So, after few photos it crashes.
Any solution works for me, but it would be great to see what I'm doing wrong.
Thank you once again.
This was a bug in MonoTouch.
There is a workaround you can use until you get the fix:
[DllImport ("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
extern static void CFRetain (IntPtr handle);
void HandleAVCaptureCompletionHandlercompletionHandler (CMSampleBuffer imageDataSampleBuffer, NSError error)
{
try {
CFRetain (imageDataSampleBuffer.Handle);
(...)
} finally {
imageDataSampleBuffer.Dispose ();
}
}
I've added a Dispose call, there might be a limited amount of buffers available, and this way you ensure that the app doesn't exhaust them (since it may take a little time before the GC would free it automatically)
Also note that you should remove the workaround once you install a MonoTouch version with the real fix, since you'll leak the buffers otherwise.

Resources