Ios插件开发讲义

Download Report

Transcript Ios插件开发讲义

IOS插件开发
背景
为了满足更多地应用场景,
ExMobi5.0版本开始支持原生
开发。它是以插件的形式集成
到ExMobi中,
提供给应用开发人员进行调用。
右图为通讯录插件和仿zaker
效果菜单插件的效果图。
实例
• 以简单的slider插件控件为例,来讲述IOS插件控件的实现。
定义xml标签
制作插件
测试插件
打包插件
FAQ
定义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,这些不多说明。
具体看下插件控件特有的几个属性:
属性名
说明
必填
备注
type
插件类型
必填
插件的标识
factoryname
插件工厂
必填
ios特有的属性,android没用。
其它可以根据控件需要,自定义属性,如回调函数等。
插件名是ABC_SliderComponent
插件工厂为:XKPlugin_Test2_ComponentFactory
回调,在用户拖动控件的slider控件的时候,触发js里changetodo方法,支持传参。
注意事项:ios开发,工程内文件都不能同名,所以在命名上要注意唯一性。
定义xml标签
制作插件
• 编辑插件类
测试插件
打包插件
FAQ
插件流程图
插件类
插件工厂类
……
插件类
调用xxx插件
ExMobi
页面
getView()
view
制作插件
制作插件
• 获取基础工程
• 创建view
• 编辑插件类
制作插件--获取基础工程
把定义的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_Component createComponent %@", type);
if ([@"ABC_SliderComponent" isEqualToString:type]){
return [[ABC_SliderComponent alloc] init];
}
return nil;
}
制作插件--创建view
新建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
- (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];
}
制作插件—编辑插件类
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;
- (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
方法
说明
实现
initComponent
初始化相关资源
无
releaseComponent
释放相关资源
无
getView
返回用于显示的UIView
setViewSize
告诉组件占用的空间大小
一般不
用处理
loadXml
加载子节点
无
set
设置组件属性值
备注
对于不可见组件,返回nil
对于不可见组件,此方法不会被调用
此方法会在2处被调用:
1. 页面中组件xml节点中定义的属性,会
通过此方法设置给组件
2. 页面中js调用组件的set方法
方法
说明
实现
get
获取组件的属性值
addChildElement
增加子节点数据,
call
调用组件的方法,
helper_callJsScript
调用ExMobi脚本
无
helper_getAppId
获取插件控件当前位于应
用id
无
helper_getImage
获取应用内图片,
无
helper_setValue
设置插件表单提交值
无
helper_getValue
获取插件表单提交值
无
备注
页面中Js调用组件的get方法
无
页面中组件xml节点定义了子节点,页面会调
用addChildElement传递节点数据给组件,根
据子节点数量调用多次
页面中Js调用组件的call方法,一般赋值用
sys_initComponent
无
sys_releaseComponent
无
支持res:
方法
说明
实现
备注
onSize
设置插件大小
ExMobi JS脚本动态设置宽、高时触发该回调
函数
pushViewController
打开页面
添加viewController到ExMobi导航bar中
popViewControllerAnimated
关闭顶层原生页
面
弹出顶层viewcontroller
popAllViewControllerAnimated
关闭所有原生页
面
弹出所有原生viewcontroller
presentModalViewController
打开新视图
横竖屏不受导航bar控制
@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_SliderViewController alloc] init];
controller.delegate = self;
sliderView = controller;
}
}
return sliderView.view;
修改ABC_SliderComponent
实现set方法,这个方法是用来获取xml里的属性的。
-(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];
}
修改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;
}
修改ABC_SliderComponent
新增onChange函数,如果slider控件被拖动,就触发ExMobi中的js函数,
具体的函数名在xml中设置,即例子中设置的onchange="changetodo" 。
-(void) onChange:(NSString*) value{
if (onChange != nil){
NSString* script = [NSString
stringWithFormat:@"%@('%@');",onChange,value];
[super helper_callJsScript:script];
}
}
修改ABC_SliderComponent
Call方法,在ExMobi
的js中可以触发此方
法,一般是用来给控
件赋值的。
-(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;
}
}
定义xml标签
制作插件
• 编辑插件类
测试插件
打包插件
FAQ
测试插件
由于插件需要在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 = [[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;
}
测试插件
在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"
@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;
}
测试插件
- (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_ComponentFactor
y"];
//根据插件名创建插件
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];
测试插件
//获取插件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
定义xml标签
制作插件
• 编辑插件类
测试插件
打包插件
FAQ
打包
切换到文件管理器,获取该插件工程调用的插件工厂类 ,如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文件夹(可选);
打包
节点/属性
说明
必须项
Version
插件版本
是
Type
插件名称
是
factoryname
插件工厂类名 Ios插件特有
否
description
插件描述
否
date
插件创建日期
否
vendor
插件作者
否
vendor.url
插件作者网址
否
vendor.email
插件作者邮件
否
打包
插件配置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>
定义xml标签
制作插件
• 编辑插件类
测试插件
打包插件
FAQ
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 = [UIImage imageNamed:@"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.n
ame];
[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
包即可。
FAQ
• 生成静态库plugin.a的类型
plugin.a必须是iOS Device版本,而不是iOS Simulator版本。
执行lipo –info plugin.a可以看到architecture为arm类型,而不是i386类型。
• 能否通过直接build方式生成.a包
可以,但不推荐。建议用Product->Archive生成,这样生成的 .a包体积更小。