NSItemProviderReading on Xamarin - ios

To do files drag & drop for files in iOS 11 you need to implement NSItemProviderReading, there is the sample code for swift here: iOS 11 dropInteraction performDrop for files
However, how to do that in Xamarin, I guess the class definition should look like this, but how to implement the methods?
public class DocumentProvider : NSObject, INSItemProviderReading
{
}

You can implement the NSItemProviderReading like this:
class MyItemProvider : UIView, INSItemProviderReading {
[Export ("readableTypeIdentifiersForItemProvider")]
public static string [] ReadableTypeIdentifiersForItemProvider => new string [] { "public.image", "public.data" }
[Export ("objectWithItemProviderData:typeIdentifier:error:")]
public static MyItemProvider GetObject (NSData data, string typeIdentifier, out NSError outError)
{
outError = null;
switch (typeIdentifier) {
case "public.image": return new ...;
case "public.data": return new ...;
default:
outError = new NSError (...);
return null;
}
}
}
Reference:NSItemProviderReading requirements.

Related

Objective c binding not loading in xamarin forms ios

I am building my first objective c library binding. I am following this documentation from Microsoft. I have successfully created a fat binary from IronSource.framework and have also generated apidefinition and structs using objective sharpie and added my binary to it.
After doing that I added binding library to my xamarin.ios project. when i try to create a new instance of a class from the exposed API I get an error.
The type or namespace name 'IronSource' could not be found (are you missing a using directive or an assembly reference?) Stock.iOS D:\Backup Version for Stock Adviser\Version 1.6\Version code 37\StockAdviserCode\Stock\Stock\Stock.iOS\AdControlViewRenderer.cs
'AdControlViewRenderer.BannerWrapper' does not implement inherited
abstract member
'ISBannerDelegate.DidClickBanner()' Stock.iOS D:\Backup Version for
Stock Adviser\Version 1.6\Version code
37\StockAdviserCode\Stock\Stock\Stock.iOS\AdControlViewRenderer.cs
I think my objective c library and xamarin.ios project are not linking properly
My Binding project name is IronSource and my xamarin forms ios project name is Stock.iOS
//APIDefinition
using System;
using Foundation;
using ObjCRuntime;
using UIKit;
namespace IronSource
{
// #interface ISBannerView : UIView
[BaseType(typeof(UIView))]
interface ISBannerView
{
}
// #protocol ISBannerDelegate <NSObject>
[BaseType(typeof(NSObject))]
[Model]
interface ISBannerDelegate
{
// #required -(void)bannerDidLoad:(ISBannerView *)bannerView;
[Abstract]
[Export("bannerDidLoad:")]
void BannerDidLoad(ISBannerView bannerView);
// #required -(void)bannerDidFailToLoadWithError:(NSError *)error;
[Abstract]
[Export("bannerDidFailToLoadWithError:")]
void BannerDidFailToLoadWithError(NSError error);
// #required -(void)didClickBanner;
[Abstract]
[Export("didClickBanner")]
void DidClickBanner();
// #required -(void)bannerWillPresentScreen;
[Abstract]
[Export("bannerWillPresentScreen")]
void BannerWillPresentScreen();
// #required -(void)bannerDidDismissScreen;
[Abstract]
[Export("bannerDidDismissScreen")]
void BannerDidDismissScreen();
// #required -(void)bannerWillLeaveApplication;
[Abstract]
[Export("bannerWillLeaveApplication")]
void BannerWillLeaveApplication();
}
}
//my xamarin.ios
using System;
using CoreGraphics;
using Foundation;
using Stock.iOS;
using Stock.Services;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using IronSource;
[assembly: ExportRenderer(typeof(AdControlView), typeof(AdControlViewRenderer))]
namespace Stock.iOS
{
public class AdControlViewRenderer : ViewRenderer
{
public AdControlViewRenderer()
{ }
protected AdControlView AdViewControl => (AdControlView)Element;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || Element == null)
return;
IronSource ironSource = new IronSource();
SetNativeControl();
}
private void SetNativeControl()
{
try
{
BannerWrapper bannerWrapper = new BannerWrapper(ViewController);
IronSource.SetBannerDelegate(bannerWrapper);
var bannerSize = new ISBannerSize("BANNER");
IronSource.LoadBannerWithViewController(ViewController, bannerSize);
var adview = bannerWrapper.BannerView();
SetNativeControl(adview);
}
catch (Exception ex)
{
}
}
}
public class BannerWrapper : ISBannerDelegate
{
readonly UIViewController parent;
ISBannerView bannerView = null;
public bool DestroyBanner()
{
if (bannerView != null)
{
IronSource.DestroyBanner(bannerView);
bannerView = null;
return true;
}
return false;
}
public BannerWrapper(UIViewController viewController)
{
this.parent = viewController;
}
public override void BannerDidClick()
{
}
public override void BannerDidDismissScreen()
{
}
public override void BannerDidFailToLoadWithError(NSError error)
{
}
public ISBannerView BannerView()
{
ISBannerView bannerView = new ISBannerView();
nfloat y = this.parent.View.Frame.Size.Height - (bannerView.Frame.Size.Height / 2);
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
y -= this.parent.View.SafeAreaInsets.Bottom;
}
bannerView.Center = new CGPoint(this.parent.View.Frame.Size.Width / 2, y);
return bannerView;
}
public override void BannerDidLoad(ISBannerView bnView)
{
InvokeOnMainThread(() =>
{
bannerView = bnView;
nfloat y = this.parent.View.Frame.Size.Height - (bannerView.Frame.Size.Height / 2);
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
y -= this.parent.View.SafeAreaInsets.Bottom;
}
bannerView.Center = new CGPoint(this.parent.View.Frame.Size.Width / 2, y);
this.parent.View.AddSubview(bannerView);
bannerView.AccessibilityLabel = "bannerContainer";
});
}
public override void BannerWillLeaveApplication()
{
}
public override void BannerWillPresentScreen()
{
}
}
}
Any help is welcome
So, I was able to make it work by adding fat binary to Xamarin.ios and changed the build type from "Do not copy" to "Copy Always" and then added a linker to that fat binary using
-cxx -gcc_flags "-L${ProjectDir} -lIronSource -force_load ${ProjectDir}/IronSource.a
I used this GitHub repo for guidance.
Overall to setup IronSoruce, I had to add dll in references, native framework to native reference, fat binary to projectdir and add linker to fat binary.

