2014

51
IOS 插插插插

Upload: jorden-guzman

Post on 03-Jan-2016

39 views

Category:

Documents


7 download

DESCRIPTION

IOS 插件开发培训. 2014. 背景. 为了满足更多地应用场景, ExMobi5.0 版本开始支持原生开发。它是以插件的形式集成到 ExMobi 中, 提供给应用开发人员进行调用。 右 图为通讯录插件和仿 zaker 效果菜单插件的效果图。. 实例. 以简单的 slider 插件控件为例,来讲述 IOS 插件控件的实现。. 定义 xml 标签. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 2014

IOS 插件开发

Page 2: 2014

为了满足更多地应用场景, ExMobi5.0 版本开始支持原生开发。它是以插件的形式集成到 ExMobi 中,提供给应用开发人员进行调用。右图为通讯录插件和仿 zaker效果菜单插件的效果图。

背景

Page 3: 2014

实例

• 以简单的 slider 插件控件为例,来讲述 IOS 插件控件的实现。

Page 4: 2014

定义 xml 标签

制作插件

测试插件

打包插件

FAQ

Page 5: 2014

定义 xml标签

<nativecomponent id="native" style="width:200;height:300" type="ABC_SliderComponent" factoryname="XKPlugin_Test2_ComponentFactory" onchange="changetodo" minvalue =”1” maxvalue=”10”>

</nativecomponent>

Page 6: 2014

属性名 说明 必填 备注type 插件类型 必填 插件的标识factoryname 插件工厂 必填 ios 特有的属性, android 没用。

控件名是固定的: nativecomponent部分控件属性也是固定的,其中包括和基础控件统一的属性,如 id 、 name 、 style ,这些不多说明。具体看下插件控件特有的几个属性:

其它可以根据控件需要,自定义属性,如回调函数等。

插件名是 ABC_SliderComponent插件工厂为: XKPlugin_Test2_ComponentFactory回调,在用户拖动控件的 slider 控件的时候,触发 js 里 changetodo 方法,支持传参。注意事项: ios 开发,工程内文件都不能同名,所以在命名上要注意唯一性。

定义 xml标签

Page 7: 2014

定义 xml 标签

• 编辑插件类

制作插件

测试插件

打包插件

FAQ

Page 8: 2014

ExMobi页面

插件工厂类

插件类

插件类

……

调用 xxx 插件

view

getView()

插件流程图

Page 9: 2014

制作插件• 获取基础工程• 创建 view• 编辑插件类

制作插件

Page 10: 2014

制作插件 --获取基础工程

把定义的 xml 标签文件上传到 edn 门户,生成插件工程,解压缩,用xcode 工具打开,如左图所示。可以从 edn 门户获取实例工程,参考实例工程进行开发。

AppPlugin 下固定头文件,不要做修改:XKPlugin_ComponentFactory.h XKPlugin_Component.h

XKPlugin_Test2_ComponentFactory 两个文件是根据定义的 xml 标签自动生成的,也无须处理。

生成了 ABC_SliderComponent 文件夹,并且包含了ABC_SliderComponent.h 和 ABC_SliderComponent.m 两个文件,这两个文件就是插件文件。

Page 11: 2014

@implementation XKPlugin_Test2_ComponentFactory

- (XKPlugin_Component*) createComponent:(NSString*) type{ NSLog(@"XKPlugin_Component createComponent %@", type); if ([@"ABC_SliderComponent" isEqualToString:type]){ return [[ABC_SliderComponent alloc] init]; } return nil; }

XKPlugin_Test2_ComponentFactory 插件工厂类代码如下:

制作插件—插件工厂类

Page 12: 2014

新建 ABC_SliderViewController ,继承 UIViewController

在 ABC_SliderViewController.h 头文件中,申明setRangeMinValue 方法(设置 slider 控件的起始值和最大值),定义 ABC_SliderComponent 属性。

