●首先,我们用xcode创建一个Obj-C静态库。●之后我们用Xamarin.iOS绑定这个静态库。●接着使用工具 Objective Sharpie自动生成一些(是的,不是全部)必要的API定义,这能减少自己定义的工作量。●最后,我们建立一个Xamarin.iOS项目来使用这个绑定。
●Xcode 6 和 iOS --在苹果机上已经安装Xcode 6和最近的iOS API。●Xcode Command Line Tools --在苹果机上已经安装当前Xcode版本匹配的Xcode命令行工具。(参阅下文的安装细节)●Xamarin Studio 或 Visual Studio --在开发机已安装最新版的Xamarin Studio 或VisualStudio。Xamarin Studio (或Xamarin Build Host) 和一台苹果mac机是开发Xamarin应用必须的。( 译注2)●Objective Sharpie--当前版本的Objective Sharpie可以在 这里下载。
●安装Xcode6 或新版本--在安装X6时,一些命令行工具已捆绑安装。 在OS X 10.9及之后,可以使用xcrun命令。
●使用终端(Terminal Application)-- 可以在终端中使用xcode-select --install命令安装:
打开终端
输入xcode-select --install 并回车
会有安装向导,点击“安装”(Install)
安装包会从苹果服务器下载并安装:
●从Apple Developers下载--Downloads for Apple Developers:
安装完命令行工具后,我们已经准备好继续进行这个演练。
●新建一个静态库--这步包括创建一个 Obj-C格式的静态库项目InfColorPicker ,输出一个.a扩展名的 静态库文件,该文件最终将被嵌入到.Net的类库中。
●新建一个Xamarin绑定项目-- 当完成一个静态库后,我们需要创建一个Xamarin的绑定项目(译注4)。这个绑定项目由之前我们创建的静态库及C#形式的元数据(meta-data)组成,C#的元数据描述了这些Obj-C API能被怎么使用。 这些元数据通常称为API 定义(API definitions)。我们将使用工具Objective Sharpie来帮助我们自动生成这些API定义。
●规范API定义--Objective Sharpie帮助我们做了很多工作,但在API能被使用前我们需要处理些变化--对自动生成的某些API做些调整。
●使用绑定的类库--最后,我们创建一个Xamarin应用来展示怎么使用之前创建好的绑定库。
现在我们已经知道了会涉及哪些步骤,让我们继续前进完成这些步骤。
• InfColorPicker - 这个文件夹包含项目的Objective-C 代码• PickerSamplePad - 这个包含一个iPad的示例。• PickerSamplePhone - 这个包含一个iPhone的示例。
•创建一个ARM 7版本的静态库•创建一个x86 版本的静态库•使用lipo命令将2个库打包到一起
XBUILD=/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild PROJECT_ROOT=./InfColorPicker PROJECT=$(PROJECT_ROOT)/InfColorPicker.xcodeproj TARGET=InfColorPicker all: libInfColorPickerSDK.a libInfColorPicker-i386.a: $(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphonesimulator -configuration Release clean build -mv $(PROJECT_ROOT)/build/Release-iphonesimulator/lib$(TARGET).a $@ libInfColorPicker-armv7.a: $(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphoneos -arch armv7 -configuration Release clean build -mv $(PROJECT_ROOT)/build/Release-iphoneos/lib$(TARGET).a $@ libInfColorPickerSDK.a: libInfColorPicker-i386.a libInfColorPicker-armv7.a xcrun -sdk iphoneos lipo -create -output $@ $^ clean: -rm -f *.a *.dll
1.启动Xamarin Studio2.在“File”菜单下选择新建一个方案(New > Solution...):
3.在新建方案对话框,选择新建一个绑定项目(Library > iOS Binding Project):
4.点击下一步"Next"
5.输入InfColorPickerBinging作为绑定项目的名称,点击"Create"按钮创建方案:
方案被创建并默认已经包含2个文件:
• ApiDefinition.cs -- 这个文件是Obj-C API怎样被包装成C#的约定。
• StructsAndEnums.cs -- 这个文件里列出必要的结构和枚举声明。
1.在刚创建的方案里右击InfColorPicker项目,在弹出菜单选择添加文件Add > Add Files...:
2.定位到 libInfColorPickerSDK.a文件,选中它并点击"Open"按钮
3.选择拷贝文件到文件夹(Copy the file to the directory),点击"OK"按钮
4.文件会被添加到项目里
当这个.a文件被添加到绑定项目,Xamarin.iOS会自动识别出这个Obj-C库,生成一个特定名称的文件libInfColorPickerSDK.linkwith.cs:
这个文件中的LinkWith属性告诉Xamarin该怎么处理这个刚添加的静态库。文件中内容是如下的一段代码:using ObjCRuntime; [assembly: LinkWith ("libInfColorPickerSDK.a", SmartLink = true, ForceLoad = true)]
sharpie -help
Europa:Resources kmullins$sharpie -helpusage: sharpie [OPTIONS] TOOL [TOOL_OPTIONS]
Options:
-h, --helpShow detailed help
-v, --versionShow version information
Available Tools:
xcode Get information about Xcode installations and available SDKs.
bind Create a Xamarin C#binding to Objective-C APIsEuropa:Resources kmullins$
我们会用到 ObjectiveSharpie的如下命令:
要获取 Objective Sharpie具体工具的帮助信息,可以输入工具的名称和 -help命令。 例如输入sharpie xcode -help会得到下列的信息:xcode --这个命令提供一些当前的Xcode、及iOS和MacAPIs的版本信息。当之后我们进行绑定工作时会用到这些信息。bind --我们将使用这个命令来解析InfColorPicker 项目的头文件(.h文件)来初始化ApiDefinition.cs 和StructsAndEnums.cs 文件。
Europa:Resources kmullins$sharpie xcode -helpusage: sharpie xcode [OPTIONS]+
Options:
-h, --help Show detailed help
-v, --verbose Be verbose with output
--sdks List all available Xcode SDKs. Pass -verbose for
more details.
Europa:Resources kmullins$
在我们处理绑定前,需要确认当前安装的一些SDK的信息。在终端输入以下命令sharpie xcode -sdks:
查看上面,可以看到机器上已经安装有 iphoneos8.1 SDK 。根据这个,我们已经准备好解析 InfColorPicker 项目的头文件,并初始生成 ApiDefinition.cs 和 StructsAndEnums.cs文件了。
Europa:Resources kmullins$ sharpie xcode -sdks macosx10.10 macosx10.9 iphoneos8.1 iphonesimulator8.1 iphonesimulator7.1 Europa:Resources kmullins$
查看上面,可以看到机器上已经安装有 iphoneos8.1 SDK 。根据这个,我们已经准备好解析 InfColorPicker 项目的头文件,并初始生成 ApiDefinition.cs 和 StructsAndEnums.cs文件了。
在终端输入下面的命令:
sharpie bind --output=InfColorPicker --namespace=InfColorPicker --sdk=iphoneos8.1 [full-path-to-project]/InfColorPicker/InfColorPicker/*.h
[full-path-to-project]是你机器上 InfColorPicker 项目的Xcode 项目文件(.xcodepro)所在的路径。这个例子中,我们使用了*.h的通配符来解析文件夹下的所有头文件。通常你可以不用这样处理,你可以使用一个顶级的头文件,并在里面正确引用所有相关的头文件,然后只要把这个顶级头文件指定给Objective Sharpie就可以。
终端上会有如下输出:
Europa:Resources kmullins$sharpie bind -output InfColorPicker -namespace InfColorPicker -sdk iphoneos8.1 /Users/kmullins/Projects/InfColorPicker/InfColorPicker/InfColorPicker.h -unifiedCompiler configuration:
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk -miphoneos-version-min=8.1 -resource-dir /Library/Frameworks/ObjectiveSharpie.framework/Versions/1.1.1/clang-resources -arch armv7 -ObjC
[ 0%] parsing /Users/kmullins/Projects/InfColorPicker/InfColorPicker/InfColorPicker.h
In file included from /Users/kmullins/Projects/InfColorPicker/InfColorPicker/InfColorPicker.h:60:
/Users/kmullins/Projects/InfColorPicker/InfColorPicker/InfColorPickerController.h:28:1: warning: no 'assign','retain', or 'copy' attribute is specified - 'assign' is assumed [-Wobjc-property-no-attribute]@property (nonatomic) UIColor* sourceColor;
^
/Users/kmullins/Projects/InfColorPicker/InfColorPicker/InfColorPickerController.h:28:1: warning: default property
attribute 'assign' not appropriate for non-GC object [-Wobjc-property-no-attribute]/Users/kmullins/Projects/InfColorPicker/InfColorPicker/InfColorPickerController.h:29:1: warning: no 'assign','retain', or 'copy' attribute is specified - 'assign' is assumed [-Wobjc-property-no-attribute]@property (nonatomic) UIColor* resultColor;
^
/Users/kmullins/Projects/InfColorPicker/InfColorPicker/InfColorPickerController.h:29:1: warning: default property
attribute 'assign' not appropriate for non-GC object [-Wobjc-property-no-attribute]4 warnings generated.
[100%] parsing complete
[bind] InfColorPicker.cs
Europa:Resources kmullins$
同时,在我们的文件夹下,InfColorPicker.enums.cs 和InfColorPicker.cs文件已被创建 :
在Xamarin中打开这2个文件。复制InfColorPicker.cs 中的内容到ApiDefinition.cs ,即用InfColorPicker.cs中namespace开始的代码段替换掉现有的。(using引用声明不用替换)。
目前版本的 Objective Sharpie有时候会创建重复的接口定义,所以我们要找到第一个InfColorPickerControllerDelegate定义删除它。
InfColorPicker 会用到些CoreGraphics的类,需要添加CoreGraphics 的引用:
using CoreGraphics;
这个版本的Objective Sharpie翻译委托定义(Delegate)时有时会有问题,所以要用下面的的代码替换掉InfColorPickerControllerDelegate接口定义部分的[Protocol, Model] 行。
[BaseType(typeof(NSObject))] [Model]
替换后的定义如下:
类似的,我们操作InfColorPicker.enums.cs文件。 除了using引用部分,复制所有内容到StructsAndEnums.cs:
这个版本的Objective Sharpie 还不能创建一个枚举类型的名称(醉了)。我们需要替换掉 <unamed-C-enum>,比如这里使用InfComponentIndex:
到这步,我们的绑定项目已经完成了。让我们编译一下看看有无错误。
2.添加对绑定项目的引用-使InfColorPickerSample 项目引用InfColorPickerBinding项目。
3.创建iPhone的UI-双击InfColorPickerSample项目的MainStoryboard.storyboard,在iOS设计界面添加一个叫 ChangeColorButton的按钮控件(Button),如下图:
4.添加 InfColorPickerView.xib --在InfColorPicker 的Obj-C库中包含(include)一个.xib文件,但Xamarin并未把它包含进绑定项目,这会导致我们运行示例程序时错误。解决办法是把这个.xib添加到我们的Xamarin项目。 右击我们的Xamarin项目,选择Add > Add Files,并定位到这个.xib文件。如下图:
5.在对话框中选择拷贝这个文件到项目
接下来,让我们快速浏览下Obj-C中的协议(Protocols 译注7) 是怎么被C#代码解析绑定的。
@protocol InfColorPickerControllerDelegate @optional - (void) colorPickerControllerDidFinish: (InfColorPickerController*) controller; // This is only called when the color picker is presented modally. - (void) colorPickerControllerDidChangeColor: (InfColorPickerController*) controller; @end
[BaseType(typeof(NSObject))] [Model] public partial interface InfColorPickerControllerDelegate { [Export ("colorPickerControllerDidFinish:")] void ColorPickerControllerDidFinish (InfColorPickerController controller); [Export ("colorPickerControllerDidChangeColor:")] void ColorPickerControllerDidChangeColor (InfColorPickerController controller); }
• 强委托(Strong Delegate)-- 使用强委托是指创建一个继承了InfColorPickerControllerDelegate的子类,并且实现(override)相应的方法。InfColorPickerController 将使用这个类的一个实例来与它的客户沟通。强委托是可感知(可推断),类型安全,及封装良好的。出于这些原因,我们应该尽可能的使用强委托来代替弱委托。
•弱委托(Weak Delegate) -- 一个弱委托是种稍微不同技术方法,它是指在某个类中(比如InfColorPickerSampleViewController)创建一个公共方法并通过添加Export属性把该方法指定(exposing)给InfColorPickerDelegate协议。
在这个演练中,2种方法我们都会讨论到。 让我们从实现一个强委托开始,再完成一个弱委托。
using InfColorPickerBinding; using UIKit; namespace InfColorPickerSample { public class ColorSelectedDelegate:InfColorPickerControllerDelegate { readonly UIViewController parent; public ColorSelectedDelegate (UIViewController parent) { this.parent = parent; } public override void ColorPickerControllerDidFinish (InfColorPickerController controller) { parent.View.BackgroundColor = controller.ResultColor; parent.DismissViewController (false, null); } } }
新建ColorSelectedDelegate的实例-- 我们的事件处理程序需要一个实例,类型是之前我们创建的ColorSelectedDelegate。编辑类InfColorPickerSampleViewController ,添加一个如下的实例变量:
ColorSelectedDelegate selector;
public override void ViewDidLoad () { base.ViewDidLoad (); ChangeColorButton.TouchUpInside += HandleTouchUpInsideWithStrongDelegate; selector = new ColorSelectedDelegate (this); }
using InfColorPicker; ... private void HandleTouchUpInsideWithStrongDelegate (object sender, EventArgs e) { InfColorPickerController picker = InfColorPickerController.ColorPickerViewController(); picker.Delegate = selector; picker.PresentModallyOverViewController (this); }
运行程序
到这里我们已经完成了所有功能代码,如果你运行程序,你可以设置背景色如下:
恭喜!你已经实现了在一个Xamarin应用中调用一个Obj-C类库。 接着,我们可以学习下弱委托的使用。
为TouchUpInside创建事件处理
让我们给设置背景色的按钮的TouchUpInside 事件添加事件处理。这个处理和之前章节创建的HandleTouchUpInsideWithStrongDelegate 有相同的职能,不过我们将使用弱委托来替换强委托。
编辑 ViewController类并添加如下的方法:
private void HandleTouchUpInsideWithWeakDelegate (object sender, EventArgs e) { InfColorPickerController picker = InfColorPickerController.ColorPickerViewController(); picker.WeakDelegate = this; picker.SourceColor = this.View.BackgroundColor; picker.PresentModallyOverViewController (this); }
public override void ViewDidLoad () { base.ViewDidLoad (); ChangeColorButton.TouchUpInside += HandleTouchUpInsideWithWeakDelegate; }
[Export("colorPickerControllerDidFinish:")] public void ColorPickerControllerDidFinish (InfColorPickerController controller) { View.BackgroundColor = controller.ResultColor; DismissViewController (false, null); }
译注1:strong delegate在这里使用了C#中更熟悉的"委托"的叫法,但在iOS更强调的是引用的意思。weak delegate类似。
译注2:本文中的Xamarin.iOS是指在苹果机上的Xamarin环境,其实在 windows环境--Xamarin Studio或VS Xamarin中,你可以直接做类似的操作,如在VS里新建一个Xamarin binding project。
译注3:.Net程序员可能对makefile不熟悉,可以理解为一段脚本命令或一个批处理文件,其中会通过命令的方式调用IDE的编译器。在苹果机上可以通过文本软件创建,也有很多插件工具,或者,在终端直接输入命令:touch Makefile。更多信息请搜索makefil相关。
译注4:你同样可以在Windows环境创建绑定项目。
译注5:Obj-C中.h是头文件,.m文件是具体的实现内容。
译注6:可以直接指定具体的路径,也可以使用cd定位到文件夹,使用相对路径。
译注7:Obj-C中的Interface关键字是对类的声明,Protocols关键字 才更接近C#中的interface。