Unity app crashes when built for iOS with camera enabled

I have an app which uses zxing to scan qr codes in the app. However when I build the app with these scripts in the scene the app crashes on startup. I thought it was something in the Awake() or Start() but I've wrapped those methods in a try catch, and even then I'm not getting any errors, and it doesn't crash on android and in the editor.
I don't have access to a Mac, and am using Unity Cloud Build to build it.
I also don't know how to enable permissions, I thought I did when creating the .p12 file, but I've also found that there's an info.plist file that I have to request permissions with.
Prior research I found this Unity Question about adding items to the Xcode project but not only did including the xcodeapi give me errors, but the using statements didn't work.
There are two scripts
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System;
public class WebCamController : MonoBehaviour {
public int desiredWidth = 1280;
public int desiredHeight = 720;
public int desiredFPS = 60;
public RawImage output;
[HideInInspector]
public WebCamTexture webcamTexture;
void Start ()
{
webcamTexture = new WebCamTexture(desiredWidth, desiredHeight, desiredFPS);
output.texture = webcamTexture;
Play();
}
public void Play()
{
webcamTexture.Play();
}
public void Pause()
{
webcamTexture.Stop();
}
}
and
using UnityEngine;
using System.Collections;
using ZXing;
using ZXing.QrCode;
using ZXing.Common;
using System;
public class CodeScanner : MonoBehaviour {
private static CodeScanner _instance;
public static CodeScanner Instance
{
get
{
if(null == _instance)
{
Debug.Log("Code Scanner Instance not found");
}
return _instance;
}
}
[Header("References")]
public WebCamController wcc;
[Header("Properties")]
private BarcodeReader codeScanner;
private string lastScanned = "";
public delegate void Found(string text, string type);
public event Found OnCodeScanned;
private bool active;
public void Awake()
{
_instance = this;
}
void Start () {
codeScanner = new BarcodeReader();
StartCoroutine(ReadCode());
wcc.Play();
}
IEnumerator ReadCode()
{
while (active)
{
try
{
var data = codeScanner.Decode(wcc.webcamTexture.GetPixels32(), wcc.webcamTexture.width, wcc.webcamTexture.height);
if (data != null)
{
//if (data.Text != lastScanned)
//{
OnCodeScanned(data.Text, data.BarcodeFormat.ToString());
//}
lastScanned = data.Text;
}
}
catch(Exception e)
{
}
yield return new WaitForSeconds(1.0f);
}
}
public void Activate()
{
wcc.Play();
active = true;
StartCoroutine(ReadCode());
}
public void Stop()
{
active = false;
wcc.Pause();
}
}
My device is added properly to the .p12 certificate I can compile and run the program without these scripts in the scene.