#import <UIKit/UIKit.h>#import "ABC_SliderComponent.h"@interface ABC_SliderViewController : UIViewController@property int width;@property int height;@property (strong,nonatomic) UISlider* mySlider;@property (assign,nonatomic) ABC_SliderComponent* delegate;-(void)setRangeMinValue:(float)minValue maxValue:(float)maxValue;@end

制作插件 --创建 view

Page 13: 2014

- (void)viewDidLoad{ [super viewDidLoad];

// 初始化滑片 _mySlider = [[UISlider alloc] initWithFrame:CGRectMake(0, 0, 200, 50)]; // 设置拖动滑块图 [_mySlider setThumbImage:[UIImage imageNamed:@"slider_normal.png"] forState:UIControlStateNormal]; [_mySlider setThumbImage:[UIImage imageNamed:@"slider_normal.png"] forState:UIControlStateHighlighted]; // 设置 valuechange 事件是否实时回调

[_mySlider setContinuous:NO];// 结束拖动 [_mySlider addTarget:self

action:@selector(endDrag:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];

// 添加 [self.view addSubview:_mySlider];}

// 结束拖动-(void) endDrag:(UISlider*) slider{ NSLog(@"slider end drag"); NSString* sliderValue = [NSString stringWithFormat:@"%f",[_mySlider value]]; [_delegate onChange:sliderValue];} // 设置 slider 区间-(void)setRangeMinValue:(float)minValue maxValue:(float)maxValue{ [_mySlider setMinimumValue:minValue]; [_mySlider setMaximumValue:maxValue];}

Page 14: 2014

ABC_SliderComponent.h 继承了 XKPlugin_Component.h ,XKPlugin_Component.h 中有声明了很多方法

@interface XKPlugin_Component : NSObject

@property int width;@property int height;

-(void) initComponent;-(void) releaseComponent;-(UIView*) getView;-(void) setViewSize:(int)width height:(int) h;-(void) loadXml: (NSString*) xml;-(void) set:(NSString*) name value:(NSString*)value;-(NSString*) get:(NSString*) name;-(void) addChildElement: (NSString*)tag attributes:(NSDictionary*)attributes;-(NSString*) call:(NSString*)functionName             par1:(NSString*)param1 par2:(NSString*)param2  par3:(NSString*)param3  par4:(NSString*)param4             par5:(NSString*)param5  par6:(NSString*)param6  par7:(NSString*)param7;

制作插件—编辑插件类

Page 15: 2014

- (BOOL) helper_callJsScript:(NSString*) script;-(NSString*) helper_getAppId;-(UIImage*) helper_getImage:(NSString*) uri;-(BOOL) helper_setValue:(NSString*) value;-(NSString*) helper_getValue;-(BOOL) _sys_initComponent:(NSString*)appId container:(void*)nativeview;-(void) _sys_releaseComponent;//ExMobi JS 脚本动态设置宽、高时触发该回调函数-(void)onSize;// 添加 viewController 到 ExMobi 导航 bar 中+(void)pushViewController:(UIViewController*)viewController animated:(BOOL)animated;// 弹出顶层 viewcontroller+(void)popViewControllerAnimated:(BOOL*)animated; // 弹出所有原生 viewcontroller+(void)popAllViewControllerAnimated:(BOOL*)animated; +(void)presentModalViewController:(UIViewController*)viewController;@end

Page 16: 2014
Page 17: 2014

方法 说明 实现 备注

initComponent 初始化相关资源 无

releaseComponent 释放相关资源 无

getView 返回用于显示的 UIView 对于不可见组件,返回 nil

setViewSize 告诉组件占用的空间大小 一般不用处理

对于不可见组件,此方法不会被调用

loadXml 加载子节点 无

set 设置组件属性值 此方法会在 2 处被调用:

1. 页面中组件 xml 节点中定义的属性,

会通过此方法设置给组件

2. 页面中 js 调用组件的 set 方法

Page 18: 2014

方法 说明 实现 备注

get 获取组件的属性值 页面中 Js 调用组件的 get 方法

addChildElement 增加子节点数据, 无 页面中组件 xml 节点定义了子节点,页面会调用 addChildElement 传递节点数据给组件,根据子节点数量调用多次

call 调用组件的方法, 页面中 Js 调用组件的 call 方法,一般赋值用

helper_callJsScript 调用 ExMobi 脚本 无

helper_getAppId 获取插件控件当前位于应用 id

helper_getImage 获取应用内图片 , 无 支持 res:

helper_setValue 设置插件表单提交值 无

helper_getValue 获取插件表单提交值 无

sys_initComponent 无

sys_releaseComponent 无

Page 19: 2014

方法 说明 实现 备注

onSize 设置插件大小 ExMobi JS 脚本动态设置宽、高时触发该回调函数

pushViewController 打开页面 添加 viewController 到 ExMobi 导航 bar 中

popViewControllerAnimated 关闭顶层原生页面

弹出顶层 viewcontroller

popAllViewControllerAnimated 关闭所有原生页面

弹出所有原生 viewcontroller

presentModalViewController 打开新视图 横竖屏不受导航 bar 控制

Page 20: 2014

@property int width;@property int height;插件控件占据的宽度和高度,这里取的是 xml 标签中的 style 中设置的值。

Slider 插件需要实现的是 getView 、 set 、 get 、 call 方法。

Page 21: 2014

修改 ABC_SliderComponent 文件设置成员变量 ABC_SliderViewController;在 getView 方法中创建并返回 ABC_SliderViewController ,注意要设置其 delegate 为 self自身,此处用到了委托模式。

-(UIView*) getView{ if (sliderView == nil){ // 设置 slider范围 ABC_SliderViewController* controller = [[ABC_SliderViewController alloc] init]; controller.delegate = self; sliderView = controller; } return sliderView.view;}

修改 ABC_SliderComponent

Page 22: 2014

修改 ABC_SliderComponent

-(void) set:(NSString*) name value:(NSString*)value{

// 设置属性值, // 在 js 中可以调用进行赋值 //xml 标签中设置的属性,也会执行此函数来赋值。 if ([@"minvalue" isEqualToString:name]) { minValue = [NSString

stringWithString:value]; NSLog(@"set: minvalue:%@",minValue); }else if([@"maxvalue" isEqualToString:name])

{ maxValue = [NSString

stringWithString:value]; NSLog(@"set: maxvalue:%@",maxValue); }else if([@"onchange" isEqualToString:name])

{ onChange = [NSString

stringWithString:value]; }}

实现 set 方法,这个方法是用来获取 xml 里的属性的。

Page 23: 2014

修改 ABC_SliderComponent

实现 get 方法。

-(NSString*) get:(NSString*) name{// 获得属性值 if ([@"minvalue" isEqualToString: name]){ return minValue; }else if ([@"maxvalue" isEqualToString: name]){ return maxValue; }else if ([@"onchange" isEqualToString: name]){ return onChange; } return nil;}

Page 24: 2014

-(void) onChange:(NSString*) value{ if (onChange != nil){ NSString* script = [NSString stringWithFormat:@"%@('%@');",onChange,value]; [super helper_callJsScript:script]; }}

新增 onChange 函数,如果 slider 控件被拖动,就触发 ExMobi 中的 js 函数,具体的函数名在 xml 中设置,即例子中设置的 onchange="changetodo" 。

修改 ABC_SliderComponent

Page 25: 2014

-(NSString*) call:(NSString*)functionName par1:(NSString*)param1 par2:(NSString*)param2 par3:(NSString*)param3 par4:(NSString*)param4 par5:(NSString*)param5 par6:(NSString*)param6 par7:(NSString*)param7{// 在 js 中调用给插件传值 if ([@"setRange" isEqualToString: functionName]){ float minV = [param1 floatValue]; float maxV = [param2 floatValue]; [sliderView setRangeMinValue:minV maxValue:maxV]; return nil; }else{ NSLog(@"ABC_SliderComponent ERROR: unsupported function call : %@" , functionName); return nil; }}

Call 方法,在ExMobi 的 js 中可以触发此方法,一般是用来给控件赋值的。

修改 ABC_SliderComponent

Page 26: 2014

定义 xml 标签

• 编辑插件类

制作插件

测试插件

打包插件

FAQ

Page 27: 2014

由于插件需要在 EDN 打包后才能实际使用,为了更好的开发测试, AppPlugin 工程内自带了一个测试工程TestAppPlugin ,所以插件制作完,可以先在测试工程中测试,测试通过后,再提交到门户上进行打包。

测试工程中有个 common 文件夹,下面有很多文件。

把 AppPlugin写的插件工厂类的头文件复制到测试工程中。即 XKPlugin_Test2_ComponentFactory.h

测试插件

Page 28: 2014

@implementation XKPluginManager_ComponentFactory

- (XKPlugin_ComponentFactory*) createFactory:(NSString*) factoryName{ if (_factorys == nil) { _factorys = [[NSMutableDictionary alloc]init]; } XKPlugin_ComponentFactory* factory = [_factorys objectForKey:factoryName]; if (factory == nil) { if ([@"XKPlugin_Test2_ComponentFactory" isEqualToString:factoryName]){ factory =[[XKPlugin_Test2_ComponentFactory alloc] init]; [_factorys setObject:factory forKey:factoryName]; } } return factory;}

打开 common 下的 XKPluginManager_ComponentFactory.mm 文件,增加如下红色代码。

测试插件

Page 29: 2014

测试插件

在 ABCViewController 中增加一个按钮,给按钮添加事件,点击按钮进入到 ABC_TestSliderViewController 页面 ,

新建 ABC_TestSliderViewController ,继承 UIViewController ,并且勾选生成 .xib 文件。

在 .xib 文件中,拖入 view 控件( x=0 , y=60 )。选中 view 控件,右击拖入到 ABC_TestSliderViewController.h 中

定义成属性,命名为 controller 。ABC_TestSliderViewController.h 需要实现

XKPlugin_ComponentDelegate

Page 30: 2014

测试插件

#import "ABC_TestSliderViewController.h"#import "XKPlugin_Component.h"#import "XKPluginManager_ComponentFactory.h"@interface ABC_TestSliderViewController (){ XKPlugin_Component* component;}@end @implementation ABC_TestSliderViewController- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization } return self;} 

Page 31: 2014

测试插件

- (void)viewDidLoad{ [super viewDidLoad]; [self setTitle:@"Test ABC_SliderComponent"]; //构建 factoryManager管理类 XKPluginManager_ComponentFactory* factoryManager = [[XKPluginManager_ComponentFactory alloc]init]; // 根据 factoryname 创建插件工厂类 XKPlugin_ComponentFactory* factory = [factoryManager createFactory:@"XKPlugin_Test2_ComponentFactory"]; // 根据插件名创建插件 component = [factory createComponent:@"ABC_SliderComponent"]; // 插件为空则提示并返回 if (component == nil) {

UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Test" message:@"Load ABC_SliderComponent fail" delegate:nil cancelButtonTitle:@"close" otherButtonTitles:nil]; [alert show]; return; } // 传递 self 到插件中用于接收 js 回调事件 void* container = (__bridge void*)self; [component _sys_initComponent:@"TestApp" container:container]; // 设置插件普通属性及事件属性 可用于测试 js属性设置// [component set:@"onchange" value:@"changetodo"]; // 初始化插件 [component initComponent];

Page 32: 2014

测试插件

// 获取插件 view 对象 UIView* compView = [component getView]; // 设置控件显示区域 if (compView != nil){ CGRect bound = self.container_.bounds; [self.container_ addSubview:compView]; compView.frame = bound; compView.hidden = NO; // 设置插件显示宽,高 [component setViewSize:bound.size.width height:bound.size.height]; }} - (void)didReceiveMemoryWarning{ [super didReceiveMemoryWarning]; }@end

Page 33: 2014

定义 xml 标签

• 编辑插件类

制作插件

测试插件

打包插件

FAQ

Page 34: 2014

打包

切换到文件管理器,获取该插件工程调用的插件工厂类 ,如 XKPlugin_Test2_ComponentFactory.h; 获取生成的 .a库,如 plugin.a; 建立 image 文件夹,将插件工程所需的所有图片均拷贝至 image 文件夹中;建立framework 文件夹,将插件工程所需的第三方 .a库 /framework库拷贝至该文件夹;建立 xib 文件夹,将插件工程所需的 xib 文件拷贝至改文件夹;建立 other目录,将其他类型文件(如 xml , plist 等)拷贝至该目录,建立 plugins目录,该目录下可建立多个插件名目录,将插件配置文件 config.xml 放置于此内。

把上述的文件放在文件夹里,并且压缩此文件夹为 zip 包,上传到 edn 门户上。

Page 35: 2014

打包

插件工程所需的第三方 .a/.framework库如果用到了 ExMobi 中没引入的系统库,就在此文件夹下新建 framework.txt ,写上系统库的路径,以换行来分割。其他类型文件(如 xml , plist 等)

多个插件名目录,目录里包含各自的 config 文件

生成 plugin.a 的时候,把引入的第三方 .a库删掉再 build

Page 36: 2014

包含:• 1 :插件工程中的工厂类头文件(必选);• 2 :插件 .a 包(必选);• 3 :包含插件配置文件的 natives目录,该目录包含插件

type 命名的文件夹,包含插件配置文件 config.xml (必选);

• 4 :包含第三方静态库及 framework.txt 文件(系统framework库路径列表)的 framework 文件夹(可选);

• 5 :插件工程图片的 image 文件夹(可选);• 6 :包含其他文件的 other 文件夹(可选);

打包

Page 37: 2014

打包

节点 /属性 说明 必须项

Version 插件版本 是

Type 插件名称 是

factoryname 插件工厂类名 Ios 插件特有 否

description 插件描述 否

date 插件创建日期 否

vendor 插件作者 否

vendor.url 插件作者网址 否

vendor.email 插件作者邮件 否

Page 38: 2014

打包

插件配置 config.xml :<?xml version="1.0" encoding="UTF-8" standalone="yes"?><config> <version>1.0.0</version><!-- 插件版本 --> <type> ABC_SliderComponent </type><!-- 插件名称 --> <factoryname>XKPlugin_Test2_ComponentFactory</factoryname><!-- 插件工厂类名 Ios 插件特有

--> <description>slider 插件 </description><!-- 插件描述 --> <date>2014-01-11</date><!-- 插件创建日期 --> <vendor url="www.nj.fiberhome.com.cn" email="">rachel</vendor> <!-- 插件开发者信息 --></config>

Page 39: 2014

定义 xml 标签

• 编辑插件类

制作插件

测试插件

打包插件

FAQ

Page 40: 2014

• 插件工程设置采用 ARC or MRC 都可以,示例工程提供 ARC 版 和 MRC 版,若设备采用 IOS 5.0及以上系统,建议采用 ARC 方式。• 类名的冲突 为了避免在打包编译时,插件和 ExMobi 以及其它 lib库的名字冲突,插件中的所有类名(包括插件类及其他辅助类),均应该使用特有的前缀• 能否在工程中引入第三方 .a库 可以,若引入第三方 .a库,插件打包时需要放置于 framework 文件夹,压缩后一起提交。还需要注意的是,提交前可在 EDN 页面查找 ExMobi 支持的公共 .a库,若插件使用的第三方库若与 ExMobi使用的第三方库一样,则无须提交,打包后插件仍可正常使用。 framework 也是一样的。

FAQ

Page 41: 2014

• 插件工程是否需要引入第三方包? 不需要,插件工程本身不要引入任何的包,如果引入了第三方包,最后插件工程生成的 .a 可能会和 ExMobi 有冲突。 可以在测试工程中引入第三方包,进行测试。• 插件工程中引用的系统 Framework框架打包时是否需要提供给

EDN? 请先在 EDN 上查看 ExMobi 支持的系统 Framework框架列表,若插件工程引用的系统库在此列表中则不需要提供,若不在此列表中则需要提供。• 打包时如何向 EDN 提供系统 Framework框架列表? 假设插件包含了如下图所示系统 Framework库:libz.dylib,CoreGraphics.framework,OpenAL.framework :

FAQ

Page 42: 2014

在 framework列表中点击右键,选中 Show in Finder

FAQ

Page 43: 2014

查看所在目录如下图所示 ,则相对 SDK 根目录路径为:usr/lib/libz.dylib

FAQ

Page 44: 2014

查看所在目录如下图所示 ,则相对 SDK 根目录路径为: System/Library/Frameworks/CoreGraphics.framework

FAQ

Page 45: 2014

向 EDN 提交插件工程时,建立 framework.txt 文件,将需要引用的系统framework路径地址放置于改文件中,格式为:每个路径地址占一行,再将framework.txt 文件放置于 framework 文件夹中压缩打包上传至 EDN即可。• 能否使用插件工程中的图片文件 可以,插件工程中如需图片,按照普通 UIImage读取方式使用即可,插件打包时需要放置于 image 文件夹,压缩后一起提交。需要注意图片文件命名必须使用特殊前缀以保证图片名称唯一,如: if(icon == nil){

//直接使用工程中图片 controller.icon = [UIImage imageNamed:@"ABC_city.png"];

}

FAQ

Page 46: 2014

• 插件构建时能否使用 xib 文件? 可以,提交时需要一起提交打包,需要注意 xib 文件命名必须使用特殊前缀以保证图片名称唯一。 注意: xib使用时候,请一定不要勾选 Use Autolayout选项,否则会导致提交后打包失败。

FAQ

Page 47: 2014

• 如何获取 ExMobi 应用下的图片 通过 XKPlugin_Component 基类的 -(UIImage*) helper_getImage:(NSString*) uri 方法来获取图片,注意仅支持 res :前缀的应用内图片。 如: UIImage* arrow = [super helper_getImage:@"res:/image/arrow.png"];

• 如何调用 ExMobi 页面中的脚本 通过 XKPlugin_Component 基类的 - (BOOL) helper_callJsScript:(NSString*) script 方法来调用。 如: NSString* script = [NSString stringWithFormat:@"%@('%d','%@','%@');",onSelected_,index,row.code,row.name];

[super helper_callJsScript:script];

FAQ

Page 48: 2014

• 如何设置插件控件表单提交值 通过 XKPlugin_Component 基类的 -(void) setNativeValue:(NSString *)value方法来调用。

如: -(void) setNativeValue:(NSString *)value

{

[super helper_setValue:value];

• 如何获取插件控件表单提交值 通过 XKPlugin_Component 基类的 -(NSString*) helper_getValue; 方法来调用。 如: NSString* script = NSString* script = [super helper_getValue];

FAQ

Page 49: 2014

• 能否支持全屏类 ViewController布局及多个 ViewControler切换 支持,通过 XKPlugin_Component 基类的+(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated; 方法来添加 viewController 到 ExMobi 中 通过 XKPlugin_Component 基类的+ (void)popAllViewControllerAnimated:(BOOL)animated; 方法从 ExMobi 中移除viewController

通过 + (void)popAllViewControllerAnimated:(BOOL)animated; 方法从ExMobi 中移除所有添加的 viewController

FAQ

Page 50: 2014

• 一个插件工程能否编写多个插件类 可以,按照工程创建说明构建多个插件类即可,如下图所示,该插件工程包含了 RefreshListComponent (下拉刷新插件), SliderMenuComponent ( 3D 动态菜单), AnimationComponent (滑动容器插件)等多个插件,只需导出一个 .a 包即可。

FAQ

Page 51: 2014

• 生成静态库 plugin.a 的类型 plugin.a 必须是 iOS Device 版本,而不是 iOS Simulator 版本。 执行 lipo –info plugin.a 可以看到 architecture 为 arm 类型,而不是 i386 类型。

• 能否通过直接 build 方式生成 .a 包 可以,但不推荐。建议用 Product->Archive 生成,这样生成的 .a 包体积更小。

FAQ