510 likes | 614 Views
IOS 插件开发培训. 2014. 背景. 为了满足更多地应用场景, ExMobi5.0 版本开始支持原生开发。它是以插件的形式集成到 ExMobi 中, 提供给应用开发人员进行调用。 右 图为通讯录插件和仿 zaker 效果菜单插件的效果图。. 实例. 以简单的 slider 插件控件为例,来讲述 IOS 插件控件的实现。. 定义 xml 标签.
E N D
IOS插件开发培训 2014
背景 为了满足更多地应用场景, ExMobi5.0版本开始支持原生开发。它是以插件的形式集成到ExMobi中, 提供给应用开发人员进行调用。 右图为通讯录插件和仿zaker效果菜单插件的效果图。
实例 • 以简单的slider插件控件为例,来讲述IOS插件控件的实现。
定义xml标签 <nativecomponent id="native" style="width:200;height:300"type="ABC_SliderComponent" factoryname="XKPlugin_Test2_ComponentFactory" onchange="changetodo" minvalue =”1” maxvalue=”10”> </nativecomponent>
定义xml标签 控件名是固定的:nativecomponent 部分控件属性也是固定的,其中包括和基础控件统一的属性,如id、name、style,这些不多说明。具体看下插件控件特有的几个属性: 其它可以根据控件需要,自定义属性,如回调函数等。 插件名是ABC_SliderComponent 插件工厂为:XKPlugin_Test2_ComponentFactory 回调,在用户拖动控件的slider控件的时候,触发js里changetodo方法,支持传参。 注意事项:ios开发,工程内文件都不能同名,所以在命名上要注意唯一性。
插件流程图 插件类 插件工厂类 …… 插件类 调用xxx插件 getView() view ExMobi页面
制作插件--获取基础工程 把定义的xml标签文件上传到edn门户,生成插件工程,解压缩,用xcode工具打开,如左图所示。 可以从edn门户获取实例工程,参考实例工程进行开发。 AppPlugin下固定头文件,不要做修改: XKPlugin_ComponentFactory.h XKPlugin_Component.h XKPlugin_Test2_ComponentFactory两个文件是根据定义的xml标签自动生成的,也无须处理。 生成了ABC_SliderComponent文件夹,并且包含了ABC_SliderComponent.h和ABC_SliderComponent.m两个文件,这两个文件就是插件文件。
制作插件—插件工厂类 XKPlugin_Test2_ComponentFactory插件工厂类代码如下: @implementation XKPlugin_Test2_ComponentFactory - (XKPlugin_Component*) createComponent:(NSString*) type { NSLog(@"XKPlugin_ComponentcreateComponent %@", type); if ([@"ABC_SliderComponent" isEqualToString:type]){ return [[ABC_SliderComponentalloc] init]; } return nil; }
制作插件--创建view 新建ABC_SliderViewController,继承UIViewController #import <UIKit/UIKit.h> #import "ABC_SliderComponent.h" @interfaceABC_SliderViewController : UIViewController @propertyint width; @propertyint height; @property (strong,nonatomic) UISlider* mySlider; @property (assign,nonatomic) ABC_SliderComponent* delegate; -(void)setRangeMinValue:(float)minValuemaxValue:(float)maxValue; @end 在ABC_SliderViewController.h头文件中,申明setRangeMinValue方法(设置slider控件的起始值和最大值),定义ABC_SliderComponent属性。
- (void)viewDidLoad { [superviewDidLoad]; //初始化滑片 _mySlider = [[UISlideralloc] initWithFrame:CGRectMake(0, 0, 200, 50)]; //设置拖动滑块图 [_mySlidersetThumbImage:[UIImageimageNamed:@"slider_normal.png"] forState:UIControlStateNormal]; [_mySlidersetThumbImage:[UIImageimageNamed:@"slider_normal.png"] forState:UIControlStateHighlighted]; //设置valuechange事件是否实时回调 [_mySlidersetContinuous:NO]; //结束拖动 [_mySlideraddTarget:selfaction:@selector(endDrag:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside]; // 添加 [self.viewaddSubview:_mySlider]; } //结束拖动 -(void) endDrag:(UISlider*) slider { NSLog(@"slider end drag"); NSString* sliderValue = [NSStringstringWithFormat:@"%f",[_mySlidervalue]]; [_delegateonChange:sliderValue]; } //设置slider区间 -(void)setRangeMinValue:(float)minValuemaxValue:(float)maxValue{ [_mySlidersetMinimumValue:minValue]; [_mySlidersetMaximumValue:maxValue]; }
制作插件—编辑插件类 ABC_SliderComponent.h 继承了 XKPlugin_Component.h, XKPlugin_Component.h中有声明了很多方法 @interfaceXKPlugin_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;
- (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
@property int width;@property int height; 插件控件占据的宽度和高度,这里取的是xml标签中的style中设置的值。 Slider插件需要实现的是getView、set、get、call方法。
修改ABC_SliderComponent • 修改ABC_SliderComponent文件 • 设置成员变量ABC_SliderViewController; • 在getView方法中创建并返回ABC_SliderViewController,注意要设置其delegate为self自身,此处用到了委托模式。 -(UIView*) getView{ if (sliderView == nil){ //设置slider范围 ABC_SliderViewController* controller = [[ABC_SliderViewControlleralloc] init]; controller.delegate = self; sliderView = controller; } returnsliderView.view; }
修改ABC_SliderComponent 实现set方法,这个方法是用来获取xml里的属性的。 -(void) set:(NSString*) name value:(NSString*)value{ //设置属性值, //在js中可以调用进行赋值 //xml标签中设置的属性,也会执行此函数来赋值。 if ([@"minvalue"isEqualToString:name]) { minValue = [NSStringstringWithString:value]; NSLog(@"set: minvalue:%@",minValue); }elseif([@"maxvalue"isEqualToString:name]) { maxValue = [NSStringstringWithString:value]; NSLog(@"set: maxvalue:%@",maxValue); }elseif([@"onchange"isEqualToString:name]) { onChange = [NSStringstringWithString:value]; } }
修改ABC_SliderComponent 实现get方法。 -(NSString*) get:(NSString*) name{ //获得属性值 if ([@"minvalue"isEqualToString: name]){ returnminValue; }elseif ([@"maxvalue"isEqualToString: name]){ returnmaxValue; }elseif ([@"onchange"isEqualToString: name]){ returnonChange; } returnnil; }
修改ABC_SliderComponent 新增onChange函数,如果slider控件被拖动,就触发ExMobi中的js函数, 具体的函数名在xml中设置,即例子中设置的onchange="changetodo" 。 -(void) onChange:(NSString*) value{ if (onChange != nil){ NSString* script = [NSStringstringWithFormat:@"%@('%@');",onChange,value]; [superhelper_callJsScript:script]; } }
修改ABC_SliderComponent -(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]){ floatminV = [param1 floatValue]; floatmaxV = [param2 floatValue]; [sliderViewsetRangeMinValue:minVmaxValue:maxV]; returnnil; }else{ NSLog(@"ABC_SliderComponent ERROR: unsupported function call : %@" , functionName); returnnil; } } Call方法,在ExMobi的js中可以触发此方法,一般是用来给控件赋值的。
测试插件 由于插件需要在EDN打包后才能实际使用,为了更好的开发测试,AppPlugin工程内自带了一个测试工程TestAppPlugin,所以插件制作完,可以先在测试工程中测试,测试通过后,再提交到门户上进行打包。 测试工程中有个common文件夹,下面有很多文件。 把AppPlugin写的插件工厂类的头文件复制到测试工程中。 即XKPlugin_Test2_ComponentFactory.h
测试插件 打开common下的XKPluginManager_ComponentFactory.mm文件,增加如下红色代码。 @implementation XKPluginManager_ComponentFactory - (XKPlugin_ComponentFactory*) createFactory:(NSString*) factoryName { if (_factorys == nil) { _factorys = [[NSMutableDictionaryalloc]init]; } XKPlugin_ComponentFactory* factory = [_factorysobjectForKey:factoryName]; if (factory == nil) { if ([@"XKPlugin_Test2_ComponentFactory" isEqualToString:factoryName]){ factory =[[XKPlugin_Test2_ComponentFactory alloc] init]; [_factoryssetObject:factoryforKey:factoryName]; } } return factory; }
测试插件 在ABCViewController中增加一个按钮,给按钮添加事件,点击按钮进入到ABC_TestSliderViewController页面, 新建ABC_TestSliderViewController,继承UIViewController,并且勾选生成.xib文件。 在.xib文件中,拖入view控件(x=0,y=60)。 选中view控件,右击拖入到ABC_TestSliderViewController.h中定义成属性,命名为controller。 ABC_TestSliderViewController.h需要实现XKPlugin_ComponentDelegate
测试插件 #import "ABC_TestSliderViewController.h" #import "XKPlugin_Component.h" #import "XKPluginManager_ComponentFactory.h" @interfaceABC_TestSliderViewController () { XKPlugin_Component* component; } @end @implementationABC_TestSliderViewController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [superinitWithNibName:nibNameOrNilbundle:nibBundleOrNil]; if (self) { // Custom initialization } returnself; }
测试插件 - (void)viewDidLoad { [superviewDidLoad]; [selfsetTitle:@"Test ABC_SliderComponent"]; //构建factoryManager管理类 XKPluginManager_ComponentFactory* factoryManager = [[XKPluginManager_ComponentFactoryalloc]init]; //根据factoryname创建插件工厂类 XKPlugin_ComponentFactory* factory = [factoryManagercreateFactory:@"XKPlugin_Test2_ComponentFactory"]; //根据插件名创建插件 component = [factory createComponent:@"ABC_SliderComponent"]; //插件为空则提示并返回 if (component == nil) { UIAlertView* alert = [[UIAlertViewalloc] initWithTitle:@"Test" message:@"Load ABC_SliderComponent fail"delegate:nilcancelButtonTitle:@"close"otherButtonTitles:nil]; [alert show]; return; } //传递self到插件中用于接收js回调事件 void* container = (__bridgevoid*)self; [component _sys_initComponent:@"TestApp"container:container]; //设置插件普通属性及事件属性 可用于测试js属性设置 // [component set:@"onchange" value:@"changetodo"]; //初始化插件 [component initComponent];
测试插件 //获取插件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.widthheight:bound.size.height]; } } - (void)didReceiveMemoryWarning { [superdidReceiveMemoryWarning]; } @end
打包 切换到文件管理器,获取该插件工程调用的插件工厂类 ,如XKPlugin_Test2_ComponentFactory.h; 获取生成的.a库,如plugin.a; 建立image文件夹,将插件工程所需的所有图片均拷贝至image文件夹中;建立framework文件夹,将插件工程所需的第三方.a库/framework库拷贝至该文件夹;建立xib文件夹,将插件工程所需的xib文件拷贝至改文件夹;建立other目录,将其他类型文件(如xml,plist等)拷贝至该目录,建立plugins目录,该目录下可建立多个插件名目录,将插件配置文件config.xml放置于此内。 把上述的文件放在文件夹里,并且压缩此文件夹为zip包,上传到edn门户上。
打包 插件工程所需的第三方.a/.framework库 如果用到了ExMobi中没引入的系统库, 就在此文件夹下新建framework.txt, 写上系统库的路径,以换行来分割。 其他类型文件(如xml,plist等) 多个插件名目录, 目录里包含各自的config文件 生成plugin.a的时候,把引入的第三方.a库删掉再build
打包 包含: • 1:插件工程中的工厂类头文件(必选); • 2:插件.a包(必选); • 3:包含插件配置文件的natives目录,该目录包含插件type命名的文件夹,包含插件配置文件config.xml(必选); • 4:包含第三方静态库及framework.txt文件(系统framework库路径列表)的framework文件夹(可选); • 5:插件工程图片的image文件夹(可选); • 6:包含其他文件的other文件夹(可选);
打包 插件配置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><!--插件创建日期--> <vendorurl="www.nj.fiberhome.com.cn" email="">rachel</vendor><!--插件开发者信息--> </config>
FAQ • 插件工程设置采用ARC or MRC 都可以,示例工程提供ARC版 和 MRC版,若设备采用IOS 5.0及以上系统,建议采用ARC方式。 • 类名的冲突 为了避免在打包编译时,插件和ExMobi以及其它lib库的名字冲突,插件中的所有类名(包括插件类及其他辅助类),均应该使用特有的前缀 • 能否在工程中引入第三方.a库 可以,若引入第三方.a库,插件打包时需要放置于framework文件夹,压缩后一起提交。还需要注意的是,提交前可在EDN页面查找ExMobi支持的公共.a库,若插件使用的第三方库若与ExMobi使用的第三方库一样,则无须提交,打包后插件仍可正常使用。 framework也是一样的。
FAQ • 插件工程是否需要引入第三方包? 不需要,插件工程本身不要引入任何的包,如果引入了第三方包,最后插件工程生成的.a可能会和ExMobi有冲突。 可以在测试工程中引入第三方包,进行测试。 • 插件工程中引用的系统Framework框架打包时是否需要提供给EDN? 请先在EDN上查看ExMobi支持的系统Framework框架列表,若插件工程引用的系统库在此列表中则不需要提供,若不在此列表中则需要提供。 • 打包时如何向EDN提供系统Framework框架列表? 假设插件包含了如下图所示系统Framework库:libz.dylib,CoreGraphics.framework,OpenAL.framework:
FAQ 在framework列表中点击右键,选中Show in Finder
FAQ 查看所在目录如下图所示,则相对SDK根目录路径为: usr/lib/libz.dylib
FAQ 查看所在目录如下图所示,则相对SDK根目录路径为: System/Library/Frameworks/CoreGraphics.framework
FAQ 向EDN提交插件工程时,建立framework.txt文件,将需要引用的系统framework路径地址放置于改文件中,格式为:每个路径地址占一行,再将framework.txt文件放置于framework文件夹中压缩打包上传至EDN即可。 • 能否使用插件工程中的图片文件 可以,插件工程中如需图片,按照普通UIImage读取方式使用即可,插件打包时需要放置于image文件夹,压缩后一起提交。需要注意图片文件命名必须使用特殊前缀以保证图片名称唯一,如: if(icon == nil){ //直接使用工程中图片 controller.icon = [UIImageimageNamed:@"ABC_city.png"]; }
FAQ • 插件构建时能否使用xib文件? 可以,提交时需要一起提交打包,需要注意xib文件命名必须使用特殊前缀以保证图片名称唯一。 注意:xib使用时候,请一定不要勾选Use Autolayout选项,否则会导致提交后打包失败。
FAQ • 如何获取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 • 如何设置插件控件表单提交值 通过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 • 能否支持全屏类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 • 一个插件工程能否编写多个插件类 可以,按照工程创建说明构建多个插件类即可,如下图所示,该插件工程包含了 RefreshListComponent(下拉刷新插件),SliderMenuComponent(3D动态菜单),AnimationComponent(滑动容器插件)等多个插件,只需导出一个.a包即可。