How to pass arguments from iOS (Objc/Swift) to Unity that are structs/classes? (without using UnitySendMessage)

I am trying to find a good way to pass parameters from my native unity plugin in objc/swift to C# unity.
It seems that I will need to use Marshall but I couldn't find a good example of this anywhere.
The only thing I did found is the UnitySendMessage, but that passes strings only as parameters, and even those are limited to 1024 bytes, which is not enough for a JSON representation of the objects, and handling multiple messages seems a bit of an overkill for this.
The idea is to be able to question the plugin for object detection from a MTLTexture and return the objects that are recognised.
Code samples:
SwiftBridge
import Foundation
import UIKit
import Vision
#objc public class SwiftBridge: NSObject {
var delegate: DelegateCallbackFunction?
#objc static let shared = SwiftBridge()
#objc func evaluate(texture: MTLTexture) -> Bool {
guard let delegate = self.delegate else {
return false
}
let rect = CGRect(x: 1, y: 2, width: 100, height: 200)
delegate(rect)
return true
}
#objc func setDelegate(callback: #escaping DelegateCallbackFunction) -> Bool {
self.delegate = callback
return true
}
}
Unity
using System;
using UnityEngine;
using System.Runtime.InteropServices;
using AOT;
[StructLayout(LayoutKind.Sequential)]
public struct CGPoint {
public float x;
public float y;
};
[StructLayout(LayoutKind.Sequential)]
public struct CGSize {
public float width;
public float height;
};
[StructLayout(LayoutKind.Sequential)]
public struct CGRect {
public CGPoint origin;
public CGSize size;
}
public class UnityBridge : MonoBehaviour {
#region Declare external C interface
// #if UNITY_IOS && !UNITY_EDITOR
[DllImport("__Internal")]
private static extern int _vision_detectObjectsIn(IntPtr texture);
delegate bool ObjectDetectedCallback(ref CGRect rect);
[DllImport("__Internal")]
private static extern void _vision_setDelegate(ObjectDetectedCallback callback);
[MonoPInvokeCallback(typeof(ObjectDetectedCallback))]
private static bool delegateMessageReceived(ref CGRect rect) {
Debug.Log("Message received: " + rect.origin.x);
return true;
}
// #endif
#endregion
public void initializeDelegate() {
if (Application.platform == RuntimePlatform.IPhonePlayer) {
_vision_setDelegate(delegateMessageReceived);
}
}
#region Wrapped methods and properties
public void EvaluateTexture(IntPtr texture) {
initializeDelegate();
if (texture == IntPtr.Zero) {
Debug.LogError("[Texture] Pointer to buffer is null.");
return;
}
bool success;
#if UNITY_IOS && !UNITY_EDITOR
_vision_detectObjectsIn(texture);
#endif
}
#endregion
#region Singleton implementation
private static WeRDetectorUnity _instance;
public static WeRDetectorUnity Instance {
get {
if (_instance == null) {
var obj = new GameObject("WeRDetectorUnity");
_instance = obj.AddComponent<WeRDetectorUnity>();
}
return _instance;
}
}
void Awake() {
if (_instance != null) {
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
}
#endregion
}
The message receive print in Unity does not return a 1 as it should but rather a strange exponent small number.
Any idea??
Blockquote
You need to create struct for pass parameter its you can use parameters on multiple time and also access in main class.
struct Location {
let latitude:"latitude"
let longitude: "longitude"
}

Xamarin iOS, how to save a video and play it from a file

I am able to record a video and save it with:
var doc = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
string filename = Path.GetRandomFileName () + ".mov";
var videopath = Path.Combine (doc, filename);
^ That's the video path I am saving it to but I can't find how to play the video from the file.
mp = new MPMoviePlayerController(NSUrl.FromFilename("Movies/Showhouzreel2music.m4v"));
^ I have tried this and can see this video file and play it, but this video was placed by me and won't work for the video I have taken.
I would love any directions or help you guys can give.
Thank you and if you need any more information, feel free to ask.
You said you can get MPMoviePlayerController working with a file you placed?
If so then just try this.
string newVideoPath = Path.GetFileName(videopath);
mp = new MPMoviePlayerController(NSUrl.FromFilename(newVideoPath));
EDIT (More elaborate answer):
Implement something like this.
public void PlayVideo(string videopath)
{
string name = Path.GetFileName(videopath);
Device.BeginInvokeOnMainThread(() =>
{
QLPreviewItemFileSystem prevItem = new QLPreviewItemFileSystem(name, videopath);
QLPreviewController previewController = new QLPreviewController();
previewController.DataSource = new PreviewControllerDS(prevItem);
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(previewController, true, null);
});
}
public class PreviewControllerDS : QLPreviewControllerDataSource
{
private QLPreviewItem _item;
public PreviewControllerDS(QLPreviewItem item)
{
_item = item;
}
public override IQLPreviewItem GetPreviewItem(QLPreviewController controller, nint index)
{
return _item;
}
public override nint PreviewItemCount(QLPreviewController controller)
{
return 1;
}
}
public class QLPreviewItemFileSystem : QLPreviewItem
{
string _fileName, _filePath;
public QLPreviewItemFileSystem(string fileName, string filePath)
{
_fileName = fileName;
_filePath = filePath;
}
public override string ItemTitle
{
get
{
return _fileName;
}
}
public override NSUrl ItemUrl
{
get
{
return NSUrl.FromFilename(_filePath);
}
}
}
And whenever you need to call it use this:
PlayVideo(videopath);
This will also allow you to open up other files should you choose to in the future. (Like pdfs, images, etc.) Note: You might have to modify some code to get it to work, as I haven't tested this yet.

MvvmLight unable to create a controller for key

I am designing a cross platform application architecture using Xamarin iOS and Xamarin Android I decided to go with MvvmLight, it looks descent and is not hiding everything from the MVVM pattern, good and flexible.
While everything started to make sense trying to set it up and learn how to use it, I find myself difficult to understand why I get the following error.
Unable to create a controller for key ChartsPage
The setup.
In a PCL I have my ViewModels. I have a ViewModelLocator setup. I use the mvvmlightlibs Nuget Package.
public class ViewModelLocator
{
public static readonly string SchedulerPageKey = #"SchedulerPage";
public static readonly string ChartsPageKey = #"ChartsPage";
[SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public SchedulerViewModel Scheduler
{
get
{
return ServiceLocator.Current.GetInstance<SchedulerViewModel>();
}
}
public BizchartsViewModel Bizcharts
{
get
{
return ServiceLocator.Current.GetInstance<BizchartsViewModel>();
}
}
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
// Haven't declared something yet
}
else
{
// Haven't declared something yet
}
SimpleIoc.Default.Register<SchedulerViewModel>();
SimpleIoc.Default.Register<BizchartsViewModel>();
}
}
The I have a unified iOS application using universal storyboard with size classes which has an initial UINavigationViewController SchedulerViewController and in the ViewDidLoad method I test the navigation to BizchartsViewController with 3 seconds delay. After 3 seconds I get the exceptions.
In the AppDelegate.
private static ViewModelLocator _locator;
public static ViewModelLocator Locator
{
get
{
if (_locator == null)
{
SimpleIoc.Default.Register<IDialogService, DialogService>();
_locator = new ViewModelLocator();
}
return _locator;
}
}
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
var nav = new NavigationService();
nav.Initialize((UINavigationController)Window.RootViewController);
nav.Configure(ViewModelLocator.ChartsPageKey, typeof(BizchartsViewController));
SimpleIoc.Default.Register<INavigationService>(() => nav);
return true;
}
The SchedulerViewController.
partial class SchedulerViewController : UIViewController
{
public SchedulerViewModel Vm {
get;
private set;
}
public SchedulerViewController (IntPtr handle) : base (handle)
{
Vm = AppDelegate.Locator.Scheduler;
}
public async override void ViewDidLoad ()
{
base.ViewDidLoad ();
await Task.Delay (3000);
Vm.NavigateToCharts ();
}
}
The SchedulerViewModel.
public class SchedulerViewModel : ViewModelBase
{
public void NavigateToCharts()
{
var nav = ServiceLocator.Current.GetInstance<INavigationService>();
nav.NavigateTo(ViewModelLocator.ChartsPageKey);
}
}
I definitely miss a detail somewhere!!!
If you follow carefully the blog post here, it says that with Storyboard you should use the string overload and not the typeof() in nav.Configure(Key, ViewController) and always set the storyboardId and restorationId in the Storyboard ViewController.
Note that because we are using a Storyboard, you must make sure to use
the Configure(string, string) overload, and NOT the Configure(string,
Type) one.

Resources