最近尝试用Xamarin开发iOS app的时候发现绑定第三方库的时候比Android麻烦很多。
绑定iOS Framework
首先下载一个iOS Framework:DynamsoftBarcodeReader.framework。
在Visual Studio 2015中创建工程iOS bindings library。
Framework里其实是一个静态链接库,把
DynamsoftBarcodeReader.framework\DynamsoftBarcodeReader
重命名成
DynamsoftBarcodeReader.framework\DynamsoftBarcodeReader.a。
把这个静态库拖入到Visual Studio中会自动生成DynamsoftBarcodeReader.linkwith.cs。参考ObjCRuntime.LinkWithAttribute Class配置这个文件。除了指定平台,这里还需要添加一个dylib依赖库:
using System;
using ObjCRuntime;
[assembly: LinkWith ("DynamsoftBarcodeReader.a", LinkTarget.ArmV7 | LinkTarget.Simulator, ForceLoad = true, LinkerFlags = "-lc++.1")]
创建一个用于定义接口的ApiDefinition.cs。这个文件如果手动创建会很麻烦,而且容易出错。因此可以借助工具Objective Sharpie,这个工具只能在macOS上安装。
装好之后我们可以用命令生成文件:
sharpie -tlm-do-not-submit bind -framework ~/Desktop/DynamsoftBarcodeReader.framework -sdk iphoneos10.2
把文件拷贝到Visual Studio 2015中。打开文件会发现出现很多的错误,有很多的接口定义上面都有一个[Verify] attributes。如果确认转换没有问题,就可以删除,重新编译就可以通过了。这里是我的:
using System;
using CoreGraphics;
using Foundation;
using ObjCRuntime;
using UIKit;
namespace DBRiOS {
[Static]
partial interface Constants
{
// extern double DynamsoftBarcodeReaderVersionNumber;
[Field ("DynamsoftBarcodeReaderVersionNumber", "__Internal")]
double DynamsoftBarcodeReaderVersionNumber { get; }
// extern const unsigned char [] DynamsoftBarcodeReaderVersionString;
[Field ("DynamsoftBarcodeReaderVersionString", "__Internal")]
NSString DynamsoftBarcodeReaderVersionString { get; }
}
// @interface Barcode : NSObject
[BaseType (typeof(NSObject))]
interface Barcode
{
// @property (nonatomic) long format;
[Export ("format")]
nint Format { get; set; }
// @property (copy, nonatomic) NSString * formatString;
[Export ("formatString")]
string FormatString { get; set; }
// @property (copy, nonatomic) NSString * displayValue;
[Export ("displayValue")]
string DisplayValue { get; set; }
// @property (copy, nonatomic) NSData * rawValue;
[Export ("rawValue", ArgumentSemantic.Copy)]
NSData RawValue { get; set; }
// @property (copy, nonatomic) NSArray * cornerPoints;
[Export ("cornerPoints", ArgumentSemantic.Copy)]
NSObject[] CornerPoints { get; set; }
// @property (nonatomic) CGRect boundingbox;
[Export ("boundingbox", ArgumentSemantic.Assign)]
CGRect Boundingbox { get; set; }
// +(long)OneD;
[Static]
[Export ("OneD")]
nint OneD { get; }
// +(long)CODE_39;
[Static]
[Export ("CODE_39")]
nint CODE_39 { get; }
// +(long)CODE_128;
[Static]
[Export ("CODE_128")]
nint CODE_128 { get; }
// +(long)CODE_93;
[Static]
[Export ("CODE_93")]
nint CODE_93 { get; }
// +(long)CODABAR;
[Static]
[Export ("CODABAR")]
nint CODABAR { get; }
// +(long)ITF;
[Static]
[Export ("ITF")]
nint ITF { get; }
// +(long)EAN_13;
[Static]
[Export ("EAN_13")]
nint EAN_13 { get; }
// +(long)EAN_8;
[Static]
[Export ("EAN_8")]
nint EAN_8 { get; }
// +(long)UPC_A;
[Static]
[Export ("UPC_A")]
nint UPC_A { get; }
// +(long)UPC_E;
[Static]
[Export ("UPC_E")]
nint UPC_E { get; }
// +(long)INDUSTRIAL_25;
[Static]
[Export ("INDUSTRIAL_25")]
nint INDUSTRIAL_25 { get; }
// +(long)PDF417;
[Static]
[Export ("PDF417")]
nint PDF417 { get; }
// +(long)DATAMATRIX;
[Static]
[Export ("DATAMATRIX")]
nint DATAMATRIX { get; }
// +(long)QR_CODE;
[Static]
[Export ("QR_CODE")]
nint QR_CODE { get; }
// +(long)UNKNOWN;
[Static]
[Export ("UNKNOWN")]
nint UNKNOWN { get; }
}
// @interface ReadResult : NSObject
[BaseType (typeof(NSObject))]
interface ReadResult
{
// @property (nonatomic) int errorCode;
[Export ("errorCode")]
int ErrorCode { get; set; }
// @property (copy, nonatomic) NSString * errorString;
[Export ("errorString")]
string ErrorString { get; set; }
// @property (nonatomic) NSArray * barcodes;
[Export ("barcodes", ArgumentSemantic.Assign)]
Barcode[] Barcodes { get; set; }
}
// @interface BarcodeReader : NSObject
[BaseType (typeof(NSObject))]
interface BarcodeReader
{
// +(int)DBRERROR_OK;
[Static]
[Export ("DBRERROR_OK")]
int DBRERROR_OK { get; }
// +(int)DBRERROR_UNKNOWN;
[Static]
[Export ("DBRERROR_UNKNOWN")]
int DBRERROR_UNKNOWN { get; }
// +(int)DBRERROR_NOMEMORY;
[Static]
[Export ("DBRERROR_NOMEMORY")]
int DBRERROR_NOMEMORY { get; }
// +(int)DBRERROR_NULL_POINTER;
[Static]
[Export ("DBRERROR_NULL_POINTER")]
int DBRERROR_NULL_POINTER { get; }
// +(int)DBRERROR_LICENSE_INVALID;
[Static]
[Export ("DBRERROR_LICENSE_INVALID")]
int DBRERROR_LICENSE_INVALID { get; }
// +(int)DBRERROR_LICENSE_EXPIRED;
[Static]
[Export ("DBRERROR_LICENSE_EXPIRED")]
int DBRERROR_LICENSE_EXPIRED { get; }
// +(int)DBRERROR_BARCODE_FORMAT_INVALID;
[Static]
[Export ("DBRERROR_BARCODE_FORMAT_INVALID")]
int DBRERROR_BARCODE_FORMAT_INVALID { get; }
// +(int)DBRERROR_PARAMETER_INVALID;
[Static]
[Export ("DBRERROR_PARAMETER_INVALID")]
int DBRERROR_PARAMETER_INVALID { get; }
// -(id)initWithLicense:(NSString *)license;
[Export ("initWithLicense:")]
IntPtr Constructor (string license);
// -(ReadResult *)readSingle:(UIImage *)image barcodeFormat:(long)format;
[Export ("readSingle:barcodeFormat:")]
ReadResult ReadSingle (UIImage image, nint format);
// -(void)readSingleAsync:(UIImage *)image barcodeFormat:(long)format sender:(id)sender onComplete:(SEL)callback;
[Export ("readSingleAsync:barcodeFormat:sender:onComplete:")]
void ReadSingleAsync (UIImage image, nint format, NSObject sender, Selector callback);
// -(ReadResult *)readSingle:(NSData *)buffer width:(int)width height:(int)height barcodeFormat:(long)format;
[Export ("readSingle:width:height:barcodeFormat:")]
ReadResult ReadSingle (NSData buffer, int width, int height, nint format);
// -(void)readSingleAsync:(NSData *)buffer width:(int)width height:(int)height barcodeFormat:(long)format sender:(id)sender onComplete:(SEL)callback;
[Export ("readSingleAsync:width:height:barcodeFormat:sender:onComplete:")]
void ReadSingleAsync (NSData buffer, int width, int height, nint format, NSObject sender, Selector callback);
}
}
编译之后生成DBRiOS.dll。
创建iOS App
创建Single View App。把DBRiOS.dll通过Reference加入到工程里。
添加Button, Lable和UIImageView到Main.storyboard中:
打开ViewController.cs添加代码:
using System;
using UIKit;
using DBRiOS;
namespace BarcodeDemo
{
public partial class ViewController : UIViewController
{
public ViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use.
}
partial void Button_read_TouchUpInside(UIButton sender)
{
BarcodeReader barcodeReader = new BarcodeReader("");
ReadResult result = barcodeReader.ReadSingle(qrimage.Image, Barcode.QR_CODE);
Barcode barcode = result.Barcodes[0];
text.Text = barcode.DisplayValue;
}
partial void UIButton16_TouchUpInside(UIButton sender)
{
text.Text = "";
}
}
遇到的问题
Xamarin Mac Agent连接失败
通过Visual Studio 2015连接Mac,如果碰到上面的问题,那应该是Mac上有些相关的东西没有安装。用Xamarin Installer安装xamarin.ios, xamarin.mac, Xamarin Studio和MonoFramework,再尝试链接应该就好了。
地址无法解析
这个问题比较奇怪,有些时候出现可能是因为Mac自动休眠了,有的时候Build就是找不到Mac。这个时候可以试试能不能调用模拟器,如果模拟器被远程启动了,再重新build工程可能就好了。还是不行就重启下Visual Studio。
添加Native Framework Reference
在Visual Studio中是可以直接添加Framework来绑定的,但是Windows上编译出来的库不能用。Mac上的虽然可以用,但是只能运行在真机上。
这里的主要问题就是没有提供LinkTarget选项,我用的Framework支持armv7, i386, x86_64, 和 arm64,但是编译之后生成的DLL尺寸只有原来的一半。
源码
https://github.com/yushulx/xamarin-bind-ios-framework