ios 开发控件中心点_AppCan
1.概述—————————————下載PDF本文檔主要介紹了如何進行AppCan iOS原生插件開發。
1.1面向的讀者在您閱讀此文檔時,我們假定您已經具備了基礎的iOS應用開發經驗,并能夠理解相關基礎概念。
此外,您也應該對HTML,JavaScript,CSS等有一定的了解,并且熟悉在JavaScript和Objective-C環境下的JSON格式數據操作。
提示:@自定義插件作者 目前ios擴展的插件目前只能在線打包進行測試和使用,閱讀如下文檔
關于自定義插件上傳(目前支持在線),請參閱文檔。
關于自定義插件發布到插件中心,請參閱文檔。
1.2開發環境需求OS X 10.10+
Xcode 7.0+
AppCan iOS插件開發包(git地址)
2.插件開發的準備工作以下以開發一個范例插件DemoPlugin為例,為您介紹如何進行iOS插件開發
在本文檔中,會用 斜體字 表示那些不是必須,但我們強烈推薦您這樣做的操作。
2.1 創建靜態庫工程打開Xcode,在菜單欄中選擇 File - New - Project…
選擇 iOS - Framework & Library - Cocoa Touch Static Library
填入您的插件基本信息,Product Name填EUExDemoPlugin(注1),點擊Next
選擇靜態庫工程的保存地址,點擊create,建立一個靜態庫工程
編輯EUExDemoPlugin這個target的Build Settings如下(注2):
將Product Name對應的值修改為 uexDemoPlugin(注3)
將Pre-configuration Build Products Path 修改為$SRCROOT/uexDemoPlugin(注4)
編輯EUExDemoPlugin這個target的Build Phases,找到Copy Files這個phase,清空其Subpath設置,移除下面列表中的.h文件
注1: 此處靜態庫工程的命名規則為 EUEx + 插件名稱,之后出現的EUExDemoPlugin亦是如此。
注2: 修改target的BuildSettings的方法如下圖所示,選中工程主體-選擇指定的target-選擇BuildSettings-選中all,然后在右上角搜索框中搜索相應的鍵,雙擊編輯對應的值
注3: 此處Product Name 的命名規則為 uex + 插件名稱,之后出現的uexDemoPlugin亦是如此。
注4: 此設置使得工程編譯得到的.a文件會生成在工程目錄下的uexDemoPlugin文件夾中,方便后續的插件打包
2.2 編輯插件調試工程關閉剛剛創建的靜態庫工程,從AppCan iOS插件開發包中找到調試插件的工程模板,復制一份到本地目錄
打開工程模板中的調試工程AppCanPlugin.xcodeproj,將剛剛創建的靜態庫工程EUExDemoPlugin.xcodeproj引入此調試工程(直接拖拽即可,注意引入之前要確認靜態庫工程已經被關閉)
編輯主工程AppCanPlugin這個target的Build Phases如下(注1):Target Dependencies 中,添加插件工程的EUExDemoPluin的target
Link Binary With Libraries中,添加libeuxDemoPlugin.a(注2)
注1:
編輯Build Phases方法如下圖所示:選中工程主體-選擇target-選擇Build Phases - 展開相應的Phase - 點擊下方的按鈕進行相應的操作
注2:
編輯完成后應該如下圖所示:
2.3 插件調試工程簡介
見下圖,紅框標注部分都是在插件開發調試中可能會用到的部分。
好了,到此,前期的準備工作就已經完成了,可以正式開始插件開發了!
3.開始插件開發
所有的開發和調試工作,都可以直接在剛剛建立的插件調試工程中進行!
3.1 編寫插件入口類在AppCan插件開發包中,打開AppCan引擎頭文件文件夾,找到engineHeader,將此文件夾引入插件工程,如下圖所示
新建插件入口類EUExPlugin。如果你的插件靜態庫工程名就是EUExDemoPlugin,那么這個類應該已經自動生成了,此步可跳過。
在EUExDemoPlugin這個類的頭文件中引入EUExBase.h 并使得EUExDemoPlugin類繼承自EUExBase
在此類中實現生命周期方法initWithBrwView:和clean
-(instancetype)initWithBrwView:(EBrowserView*)eInBrwView
{
self=[superinitWithBrwView:eInBrwView];
if(self){
//NSLog(@"插件實例被創建");
}
returnself;
}
-(void)clean{
//NSLog(@"網頁即將被銷毀");
}
插件中類的命名規則插件的入口類必須命名為EUEx開頭的類名;
插件中其他的類無命名限制,但建議增加獨特的前綴,以避免和引擎以及其他插件中的類產生類名沖突,導致打包失敗
EUExBase.h簡介EUExBase是AppCan插件入口的基類,所有的插件入口類都必須繼承自此類。
EUExBase擁有1個實例變量和3個實例方法
實例變量meBrwView是一個弱引用,指向了AppCan的網頁對象。任何對網頁的操作都會通過此對象進行。
實例方法initWithBrwView:是默認的初始化方法。每當一個網頁里調用某插件的方法時(比如uexDemoPlugin.test();),都會先去尋找插件的入口實例(EUExDemoPlugin),如果不存在,則會通過此方法創建一個新的實例并持有它。
對于同一個插件,每個網頁里只會有一個插件實例,但不同的網頁會擁有不同的插件實例。
插件子類可以覆寫此方法進行自定義初始化設置,但必須調用父類的此方法
實例方法clean 會在網頁被關閉前調用插件子類可以覆寫此方法進行必要的清除工作,比如停止NSTimer,關閉網絡連接等等。
在此方法被調用后,插件不應該再使用self.meBrwView對網頁進行任何操作。
在此方法被調用后, 除非特殊情況,不要讓插件實例被其他任何類強硬用,否則很有可能會造成內存問題。
實例方法absPath:用于轉換AppCan協議路徑(res://,wgt://等)至絕對路徑。
3.2 插件和網頁進行交互
3.2.1 暴露接口給網頁
本小節示范了如何讓網頁JS去調用一個原生的方法helloWorld,實現 JavaScript —> OC 的操作
在EUExDemoPlugin類中實現一個方法helloWorld:
-(void)helloWorld:(NSMutableArray*)inArguments{
//打印 hello world!
NSLog(@"hello world!");
}
插件入口類中實現供網頁調用的方法的注意事項1.注意所有供網頁調用的方法必須帶有一個類型為NSMutableArray類型的參數
2.引擎默認是在主線程異步調用插件方法,因此需要長時間耗時的阻塞操作,最好放在非主線程中進行,以免造成App卡死。在主工程的plugin.xml中添加此接口的信息,規則如下
plugin.xml中注冊插件方法的基本規則
1.每一個插件唯一對應了一個節點,節點中必須聲明此插件的名字 用name字段表示
2.在插件節點內,每個 節點對應了一個暴露給網頁的插件方法,方法名字用name字段表示
在網頁中寫一個按鈕,在點擊按鈕的JS事件中調用uexDemoPlugin.helloWorld();
html部分
JavaScript部分
varhelloWorld=function(){
uexDemoPlugin.helloWorld();
}好了 讓我們運行工程,點擊按鈕看一下效果吧!
3.2.2 網頁傳值給原生環境
本小節示范了如何從網頁傳值給原生環境
在EUExDemoPlugin類中實現一個方法sendValue:
-(void)sendValue:(NSMutableArray*)inArguments{
//打印傳入的參數個數
NSLog(@"arguments count : %@",@(inArguments.count));
//打印每個參數的描述,和參數所在的類的描述
for(NSIntegeri=0;i
id obj=inArguments[i];
NSLog(@"value : %@ , class : %@ ",[obj description],[[objclass]description]);
}
}在plugin.xml中添加方法
在網頁中調用的JS如下
uexDemoPlugin.sendValue("aaa",12,true,["x","y"],{key:"value"});結果如下
JaveScript—>OC傳值的轉換規則
由上述例子可以看到,JSValue按照如下規則轉換成了NSObject
JSValue
NSObject
String
NSString
Number
NSNumber
Boolean
NSNumber
Array
NSArray
Object
NSDictionary
null,undefined
NSNull
Function
不支持(注)
注: 任何function都會被轉換成一個空的NSDictionary,其所有信息都會丟失;
3.2.3 網頁傳遞JSON數據給原生環境上述直接傳值在Android方面會引起諸多兼容性問題,并且后續的拓展性較差。因此建議統一采用JSON數據格式進行傳值。
AppCan引擎內封裝了SBJSON庫用于JSON的解析,之后的示例代碼都會采用這個庫進行JSON的序列化/反序列化。
當然,如果您偏好其他的JSON解析方法,只需要替換其中的JSON解析步驟即可,完全沒有任何影響。從AppCan插件開發包的AppCan引擎頭文件文件夾中找到JSON文件夾(這個文件夾中包含了SBJSON庫的頭文件),引入靜態庫工程。在EUExDemoPlugin類中引入JSON.h
在EUExDemoPlugin類中實現一個方法sendJSONValue:,并在config.xml中添加相應的方法。
-(void)sendJSONValue:(NSMutableArray*)inArguments{
if([inArguments count]<1){
//當傳入的參數為空時,直接返回,避免數組越界錯誤。
return;
}
id json=[inArguments[0]JSONValue];
NSLog(@"json : %@ class : %@",[json description],[[jsonclass]description]);
}在網頁中調用的JS如下
varjson={
key1:"aaa",
key2:12,
key3:true,
key4:["x","y"],
key5:{key:"value"}
}
uexDemoPlugin.sendJSONValue(JSON.stringify(json));結果如下
3.2.4 原生異步回調JS給網頁此小節示范了如何通過EUtility工具類中的方法執行網頁中的JS,實現OC-->JavaScript的操作
異步回調的本質是執行一段JS腳本在EUExDemoPlugin類中引入EUtility.h,這個頭文件在engineHeader文件夾中,之前就應該已經引入工程了。EUtility是引擎中的工具類,此類中主要封裝了原生操作網頁的一系列方法。
由于方法較多,這里就不一一解釋了,可以直接參看EUtility.h中的方法注釋。
如果EUtility.h中報Expected a Type錯誤,在EUtility.h中引入系統庫UIKit(#import )即可解決
//EUtility.h中和JS相關的方法有4個
//在指定網頁中執行JS腳本
+(void)brwView:(EBrowserView*)inBrwView evaluateScript:(NSString*)inScript;
//在主窗口中執行JS腳本
+(void)evaluatingJavaScriptInRootWnd:(NSString*)script;
//在最頂端的窗口中執行JS腳本
+(void)evaluatingJavaScriptInFrontWnd:(NSString*)script;
//以及對上述3個方法的進一步封裝
+(void)uexPlugin:(NSString*)pluginName callbackByName:(NSString*)functionName withObject:(id)obj andType:(uexPluginCallbackType)type inTarget:(id)target;
//詳細參數說明請見EUtility.h中的注釋在EUExDemoPlugin類中實現一個方法doCallback:,并在config.xml中添加相應的方法。
-(void)doCallback:(NSMutableArray*)inArguments{
NSDictionary*dict=@{
@"key":@"value"
};
//構造JavaScript腳本
//[dict JSONFragment] 可以把NSString NSDictionary NSArray 轉換成JSON字符串
NSString*jsStr=[NSStringstringWithFormat:@"if(uexDemoPlugin.cbDoCallback){uexDemoPlugin.cbDoCallback('%@')}",[dictJSONFragment]];
//回調給當前網頁
[EUtilitybrwView:self.meBrwView evaluateScript:jsStr];
}在網頁中注冊回調函數cbDoCallback,并調用doCallback方法在cbDoCallback函數中,我們封裝一個JS方法showDetails用于展示回調結果
//封裝一個JS方法用于查看回調結果
varshowDetails=function(){
varcount=arguments.length;
varresult="";
for(inti=0;i
result+=("type:"+typeof(obj)+"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n value:"+obj+"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n");
}
alert(result);
}
window.uexOnload=function(){
uexDemoPlugin.cbDoCallback=function(jsonStr){
//回調的參數是JSON字符串,需要解析成Object
varjson=JSON.parse(jsonStr);
//查看回調結果
showDetails(jsonStr,json,json.key);
}
};uexDemoPlugin.doCallback();
注冊異步回調的JS方法注意事項注冊回調函數必須要在window.uexOnload或者AppCan.ready(如果你已經引入了appcan.js)中進行
插件調試時,建議使用window.uexOnload,避免AppCanJSSDK可能的干擾調用接口后,控制臺顯示數據如下
每次都要如上構造JavaScript腳本確實有些繁瑣,因此EUtility封裝了一個更簡潔的方法,回調過程可以省略如下
NSDictionary*dict=@{
@"key":@"value"
};
[EUtilityuexPlugin:@"uexDemoPlugin"
callbackByName:@"cbDoCallback"
withObject:dict
andType:uexPluginCallbackWithJsonString
inTarget:self.meBrwView];由于不同的方法可能都需要進行回調,因此可以進行進一步封裝,方便復用
/**
* 異步回調方法的封裝
*
* @param funcName 回調函數名
* @param obj 回調的對象
*/
-(void)callbackJSONWithName:(NSString*)funcNameobject:(id)obj{
[EUtilityuexPlugin:@"uexDemoPlugin"
callbackByName:funcName
withObject:obj
andType:uexPluginCallbackWithJsonString
inTarget:self.meBrwView];
}NSDictionary*dict=@{
@"key":@"value"
};
//然后在插件接口中直接調用此方法即可
[selfcallbackJSONWithName:@"cbDoCallback"object:dict];
3.2.5 同步返回值給網頁此回調方式僅限3.3+引擎
方法可以直接同步返回值給網頁,避免繁瑣的異步過程。
注意若使用同步回調,需避免在返回值前執行耗時的操作,以免影響用戶體驗。在EUExDemoPlugin類中實現一個方法doSyncCallback:
-(NSDictionary*)doSyncCallback:(NSMutableArray*)inArguments{
return@{
@"key1":@"value1",
@"key2":@(NO),
@"key3":@{
@"subKey":@"subValue"
}
};
}
如何在plugin.xml中注冊一個同步方法在plugin.xml中聲明此方法,并且設置屬性sync為"true",表明這是一個同步方法
在網頁中如下所示調用JS進行測試
//將獲取的返回值賦值給obj
varobj=uexDemoPlugin.doSyncCallback();
//查看obj的結構
showDetails(obj,obj.key1,obj.key2,obj.key3.subKey);控制臺顯示的結果如下
OC—>JavaScript同步返回值的轉換規則NSObject
JSValue
NSString
String
@ YES,@ NO
Boolean
其他NSNumber
Number
NSArray
Array
NSDictionary
Object
nil,NSNull
null
block
Function(注)
注:返回block會被轉化成JS中的function,但block中的代碼如果需要繼續與JS交互,可能會用到JavaScriptCore.framework這個系統庫中的方法,這里就不做詳細介紹了,您可以自行去研究。
3.3 插件UI操作
3.3.1 在網頁上添加View
本小節介紹了插件如何在網頁上添加原生的View
添加view的限制原生View總是會在網頁頂端,即網頁中所有
等網頁元素上方在EUExDemoPlugin類中實現方法addView: removeView并在plugin.xml中聲明addView方法有一個必選參數isScrollable 用來控制被添加的view是跟隨網頁滑動 還是固定在窗口上EUtility 中有2個方法brwView: addSubviewToScrollView:,brwView: addSubview:分別對應了上述2種情況
用一個實例變量aView來管理被添加的View
@property(nonatomic,strong)UIView*aView;-(void)addView:(NSMutableArray*)inArguments{
if(self.aView){
//如果已經添加了view 直接返回
return;
}
if([inArguments count]<1){
//參數不傳 直接返回
return;
}
id info=[inArguments[0]JSONValue];
if(!info||![info isKindOfClass:[NSDictionaryclass]]){
//參數解析后不是NSDictionary 直接返回
return;
}
if(!info[@"isScrollable"]){
//如果參數信息不包含isScrollable這個鍵 直接返回
return;
}
BOOL isScroll=[info[@"isScrollable"]boolValue];
//新建一個view,并將其背景設置為紅色
UIView*view=[[UIViewalloc]initWithFrame:CGRectMake(10,400,300,200)];
view.backgroundColor=[UIColorredColor];
if(isScroll){
[EUtilitybrwView:self.meBrwView addSubviewToScrollView:view];
}else{
[EUtilitybrwView:self.meBrwView addSubview:view];
}
//插件對象持有此view,方便對其進行移除操作
self.aView=view;
}
-(void)removeView:(NSMutableArray*)inArguments{
if(self.aView){
[self.aView removeFromSuperview];
self.aView=nil;
}
}在網頁中我們設置一個單選框用于控制是否跟隨網頁滑動,相關HTML以及JS代碼如下
isScrollable
varaddView=function(){
//獲取單選框的網頁對象
varcheckbox1=document.getElementById("checkbox1");
//以單選框是否被勾選作為isScrollable的值,是個boolean
varjson={
isScrollable:checkbox1.checked
}
uexDemoPlugin.addView(JSON.stringify(json));
}
varremoveView=function(){
uexDemoPlugin.removeView();
}運行結果如下
在網頁中展示一個viewController本小節介紹了插件如何在網頁上展示原生的ViewController。
展示viewController的限制只能以present的方式從當前的網頁controller中切換到你自己的viewController中;在插件工程中新建一個繼承自UIViewController的類uexDemoPluginViewController,然后在它的view上添加一個按鈕,按鈕會調用EUExDemoPlugin中的dismissViewController方法;
uexDemoPluginViewController.h
@classEUExDemoPlugin;
@interfaceuexDemoPluginViewController:UIViewController
-(instancetype)initWithEUExObj:(EUExDemoPlugin*)euexObj;
@end
uexDemoPluginViewController.m
#import "uexDemoPluginViewController.h"
#import "EUExDemoPlugin.h"
@interfaceuexDemoPluginViewController()
@property(nonatomic,weak)EUExDemoPlugin*euexObj;
@end
@implementationuexDemoPluginViewController
-(instancetype)initWithEUExObj:(EUExDemoPlugin*)euexObj{
self=[superinit];
if(self){
_euexObj=euexObj;
}
returnself;
}
-(void)viewWillAppear:(BOOL)animated{
[superviewWillAppear:animated];
self.view.backgroundColor=[UIColorwhiteColor];
UIButton*button=[UIButtonbuttonWithType:UIButtonTypeSystem];
button.frame=CGRectMake(100,100,180,60);
[button setTitle:@"關閉ViewController"forState:UIControlStateNormal];
[button addTarget:selfaction:@selector(onCloseButtonClick:)forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
-(void)onCloseButtonClick:(id)sender{
[self.euexObj dismissViewController];
}EUExDemoPlugin類中實現方法presentController并在plugin.xml中聲明
@property(nonatomic,strong)uexDemoPluginViewController*aViewController;-(void)presentController:(NSMutableArray*)inArguments{
if(self.aViewController){
return;
}
uexDemoPluginViewController*controller=[[uexDemoPluginViewController alloc]initWithEUExObj:self];
[EUtilitybrwView:self.meBrwView presentViewController:controller animated:YES completion:nil];
self.aViewController=controller;
}EUExDemoPlugin類中實現方法dismissViewController 關閉被present的ViewController,并給前端一個監聽回調onControllerClose
-(void)dismissViewController{
if(self.aViewController){
[self.aViewController dismissViewControllerAnimated:YES completion:^{
[selfcallbackJSONWithName:@"onControllerClose"object:nil];
self.aViewController=nil;
}];
}
}前端JS中封裝相關的回調方法,然后執行uexDemoPlugin.presentController();
//在window.uexOnload中
uexDemoPlugin.onControllerClose=function(){
alert("controller 被關閉!")
}結果如下
4.生成插件包此步驟應該在您插件所有接口封裝完畢,并在調試工程中測試完成后再進行
以下說明中均以范例插件uexDemoPlugin為例進行的操作。
在實際操作時,應該將所有出現的DemoPlugin替換成您自己的插件名字。
4.1 編譯插件靜態庫.a文件確認插件調試工程已經關閉,然后打開靜態庫工程EUExDemoPlugin.xcodeproj
左上角的schemes管理中,選擇EUExDemoPlugin - Generic iOS Device
點擊Product-Build,生成插件的.a文件libuexDemoPlugin.a
4.2 編輯plugin.xmlplugin.xml主要記錄了插件的接口信息
此plugin.xml和插件調試工程中的plugin.xml完全一樣,您可以直接拷貝過來如果您的調試工程在同時調試多個插件,那么在拷貝完成之后應刪去節點中其他插件的信息
或者以下列文本為基礎,在節點中按照plugin.xml中注冊插件方法的基本規則和如何在plugin.xml中注冊一個同步方法完成plugin.xml的編輯
plugin.xml空白模板,是一個標準的xml文件
最終完成的plugin.xml示例如下
4.3編輯info.xmlinfo.xml主要記錄了插件的版本信息
示例模板如下
uexName="name"version="3.0.x"build="x">
其中 name 替換成uex開頭的插件名 x替換成當前插件的版本號(非負整數)
然后向plugin節點中加入各個版本的簡介,這些簡介以倒序加入,由一個節點和多個(可以為0個)節點構成。節點記錄了當前版本的簡介
節點記錄了歷史版本的簡介
當插件版本更新時,應該講當前的節點改為節點,同時在其之前添加新的節點
最終完成的info.xml范例如下
uexName="uexDemoPlugin"version="3.0.1"build="1">
1:添加其他開發說明的示例代碼
0:AppCan iOS插件范例
4.4 獲得插件包新建名為uexDemoPlugin的文件夾并進入。如果您進行了前文中提及所有的可選操作,那么在插件工程目錄下面,應該自動生成了此文件夾,可以直接使用。
將libuexDemoPlugin.a,plugin.xml,info.xml拷貝至此文件夾中。如果您進行了前文中提及所有的可選操作,那么編譯工程出來的.a文件將自動在此目錄中生成。
根據具體情況,將可能存在的以下文件拷貝至此文件夾中,具體請看5.其他開發說明中的說明第三方庫依賴的.bundle文件
第三方庫的靜態.framework文件
插件資源包uexDemoPlugin.bundle
插件配置文件uexDemoPlugin.plist
全部拷貝工作完成后,uexDemoPlugin文件夾內的內容如下圖所示
以上所有步驟均完成后,返回上級目錄,壓縮uexDemoPlugin文件夾,得到插件zip包。
此zip包可以直接上傳作為自定義插件包(目前ios自定義插件只支持在線打包環境,ide打包環境暫不支持)使用。
5.其他開發說明以下是一些您在開發過程中可能需要的說明。
所有說明均可在范例工程中找到相應的示例代碼。
5.1 插件如何引入第三方庫
插件引入第三方庫的具體規則如下
第三方Library(.a文件),直接引入插件工程參與編譯即可。
資源捆綁包(.bundle)和第三方靜態framework(.framework),需要引入插件工程參與編譯,生成插件包時也需要單獨放入壓縮包中,和插件.a處于同一目錄下。調試時,.bundle和.framework需要在調試工程中再引入一遍,否則會無法找到相應的文件。
建議插件工程將引入的.bundle和.framework文件添加到Copy Files的Build Phase中,這樣可以在編譯時將這些文件直接復制至uexDemoPlugin文件夾中
動態framework,目前暫不支持。
5.2 插件如何引用資源文件本小節主要介紹了如何建立插件自己的資源捆綁包(.bundle文件)以供使用。
這里的資源文件包括但不限于xib,storyboard,png,jpg,json,xml,js,plist等文件
5.2.1 生成插件資源捆綁包的target選中插件靜態庫工程,然后點擊菜單欄中的File - New - Target.. ,在彈出的對話框中選擇OS X - Framework & Library - Bundle
product Name取名為uexDemoPluginBundle,點擊finish完成創建。
修改此target的如下Build Settings將Product Name對應的值修改為 uexDemoPlugin
將Pre-configuration Build Products Path 修改為$SRCROOT/uexDemoPlugin(注1)
將Code Signing Identity 修改為Don't Code Sign
將Combine High Resolution Artwork修改為No(注2)
將Info.plist File的值置為空
在工程的uexDemoPluginBundle群組下,找到info.plist這個文件,右鍵選擇delete,然后選擇Move to Trash以刪除該文件
為靜態庫target添加此bundle的target的依賴(注3)修改EUExDemoPlugin這個target的Build Phases ,在Target Dependicies中添加剛剛創建的bundle的target
注1:此settings是為了讓build時將此bundle直接生成在uexDemoPlugin文件夾中
注2:如果你的資源包中不包含圖片文件,那么此設置可跳過。
注3:這個設置是為了保證在插件clean時可以清除生成的bundle文件,在插件build時會自動生成新的bundle文件
接下來,可以把插件需要的資源文件全部添加至uexDemoPluginBundle這個target中即可
5.2.2 如何引用插件bundle中的資源文件EUtility提供了方法bundleForPlugin:用以尋找插件bundle對應的NSBundle實例。然后用NSBundle的方法pathForResource: ofType:獲取資源路徑加載資源即可。
bundle加載@ 2x ,@ 3x圖片文件的處理方法
獲取到NSBundle實例后,用NSBundle的pathForResource: ofType:并不能自動識別@ 2x,@ 3x的圖片文件,最好用resourcePath方法獲得實際路徑,然后拼接得到圖片路徑。
示例如下
NSBundle*pluginBundle=[EUtilitybundleForPlugin:@"uexDemoPlugin"];
//從bundle中讀取資源圖片文件的示例
//直接用[pluginBundle pathForResource:@"sun" ofType:@"png"];只能匹配"sun.png"這個文件的路徑,找不到會返回nil,而不會尋找文件"sun@2x.png"和"sun@3x.png".
NSString*path=[[pluginBundle resourcePath]stringByAppendingPathComponent:@"sun.png"];
UIImage*image=[UIImageimageWithContentsOfFile:path];
5.2.3 插件如何進行讀取國際化文件Localizable.strings將國際化文件Localizable.strings放入插件bundle中,然后用EUtility.h中的方法uexPlugin: localizedString:得到國際化的字符串.示例如下
label.text=[EUtilityuexPlugin:@"uexDemoPlugin"localizedString:@"title"];
5.3 插件如何獲取系統事件
5.3.1 ApplicationDelegate事件
AppCan會將大部分ApplicationDelegate事件分發到每個插件入口類,插件入口類用相應的類方法接收即可
目前插件入口類可供接收的類方法有
+(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
+(void)application:(UIApplication*)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken;
+(void)application:(UIApplication*)app didFailToRegisterForRemoteNotificationsWithError:(NSError*)err;
+(void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo;
+(void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo fetchCompletionHandler:(void(^)(UIBackgroundFetchResult))completionHandler
+(void)application:(UIApplication*)application didReceiveLocalNotification:(UILocalNotification*)notification;
+(BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url;
+(BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation;
+(void)applicationWillResignActive:(UIApplication*)application;
+(void)applicationDidBecomeActive:(UIApplication*)application;
+(void)applicationDidEnterBackground:(UIApplication*)application;
+(void)applicationWillEnterForeground:(UIApplication*)application;
+(void)applicationWillTerminate:(UIApplication*)application;
+(void)applicationDidReceiveMemoryWarning:(UIApplication*)application;
+(void)application:(UIApplication*)application performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem completionHandler:(void(^)(BOOL))completionHandler
示例:
//EUExDemoPlugin.m中
staticNSDictionary*AppLaunchOptions;
+(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions{
NSLog(@"app launched");
//存儲launchOptions
AppLaunchOptions=launchOptions;
returnYES;
}
5.3.2 AppCan系統事件
AppCan引擎會額外分發如下事件至每個插件入口類
+(void)rootPageDidFinishLoading;//root頁面加載完成的事件,在此事件觸發后才能有效執行回調網頁的相關方法
示例:
//第一個網頁(root頁面)加載完成時會觸發此事件
//部分事件(比如application:didFinishLaunchingWithOptions:)觸發時,第一個網頁可能還沒加載完成,因此無法當時回調給網頁
//這些回調應該延遲至這個事件觸發時再回調給root頁面
+(void)rootPageDidFinishLoading{
NSString*jsStr=[NSStringstringWithFormat:@"if(uexDemoPlugin.onAppLaunched){uexDemoPlugin.onAppLaunched('%@');}",[AppLaunchOptionsJSONFragment]];
//在root頁面執行JS腳本
[EUtilityevaluatingJavaScriptInRootWnd:jsStr];
AppLaunchOptions=nil;
}
5.4 插件如何配置info.plist新建uexDemoPlugin.plist文件,可以通過Xcode新建,也可以在文本編輯器中直接輸入如下內容并另存為uexDemoPlugin.plist
打開剛剛新建的plist文件,然后在root下新建名為UexKeyValues的key,其Type選為Dictionary
在UexKeyValues這個dictionary中添加你需要添加到info.plist中的內容,打包服務器會自動將這些內容合入最后打包工程的info.plist中
配置完成后,將此uexDemoPlugin.plist放入uexDemoPlugin文件夾中
6.常見問題
上傳插件時提示目錄結構錯誤檢查zip包目錄結構是否缺失zip包解壓縮后應該只有一個uexXXX開頭的文件夾
文件夾內至少有libuexXXX.a,info.xml,plugin.xml這3個文件
首次上傳插件時設置的插件名稱應該是uex開頭,且應該與info.xml,plugin.xml中的名稱保持一致
如果是更新插件,確認info.xml中的版本號正確的遞增了,以及節點正確填寫了
在線打包時出現Undefined symbols for architecture xxx類型的報錯:
出現這種錯誤主要有以下幾種原因
生成.a的時候沒有選擇Generic iOS Device或者在用命令行編譯時沒有注明-sdk iphoneos,導致缺少對應的架構。解決方法:正確編譯引擎.a并重新生成插件包進行在線打包
缺少依賴的第三方庫或者第三方庫本身架構缺失解決方法:添加同時擁有armv7和arm64架構的第三方庫并重新生成插件包進行在線打包
缺少系統依賴庫.如果這個庫的依賴iOS版本比AppCan引擎的依賴版本高,那么此插件只能配合自定義引擎使用
反之,請去AppCan引擎github提issue或者在AppCan官方論壇發帖說明,我們會第一時間進行反饋.
目前AppCan引擎的依賴版本為iOS 7.0
在線打包時出現duplicate symbols for architecture xxx類型的報錯:
出現這種錯誤的主要原因是類名沖突,請先根據日志找到沖突的類名以及它們分別所屬的文件
如果是您的插件和非官方的插件沖突請聯系插件作者協商解決
如果您的插件和官方插件或者引擎沖突如果此類是源自知名第三方庫源碼(比如SDWebImage等等),可以嘗試只包含這些第三庫的頭文件使用
如果此類是您的自定義類或者包含您的自定義代碼,那么應該優先嘗試在類名前加上前綴避免沖突
如果此類屬于第三方.a,那么應該嘗試用libtool等工具將沖突的.o拆分出來,然后重新合并
如果以上方法都無法解決并且沖突來源于引擎,那么只能您的插件只能用自定義引擎,修改引擎源碼配合使用
總結
以上是生活随笔為你收集整理的ios 开发控件中心点_AppCan的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 天龙八部元宝兑换代码
- 下一篇: CAN总线bus-off错误恢复处理