HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

个推官方升级后PHP-SDK不能推送的问题解决方法

个推

最近,个推官方更新了SDK。可能服务端也进行了升级,8月以后使用旧的php-sdk出现不能推送的情况。
升级php-sdk,问题同样没有解决。打开php报错显示,提示 “Can't use method return value in write context”。升级php至5.6.x后解决。

小结:
1、php-sdk不更新,导致个推推送失败。
2、新版本的php-sdk不支持5.4.x版本的php环境。

继续阅读 »

最近,个推官方更新了SDK。可能服务端也进行了升级,8月以后使用旧的php-sdk出现不能推送的情况。
升级php-sdk,问题同样没有解决。打开php报错显示,提示 “Can't use method return value in write context”。升级php至5.6.x后解决。

小结:
1、php-sdk不更新,导致个推推送失败。
2、新版本的php-sdk不支持5.4.x版本的php环境。

收起阅读 »

分享 PHP后端 + APP前端工程 实例-H5资讯APP , 开源工程下载

下载 PHP 分享源码

5资讯APP应用,是以资讯类APP为例,它的功能包括:前端APP模块,和 后端PHP 结合。

当新手们无从下手时,不知道怎么开始,怎么连接服务器获得数据,从实例中参考,可以让你认知大大提升,不需为一个简单的功能头痛几天都搞不定,从中举一反三,从而提升开发效率。

本案例是用沉浸式,就是把状态栏这部分空间也占据。

主要展示了,页面预加载,打开页面,页面传值,多图片上传,下载图片, 注册,登录,微信登录,分享,微信 和 支付宝支付,推送消息 等等,

让新手更加容易理解,学习起来也更容易举一反三。

使用的前端 APP 开发工具:HBuilder , 打包方式:在线打包

扫一扫下载安卓 dome app 体验

APP 部分页面展示

目前PHP 后台暂时使用的是:phpmywind 作为后台管理,但它不是必须,不一定要使用它,也可以使用其他的后台管理

PHP 后端功能包括:

1、文章发布,课程发布,栏目设置
2、用户管理,消息推送,应用分享,APP版本管理
3、商品订单管理,支付方式管理,

PHP 后台管理页面

php 后台文件结构:

下载及源码下载详情的介绍,可以到这里查看:http://www.html5-app.com/show/70

继续阅读 »

5资讯APP应用,是以资讯类APP为例,它的功能包括:前端APP模块,和 后端PHP 结合。

当新手们无从下手时,不知道怎么开始,怎么连接服务器获得数据,从实例中参考,可以让你认知大大提升,不需为一个简单的功能头痛几天都搞不定,从中举一反三,从而提升开发效率。

本案例是用沉浸式,就是把状态栏这部分空间也占据。

主要展示了,页面预加载,打开页面,页面传值,多图片上传,下载图片, 注册,登录,微信登录,分享,微信 和 支付宝支付,推送消息 等等,

让新手更加容易理解,学习起来也更容易举一反三。

使用的前端 APP 开发工具:HBuilder , 打包方式:在线打包

扫一扫下载安卓 dome app 体验

APP 部分页面展示

目前PHP 后台暂时使用的是:phpmywind 作为后台管理,但它不是必须,不一定要使用它,也可以使用其他的后台管理

PHP 后端功能包括:

1、文章发布,课程发布,栏目设置
2、用户管理,消息推送,应用分享,APP版本管理
3、商品订单管理,支付方式管理,

PHP 后台管理页面

php 后台文件结构:

下载及源码下载详情的介绍,可以到这里查看:http://www.html5-app.com/show/70

收起阅读 »

uni-app 教程更新! 共28讲,累计时长9小时!全面讲解 uni-app (从基础到进阶 + 小实战)^_^

uniapp 教程 uniapp


课程章节共28讲,累计时长9小时!全面讲解 uni-app ^_^

  1. 介绍、部署、目录结构 2. 样式与布局 3. 配置相关 4. 生命周期
  2. 模板语法及数据绑定 6. class 及 style 7. 事件及事件绑定 8. 基础组件
  3. 表单及表单组件 10. 导航及页面传参 11. 多媒体组件 12. 网络请求
  4. 照片选择及预览 14. 文件上传及下载 15. 数据缓存 16. 设备相关
  5. 导航设置 18. 下拉刷新 19. 上拉加载 20. 跨端兼容
  6. 交互反馈 22. 第三方登录详解 23. 分享接口 24. 使用 iconfont 字体库
  7. 打包及课程推荐 26. 在 uni-app 中使用 vue.js 27. 自定义组件封装及调用 28. 新闻列表及详情实战

详情请访问 http://www.hcoder.net/index/uniapp

继续阅读 »


课程章节共28讲,累计时长9小时!全面讲解 uni-app ^_^

  1. 介绍、部署、目录结构 2. 样式与布局 3. 配置相关 4. 生命周期
  2. 模板语法及数据绑定 6. class 及 style 7. 事件及事件绑定 8. 基础组件
  3. 表单及表单组件 10. 导航及页面传参 11. 多媒体组件 12. 网络请求
  4. 照片选择及预览 14. 文件上传及下载 15. 数据缓存 16. 设备相关
  5. 导航设置 18. 下拉刷新 19. 上拉加载 20. 跨端兼容
  6. 交互反馈 22. 第三方登录详解 23. 分享接口 24. 使用 iconfont 字体库
  7. 打包及课程推荐 26. 在 uni-app 中使用 vue.js 27. 自定义组件封装及调用 28. 新闻列表及详情实战

详情请访问 http://www.hcoder.net/index/uniapp

收起阅读 »

微信授权登录后跳回app无任何回调事件

微信登录

微信授权登录可以调起微信,但是在微信上点击确认登陆后跳回app,但是之后无任何回调事件。

问题原因:

1 因为我在集成Facebook登录的时候,在 AppDelegate.m 代码中按照FB示例,加入了如下代码:

2 以上代码会覆盖 AppDelegate.m 文件中同名的函数,造成拦截了微信授权登录的回掉事件,而并没有传给5+的相应事件来处理。

解决方法:

1 将FB的函数和5+的同名函数合并为如下:

继续阅读 »

微信授权登录可以调起微信,但是在微信上点击确认登陆后跳回app,但是之后无任何回调事件。

问题原因:

1 因为我在集成Facebook登录的时候,在 AppDelegate.m 代码中按照FB示例,加入了如下代码:

2 以上代码会覆盖 AppDelegate.m 文件中同名的函数,造成拦截了微信授权登录的回掉事件,而并没有传给5+的相应事件来处理。

解决方法:

1 将FB的函数和5+的同名函数合并为如下:

收起阅读 »

微信授权登录无响应,不回跳APP

微信登录

微信授权登录可以调起微信,但是在微信上点击确认登陆后无响应,不回跳APP。

解决方法:

1 参考文章做排除:http://ask.dcloud.net.cn/article/309

2 我遇到的问题是因为下图第3部的item没有修改为自己的appid。并且这个在IOS平台5+SDK技术白皮书.docx并没有提及。

3 修改了以上item的值之后,微信授权回掉成功。

继续阅读 »

微信授权登录可以调起微信,但是在微信上点击确认登陆后无响应,不回跳APP。

解决方法:

1 参考文章做排除:http://ask.dcloud.net.cn/article/309

2 我遇到的问题是因为下图第3部的item没有修改为自己的appid。并且这个在IOS平台5+SDK技术白皮书.docx并没有提及。

3 修改了以上item的值之后,微信授权回掉成功。

收起阅读 »

微信授权登录跳出手机号码登录方式

微信登录

在HBuilder真机运行和Xcode集成中微信授权登录成功,但是提交到App Store审核上线后点击微信授权登录,会跳出微信手机号码登录方式(会让你填入手机号码,然后会微信下发一条短信给你,让你点击短信的链接完成登录,实际上这个短信链接无法呼其原app)

解决方法:

  1. 参考文章做排查(注意所有步骤):http://ask.dcloud.net.cn/article/309
  2. 如何上述核对没有任何问题,请注意 /Supporting Files/info.plist 文件中 LSApplicationQueriesSchemes 项,不可缺少如下红框里面的项目(并未确定streamapp还是hbuilder会造成影响,因为需要提交到App Store才可以验证,本地调试不会出现):

https://oscimg.oschina.net/oscnet/a1ad3558d27723eea7fc0f0f93379d96d13.jpg

继续阅读 »

在HBuilder真机运行和Xcode集成中微信授权登录成功,但是提交到App Store审核上线后点击微信授权登录,会跳出微信手机号码登录方式(会让你填入手机号码,然后会微信下发一条短信给你,让你点击短信的链接完成登录,实际上这个短信链接无法呼其原app)

解决方法:

  1. 参考文章做排查(注意所有步骤):http://ask.dcloud.net.cn/article/309
  2. 如何上述核对没有任何问题,请注意 /Supporting Files/info.plist 文件中 LSApplicationQueriesSchemes 项,不可缺少如下红框里面的项目(并未确定streamapp还是hbuilder会造成影响,因为需要提交到App Store才可以验证,本地调试不会出现):

https://oscimg.oschina.net/oscnet/a1ad3558d27723eea7fc0f0f93379d96d13.jpg

收起阅读 »

facebook登录插件(ios版)

facebook

贴出源码回报社区!

下面贴出关键代码,稍后完善更多细节步骤。个人ios开发经验不多,如果有更好的集成方式,请大家分享!

前提:

  1. 能在Xcode中成功运行官方给出的离线打包演示应用 HBuilder-Hello (iOS平台5+SDK集成)
  2. 注册了facebook开发者平台,并完成了官方ios集成步骤,参考:https://developers.facebook.com/docs/facebook-login/ios?sdk=fbsdk&locale=zh_CN

源代码目录结构

1. js层代码调用插件login.js

    loginWithFacebook: function()  
    {  
        Common.showLoading();  

        setTimeout(function(){  
            if(App.timeout)  
            {  
                Common.hideLoading();  
                mui.toast(Common.messages.LOGIN_TIMEOUT,{ duration:'short', type:'div' });  
            }  
        }, 30000);  

        if(Common.debug)  
        {  
            setTimeout(function(){  
                App.facebookAuthSuccessCallBack('{"picture":{"data":{"height":50,"is_silhouette":false,"url":"https:\/\/platform-lookaside.fbsbx.com\/platform\/profilepic\/?asid=1799672767573170&height=50&width=50&ext=1529946538&hash=AeT-c8Gg8kljhvAC","width":50}},"name":"Nick Name","id":"1791872767573170","email":"last.first@gmail.com","permissions":{"data":[{"permission":"user_birthday","status":"granted"},{"permission":"user_hometown","status":"granted"},{"permission":"user_location","status":"granted"},{"permission":"user_posts","status":"granted"},{"permission":"user_gender","status":"granted"},{"permission":"user_link","status":"granted"},{"permission":"user_age_range","status":"granted"},{"permission":"email","status":"granted"},{"permission":"public_profile","status":"granted"}]},"age_range":{"min":21},"link":"https:\/\/www.facebook.com\/app_scoped_user_id\/YXNpZADpBWEZAmSmJjc01BLXJNTGt4UDd0RlZAicXpYVG5HbTV3YUl2T29uNNNNMWpGWndhVEVieTI4S18zcDVuUjczUHRmUHgwYm8xUkwtU1lHWENuRW9vUnE2clFtalVLaWs4SldxT05iWUxZAUlZA2bjRHemhE\/","gender":"male","birthday":"01\/01\/1990","location":{"id":"106324044973002","name":"Shanghai, China"}}');  
                //App.facebookAuthCancelCallBack();  
                //App.facebookAuthErrorCallBack("auth fail!");  
            }, 2000);  

        }else  
        {  
            //plus.facebookplug.logOut();  
            plus.facebookplug.logIn(App.facebookAuthSuccessCallBack, App.facebookAuthErrorCallBack);  
        }  

    },  

    // 这个回调函数会在 cn.shaketowin.app.SDK_WebApp.java 原生类中调用, 参考类中的excuteJSCode()方法  
    facebookAuthSuccessCallBack: function(data)  
    {  
        App.timeout = false;  

        if(data != null)  
        {  
            //alert("===facebookAuthSuccessCallBack(), data: " + JSON.stringify(data));  
            // data example: {"picture":{"data":{"height":50,"is_silhouette":false,"url":"https:\/\/platform-lookaside.fbsbx.com\/platform\/profilepic\/?asid=1791872767573170&height=50&width=50&ext=1529946538&hash=AeT-c8Gg8kljhvAC","width":50}},"name":"Nick Name","id":"1987872767573170","email":"last.first@gmail.com","permissions":{"data":[{"permission":"user_birthday","status":"granted"},{"permission":"user_hometown","status":"granted"},{"permission":"user_location","status":"granted"},{"permission":"user_posts","status":"granted"},{"permission":"user_gender","status":"granted"},{"permission":"user_link","status":"granted"},{"permission":"user_age_range","status":"granted"},{"permission":"email","status":"granted"},{"permission":"public_profile","status":"granted"}]},"age_range":{"min":21},"link":"https:\/\/www.facebook.com\/app_scoped_user_id\/YXNpZADpBWEZAmSmJjc01BLXJNTGt4UD09RlZAicXpYVG5HbTV3YUl2T29uVTNNMWpGWndhVEVieTI4S18zcDVuUjczUHRmUHgwYm8xUkwtU1lHWENuRW9vUnE2clFtalVLaWs4SldxT05iWUxZAUlZA2bjRHemhE\/","gender":"male","birthday":"01\/01\/1990","location":{"id":"116324086073002","name":"Shanghai, China"}}  
            try{  
                var facebookUser = JSON.parse(data);  
                facebookUser.unionid = facebookUser.id;  
                mui.toast(Common.messages.LOGIN_SUCCESS,{ duration:'short', type:'div' });  
                App.checkUserIsExist(facebookUser, "facebook");  
            }catch(err)  
            {  
                Common.hideLoading();  
                mui.toast(Common.messages.LOGIN_FAIL,{ duration:'long', type:'div' });  
                Common.log("====login.js, facebookAuthSuccessCallBack(), decode facebook user JSON string fail!");  
            }  
        }else  
        {  
            Common.hideLoading();  
            App.facebookAuthErrorCallBack();  
        }  
    },  

    // 这个回调函数会在 cn.shaketowin.app.SDK_WebApp.java 原生类中调用, 参考类中的excuteJSCode()方法  
    facebookAuthCancelCallBack: function()  
    {  
        App.timeout = false;  
        Common.hideLoading();  

        mui.toast(Common.messages.LOGIN_CANCEL,{ duration:'long', type:'div' });  
    },  

    // 这个回调函数会在 cn.shaketowin.app.SDK_WebApp.java 原生类中调用, 参考类中的excuteJSCode()方法  
    facebookAuthErrorCallBack: function(error)  
    {  
        App.timeout = false;  
        Common.hideLoading();  

        mui.toast(Common.messages.LOGIN_FAIL,{ duration:'long', type:'div' });  
    },

2. js层代码插件facebookplug.js

document.addEventListener( "plusready",  function()  
{  
    var facebookplug =  
    {  
        logIn : function (successCallback, errorCallback )  
        {  
            var success = typeof successCallback !== 'function' ? null : function(args)  
            {  
                successCallback(args);  
            },  
            fail = typeof errorCallback !== 'function' ? null : function(code)  
            {  
                errorCallback(code);  
            };  
            callbackID = window.plus.bridge.callbackId(success, fail);  

            return window.plus.bridge.exec('facebookloginplugin', "LogIn",  [callbackID]);  
        },  
        logOut : function ()  
        {  
            window.plus.bridge.exec('facebookloginplugin', "LogOut", []);  
        },  
    };  

    window.plus.facebookplug = facebookplug;  
}, true );

3. 原生层FacebookLoginPlugin.h

//  
//  FacebookLoginPlugin.h  
//  
//  

#include "PGPlugin.h"  
#include "PGMethod.h"  
#import <Foundation/Foundation.h>  

@interface FacebookLoginPlugin : PGPlugin  

- (void)LogIn:(PGMethod*)command;  
- (void)LogOut:(PGMethod*)command;  
@end

4. 原生层FacebookLoginPlugin.m

//  
//  FacebookLoginPlugin.m  
//  

#import "FacebookLoginPlugin.h"  
#import "PDRCoreAppFrame.h"  
#import "H5WEEngineExport.h"  
#import "PDRToolSystemEx.h"  
// 扩展插件中需要引入需要的系统库  
#import <LocalAuthentication/LocalAuthentication.h>  
#import <FBSDKCoreKit/FBSDKCoreKit.h>  
#import <FBSDKLoginKit/FBSDKLoginKit.h>  

@implementation FacebookLoginPlugin  

#pragma mark 这个方法在使用WebApp方式集成时触发,WebView集成方式不触发  

/*  
 * WebApp启动时触发  
 * 需要在PandoraApi.bundle/feature.plist/注册插件里添加autostart值为true,global项的值设置为true  
 */  
- (void) onAppStarted:(NSDictionary*)options{  

    NSLog(@"5+ WebApp启动时触发");  
    // 可以在这个方法里向Core注册扩展插件的JS  

}  

// 监听基座事件事件  
// 应用退出时触发  
- (void) onAppTerminate{  
    //  
    NSLog(@"APPDelegate applicationWillTerminate 事件触发时触发");  
}  

// 应用进入后台时触发  
- (void) onAppEnterBackground{  
    //  
    NSLog(@"APPDelegate applicationDidEnterBackground 事件触发时触发");  
}  

// 应用进入前天时触发  
- (void) onAppEnterForeground{  
    //  
    NSLog(@"APPDelegate applicationWillEnterForeground 事件触发时触发");  
}  

#pragma mark 以下为插件方法,由JS触发, WebView集成和WebApp集成都可以触发  

// 登录  
- (void)LogIn:(PGMethod*)commands  
{  
    if ( commands ) {  
        // CallBackid 异步方法的回调id,H5+ 会根据回调ID通知JS层运行结果成功或者失败  
        NSString* cbId = [commands.arguments objectAtIndex:0];  

        if ([FBSDKAccessToken currentAccessToken]) {  
            //NSLog(@"==========LogIn(), already have token");  

            [self GetUserInfo:cbId];  

        }else{  

            /*  
             * Facebook会对i应用访问的字段进行控制(https://developers.facebook.com/apps/193845774573048/review-status/)  
             * 应用审核说明:https://developers.facebook.com/docs/apps/review/#app-review  
             * 默认不需要审核的有:@"public_profile", @"email"  
             * 如下权限系统提交应用审核才可以访问: @"public_profile", @"email", @"user_age_range", @"user_birthday", @"user_gender", @"user_hometown", @"user_link", @"user_location"  
             * 如果需要访问相对应的字段,将字段放入的代码 [login logInWithReadPermissions:@[@"public_profile", @"email"] 参数即可  
             */  

            FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];  
            login.loginBehavior = FBSDKLoginBehaviorWeb;  
            [login logInWithReadPermissions:@[@"public_profile", @"email"]  
                                    handler:^(FBSDKLoginManagerLoginResult *result, NSError *error)  
            {  
                if (error) {  
                    [self CallWebAppJSFun: cbId status: @"ERROR" message: error.localizedDescription];  
                } else if (result.isCancelled) {  
                    [self CallWebAppJSFun: cbId status: @"ERROR" message: @"cancel"];  
                } else {  
                    // If you ask for multiple permissions at once, you  
                    // should check if specific permissions missing  
                    //NSLog(@"========Permission  2: %@",result.grantedPermissions);  

                    [self GetUserInfo:cbId];  
                }  

            }];  
        }  
    }  
}  

// 退出登录  
- (void)LogOut:(PGMethod *)command  
{  
    FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init];  
    [loginManager logOut];  
    // or  
    //[FBSDKAccessToken setCurrentAccessToken:nil];  
}  

// 获取用户信息  
- (void)GetUserInfo:(NSString *) cbId  
{  
    /*  
     * Facebook会对i应用访问的字段进行控制(https://developers.facebook.com/apps/193845774573048/review-status/)  
     * 应用审核说明:https://developers.facebook.com/docs/apps/review/#app-review  
     * 对应默认不需要审核的字段@"public_profile", @"email"的是@"id,name,first_name,last_name,email,picture.type(large)“  
     * 对应提交应用审核才可以访问字段有 @"cover,picture.type(large),id,name,first_name,last_name,gender,birthday,email,location,hometown,about,photos,age_range,link"  
     * 如果需要访问相对应的字段,将字段放入的代码 parameters:[NSDictionary dictionaryWithObject:@"cover,..." forKey:@"fields"] 参数即可  
     */  

    [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:[NSDictionary dictionaryWithObject:@"id,name,first_name,last_name,email,picture" forKey:@"fields"]]  
     startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {  

         if (!error) {  

             //NSLog(@"==========GetUserInfo(), username: %@", [result valueForKey:@"name"]);  
//             NSString *userID = [[FBSDKAccessToken currentAccessToken] userID];  
//             NSString *userName = [result valueForKey:@"name"];  
//             NSString *email =  [result valueForKey:@"email"];  
//             NSString *userImageURL = [NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=large", [[FBSDKAccessToken currentAccessToken] userID]];  

             NSData *jsonData = [NSJSONSerialization dataWithJSONObject:result options:NSJSONWritingPrettyPrinted error:nil];  

             NSString *userInfo =  [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];  

             //NSLog(@"==========GetUserInfo(), userInfo: %@", userInfo);  

             [self CallWebAppJSFun: cbId status: @"OK" message: userInfo];  
         }  
         else{  
             [self CallWebAppJSFun: cbId status: @"ERROR" message: error.localizedDescription];  
         }  
     }];  

}  

// 回调通知JS层  
- (void)CallWebAppJSFun:(NSString *) cbId  
                status:(NSString *) st  
                message:(NSString *) msg  
{  
    // 运行Native代码结果和预期相同,调用回调通知JS层运行成功并返回结果  
    // PDRCommandStatusOK 表示触发JS层成功回调方法  
    // PDRCommandStatusError 表示触发JS层错误回调方法  

    // 如果方法需要持续触发页面回调,可以通过修改 PDRPluginResult 对象的keepCallback 属性值来表示当前是否可重复回调, true 表示可以重复回调   false 表示不可重复回调  默认值为false  

    PDRPluginResult * pResult = nil;  

    if([st isEqualToString: @"OK"])  
    {  
        pResult = [PDRPluginResult resultWithStatus:PDRCommandStatusOK messageAsString: msg];  
    }else  
    {  
        // 如果Native代码运行结果和预期不同,需要通过回调通知JS层出现错误,并返回错误提示  
        pResult = [PDRPluginResult resultWithStatus:PDRCommandStatusError messageAsString:msg];  

    }  

    // 通知JS层Native层运行结果  
    [self toCallback:cbId withReslut:[pResult toJSONString]];  

}  

// 调用指纹解锁  
- (void)AuthenticateUser:(PGMethod*)command  
{  
    if (nil == command) {  
        return;  
    }  
    BOOL isSupport = false;  
    NSString* pcbid = [command.arguments objectAtIndex:0];  
    NSError* error = nil;  
    NSString* LocalReason = @"HBuilder指纹验证";  

    // Touch ID 是IOS 8 以后支持的功能  
    if ([PTDeviceOSInfo systemVersion] >= PTSystemVersion8Series) {  
        LAContext* context = [[LAContext alloc] init];  
        if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {  
            isSupport = true;  
            [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:LocalReason reply:^(BOOL success, NSError * _Nullable error) {  
                PDRPluginResult * pResult = nil;  

                if (success) {  

                    pResult = [PDRPluginResult resultWithStatus: PDRCommandStatusOK messageAsDictionary:@{@"state":@(0), @"message":@"成功"}];  
                }  
                else{  
                    NSDictionary* pStringError = nil;  
                    switch (error.code) {  
                        case LAErrorSystemCancel:  
                        {  
                            pStringError = @{@"state":@(-1), @"message":@"系统取消授权(例如其他APP切入)"};  
                            break;  
                        }  
                        case LAErrorUserCancel:  
                        {  
                            pStringError = @{@"state":@(-2), @"message":@"用户取消Touch ID授权"};  
                            break;  
                        }  
                        case LAErrorUserFallback:  
                        {  
                            pStringError  = @{@"state":@(-3), @"message":@"用户选择输入密码"};  
                            break;  
                        }  
                        case LAErrorTouchIDNotAvailable:{  
                            pStringError  = @{@"state":@(-4), @"message":@"设备Touch ID不可用"};  
                            break;  
                        }  
                        case LAErrorTouchIDLockout:{  
                            pStringError  = @{@"state":@(-5), @"message":@"Touch ID被锁"};  
                            break;  
                        }  
                        case LAErrorAppCancel:{  
                            pStringError  = @{@"state":@(-6), @"message":@"软件被挂起取消授权"};  
                            break;  
                        }  
                        default:  
                        {  
                            pStringError  = @{@"state":@(-7), @"message":@"其他错误"};  
                            break;  
                        }  
                    }  
                    pResult = [PDRPluginResult resultWithStatus:PDRCommandStatusError messageAsDictionary:pStringError];  

                }  

                [self toCallback:pcbid withReslut:[pResult toJSONString]];  
            }];  
        }  
        else{  
            NSDictionary* pStringError = nil;  
            switch (error.code) {  
                case LAErrorTouchIDNotEnrolled:  
                {  
                    pStringError  = @{@"state":@(-11), @"message":@"设备Touch ID不可用"};  
                    break;  
                }  
                case LAErrorPasscodeNotSet:  
                {  
                    pStringError  = @{@"state":@(-12), @"message":@"用户未录入Touch ID"};  
                    break;  
                }  

                default:  
                    break;  
            }  
        }  
    }  

    if (!isSupport) {  
        PDRPluginResult* pResult = [PDRPluginResult resultWithStatus:PDRCommandStatusError messageAsString:@"Device Not Support"];  
        [self toCallback:pcbid withReslut:[pResult toJSONString]];  
    }  
}  

@end

5. 原生层AppDelegate.m(在原有函数上新增一行FB的代码,这里仅贴出需要修改的函数的代码)

//  
//  AppDelegate.m  
//  

#import "AppDelegate.h"  
#import "PDRCore.h"  
#import "PDRCommonString.h"  
#import "ViewController.h"  
#import "PDRCoreApp.h"  
#import "DCADManager.h"  
#import "PDRCoreAppManager.h"  
#import <FBSDKCoreKit/FBSDKCoreKit.h>  

// 示例默认带开屏广告,如果不需要广告,可注释下面一行  
#define dcSplashAd  

@interface AppDelegate () <DCADManagerDelgate, PDRCoreDelegate>  
@property (strong, nonatomic) ViewController *h5ViewContoller;  
@end  

@implementation AppDelegate  

@synthesize window = _window;  
#pragma mark -  
#pragma mark app lifecycle  
/*  
 * @Summary:程序启动时收到push消息  
 */  
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  
{  

    BOOL ret = [PDRCore initEngineWihtOptions:launchOptions  
                                  withRunMode:PDRCoreRunModeNormal withDelegate:self];  

    DCADManager *adManager = [DCADManager adManager];  
    UIViewController* adViewController = [adManager getADViewController];  
    adManager.delegate = self;  
    UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];  
    self.window = window;  

    ViewController *viewController = [[ViewController alloc] init];  
    self.h5ViewContoller = viewController;  

    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];  
    self.rootViewController = navigationController;  
    navigationController.navigationBarHidden = YES;  
    if ( adViewController ) {  
        [navigationController pushViewController:adViewController animated:NO];  
    } else {  
        [self startMainApp];  
        self.h5ViewContoller.showLoadingView = YES;  
    }  
    self.window.rootViewController = navigationController;  
    [self.window makeKeyAndVisible];  

    [[FBSDKApplicationDelegate sharedInstance] application:application  
                             didFinishLaunchingWithOptions:launchOptions];  

    return ret;  
}  

#pragma mark -  
#pragma mark URL  

- (BOOL)application:(UIApplication *)application  
            openURL:(NSURL *)url  
  sourceApplication:(NSString *)sourceApplication  
         annotation:(id)annotation  
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options{  
    //NSLog(@"==========here(), url: %@", url);  
    [self application:application handleOpenURL:url];  

    [[FBSDKApplicationDelegate sharedInstance] application:application  
                                                                  openURL:url  
                                            sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]  
                                           annotation:options[UIApplicationOpenURLOptionsAnnotationKey]  
                    ];  

    return YES;  
}

6. Supporting Files/Info.plist文件

继续阅读 »

贴出源码回报社区!

下面贴出关键代码,稍后完善更多细节步骤。个人ios开发经验不多,如果有更好的集成方式,请大家分享!

前提:

  1. 能在Xcode中成功运行官方给出的离线打包演示应用 HBuilder-Hello (iOS平台5+SDK集成)
  2. 注册了facebook开发者平台,并完成了官方ios集成步骤,参考:https://developers.facebook.com/docs/facebook-login/ios?sdk=fbsdk&locale=zh_CN

源代码目录结构

1. js层代码调用插件login.js

    loginWithFacebook: function()  
    {  
        Common.showLoading();  

        setTimeout(function(){  
            if(App.timeout)  
            {  
                Common.hideLoading();  
                mui.toast(Common.messages.LOGIN_TIMEOUT,{ duration:'short', type:'div' });  
            }  
        }, 30000);  

        if(Common.debug)  
        {  
            setTimeout(function(){  
                App.facebookAuthSuccessCallBack('{"picture":{"data":{"height":50,"is_silhouette":false,"url":"https:\/\/platform-lookaside.fbsbx.com\/platform\/profilepic\/?asid=1799672767573170&height=50&width=50&ext=1529946538&hash=AeT-c8Gg8kljhvAC","width":50}},"name":"Nick Name","id":"1791872767573170","email":"last.first@gmail.com","permissions":{"data":[{"permission":"user_birthday","status":"granted"},{"permission":"user_hometown","status":"granted"},{"permission":"user_location","status":"granted"},{"permission":"user_posts","status":"granted"},{"permission":"user_gender","status":"granted"},{"permission":"user_link","status":"granted"},{"permission":"user_age_range","status":"granted"},{"permission":"email","status":"granted"},{"permission":"public_profile","status":"granted"}]},"age_range":{"min":21},"link":"https:\/\/www.facebook.com\/app_scoped_user_id\/YXNpZADpBWEZAmSmJjc01BLXJNTGt4UDd0RlZAicXpYVG5HbTV3YUl2T29uNNNNMWpGWndhVEVieTI4S18zcDVuUjczUHRmUHgwYm8xUkwtU1lHWENuRW9vUnE2clFtalVLaWs4SldxT05iWUxZAUlZA2bjRHemhE\/","gender":"male","birthday":"01\/01\/1990","location":{"id":"106324044973002","name":"Shanghai, China"}}');  
                //App.facebookAuthCancelCallBack();  
                //App.facebookAuthErrorCallBack("auth fail!");  
            }, 2000);  

        }else  
        {  
            //plus.facebookplug.logOut();  
            plus.facebookplug.logIn(App.facebookAuthSuccessCallBack, App.facebookAuthErrorCallBack);  
        }  

    },  

    // 这个回调函数会在 cn.shaketowin.app.SDK_WebApp.java 原生类中调用, 参考类中的excuteJSCode()方法  
    facebookAuthSuccessCallBack: function(data)  
    {  
        App.timeout = false;  

        if(data != null)  
        {  
            //alert("===facebookAuthSuccessCallBack(), data: " + JSON.stringify(data));  
            // data example: {"picture":{"data":{"height":50,"is_silhouette":false,"url":"https:\/\/platform-lookaside.fbsbx.com\/platform\/profilepic\/?asid=1791872767573170&height=50&width=50&ext=1529946538&hash=AeT-c8Gg8kljhvAC","width":50}},"name":"Nick Name","id":"1987872767573170","email":"last.first@gmail.com","permissions":{"data":[{"permission":"user_birthday","status":"granted"},{"permission":"user_hometown","status":"granted"},{"permission":"user_location","status":"granted"},{"permission":"user_posts","status":"granted"},{"permission":"user_gender","status":"granted"},{"permission":"user_link","status":"granted"},{"permission":"user_age_range","status":"granted"},{"permission":"email","status":"granted"},{"permission":"public_profile","status":"granted"}]},"age_range":{"min":21},"link":"https:\/\/www.facebook.com\/app_scoped_user_id\/YXNpZADpBWEZAmSmJjc01BLXJNTGt4UD09RlZAicXpYVG5HbTV3YUl2T29uVTNNMWpGWndhVEVieTI4S18zcDVuUjczUHRmUHgwYm8xUkwtU1lHWENuRW9vUnE2clFtalVLaWs4SldxT05iWUxZAUlZA2bjRHemhE\/","gender":"male","birthday":"01\/01\/1990","location":{"id":"116324086073002","name":"Shanghai, China"}}  
            try{  
                var facebookUser = JSON.parse(data);  
                facebookUser.unionid = facebookUser.id;  
                mui.toast(Common.messages.LOGIN_SUCCESS,{ duration:'short', type:'div' });  
                App.checkUserIsExist(facebookUser, "facebook");  
            }catch(err)  
            {  
                Common.hideLoading();  
                mui.toast(Common.messages.LOGIN_FAIL,{ duration:'long', type:'div' });  
                Common.log("====login.js, facebookAuthSuccessCallBack(), decode facebook user JSON string fail!");  
            }  
        }else  
        {  
            Common.hideLoading();  
            App.facebookAuthErrorCallBack();  
        }  
    },  

    // 这个回调函数会在 cn.shaketowin.app.SDK_WebApp.java 原生类中调用, 参考类中的excuteJSCode()方法  
    facebookAuthCancelCallBack: function()  
    {  
        App.timeout = false;  
        Common.hideLoading();  

        mui.toast(Common.messages.LOGIN_CANCEL,{ duration:'long', type:'div' });  
    },  

    // 这个回调函数会在 cn.shaketowin.app.SDK_WebApp.java 原生类中调用, 参考类中的excuteJSCode()方法  
    facebookAuthErrorCallBack: function(error)  
    {  
        App.timeout = false;  
        Common.hideLoading();  

        mui.toast(Common.messages.LOGIN_FAIL,{ duration:'long', type:'div' });  
    },

2. js层代码插件facebookplug.js

document.addEventListener( "plusready",  function()  
{  
    var facebookplug =  
    {  
        logIn : function (successCallback, errorCallback )  
        {  
            var success = typeof successCallback !== 'function' ? null : function(args)  
            {  
                successCallback(args);  
            },  
            fail = typeof errorCallback !== 'function' ? null : function(code)  
            {  
                errorCallback(code);  
            };  
            callbackID = window.plus.bridge.callbackId(success, fail);  

            return window.plus.bridge.exec('facebookloginplugin', "LogIn",  [callbackID]);  
        },  
        logOut : function ()  
        {  
            window.plus.bridge.exec('facebookloginplugin', "LogOut", []);  
        },  
    };  

    window.plus.facebookplug = facebookplug;  
}, true );

3. 原生层FacebookLoginPlugin.h

//  
//  FacebookLoginPlugin.h  
//  
//  

#include "PGPlugin.h"  
#include "PGMethod.h"  
#import <Foundation/Foundation.h>  

@interface FacebookLoginPlugin : PGPlugin  

- (void)LogIn:(PGMethod*)command;  
- (void)LogOut:(PGMethod*)command;  
@end

4. 原生层FacebookLoginPlugin.m

//  
//  FacebookLoginPlugin.m  
//  

#import "FacebookLoginPlugin.h"  
#import "PDRCoreAppFrame.h"  
#import "H5WEEngineExport.h"  
#import "PDRToolSystemEx.h"  
// 扩展插件中需要引入需要的系统库  
#import <LocalAuthentication/LocalAuthentication.h>  
#import <FBSDKCoreKit/FBSDKCoreKit.h>  
#import <FBSDKLoginKit/FBSDKLoginKit.h>  

@implementation FacebookLoginPlugin  

#pragma mark 这个方法在使用WebApp方式集成时触发,WebView集成方式不触发  

/*  
 * WebApp启动时触发  
 * 需要在PandoraApi.bundle/feature.plist/注册插件里添加autostart值为true,global项的值设置为true  
 */  
- (void) onAppStarted:(NSDictionary*)options{  

    NSLog(@"5+ WebApp启动时触发");  
    // 可以在这个方法里向Core注册扩展插件的JS  

}  

// 监听基座事件事件  
// 应用退出时触发  
- (void) onAppTerminate{  
    //  
    NSLog(@"APPDelegate applicationWillTerminate 事件触发时触发");  
}  

// 应用进入后台时触发  
- (void) onAppEnterBackground{  
    //  
    NSLog(@"APPDelegate applicationDidEnterBackground 事件触发时触发");  
}  

// 应用进入前天时触发  
- (void) onAppEnterForeground{  
    //  
    NSLog(@"APPDelegate applicationWillEnterForeground 事件触发时触发");  
}  

#pragma mark 以下为插件方法,由JS触发, WebView集成和WebApp集成都可以触发  

// 登录  
- (void)LogIn:(PGMethod*)commands  
{  
    if ( commands ) {  
        // CallBackid 异步方法的回调id,H5+ 会根据回调ID通知JS层运行结果成功或者失败  
        NSString* cbId = [commands.arguments objectAtIndex:0];  

        if ([FBSDKAccessToken currentAccessToken]) {  
            //NSLog(@"==========LogIn(), already have token");  

            [self GetUserInfo:cbId];  

        }else{  

            /*  
             * Facebook会对i应用访问的字段进行控制(https://developers.facebook.com/apps/193845774573048/review-status/)  
             * 应用审核说明:https://developers.facebook.com/docs/apps/review/#app-review  
             * 默认不需要审核的有:@"public_profile", @"email"  
             * 如下权限系统提交应用审核才可以访问: @"public_profile", @"email", @"user_age_range", @"user_birthday", @"user_gender", @"user_hometown", @"user_link", @"user_location"  
             * 如果需要访问相对应的字段,将字段放入的代码 [login logInWithReadPermissions:@[@"public_profile", @"email"] 参数即可  
             */  

            FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];  
            login.loginBehavior = FBSDKLoginBehaviorWeb;  
            [login logInWithReadPermissions:@[@"public_profile", @"email"]  
                                    handler:^(FBSDKLoginManagerLoginResult *result, NSError *error)  
            {  
                if (error) {  
                    [self CallWebAppJSFun: cbId status: @"ERROR" message: error.localizedDescription];  
                } else if (result.isCancelled) {  
                    [self CallWebAppJSFun: cbId status: @"ERROR" message: @"cancel"];  
                } else {  
                    // If you ask for multiple permissions at once, you  
                    // should check if specific permissions missing  
                    //NSLog(@"========Permission  2: %@",result.grantedPermissions);  

                    [self GetUserInfo:cbId];  
                }  

            }];  
        }  
    }  
}  

// 退出登录  
- (void)LogOut:(PGMethod *)command  
{  
    FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init];  
    [loginManager logOut];  
    // or  
    //[FBSDKAccessToken setCurrentAccessToken:nil];  
}  

// 获取用户信息  
- (void)GetUserInfo:(NSString *) cbId  
{  
    /*  
     * Facebook会对i应用访问的字段进行控制(https://developers.facebook.com/apps/193845774573048/review-status/)  
     * 应用审核说明:https://developers.facebook.com/docs/apps/review/#app-review  
     * 对应默认不需要审核的字段@"public_profile", @"email"的是@"id,name,first_name,last_name,email,picture.type(large)“  
     * 对应提交应用审核才可以访问字段有 @"cover,picture.type(large),id,name,first_name,last_name,gender,birthday,email,location,hometown,about,photos,age_range,link"  
     * 如果需要访问相对应的字段,将字段放入的代码 parameters:[NSDictionary dictionaryWithObject:@"cover,..." forKey:@"fields"] 参数即可  
     */  

    [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:[NSDictionary dictionaryWithObject:@"id,name,first_name,last_name,email,picture" forKey:@"fields"]]  
     startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {  

         if (!error) {  

             //NSLog(@"==========GetUserInfo(), username: %@", [result valueForKey:@"name"]);  
//             NSString *userID = [[FBSDKAccessToken currentAccessToken] userID];  
//             NSString *userName = [result valueForKey:@"name"];  
//             NSString *email =  [result valueForKey:@"email"];  
//             NSString *userImageURL = [NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=large", [[FBSDKAccessToken currentAccessToken] userID]];  

             NSData *jsonData = [NSJSONSerialization dataWithJSONObject:result options:NSJSONWritingPrettyPrinted error:nil];  

             NSString *userInfo =  [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];  

             //NSLog(@"==========GetUserInfo(), userInfo: %@", userInfo);  

             [self CallWebAppJSFun: cbId status: @"OK" message: userInfo];  
         }  
         else{  
             [self CallWebAppJSFun: cbId status: @"ERROR" message: error.localizedDescription];  
         }  
     }];  

}  

// 回调通知JS层  
- (void)CallWebAppJSFun:(NSString *) cbId  
                status:(NSString *) st  
                message:(NSString *) msg  
{  
    // 运行Native代码结果和预期相同,调用回调通知JS层运行成功并返回结果  
    // PDRCommandStatusOK 表示触发JS层成功回调方法  
    // PDRCommandStatusError 表示触发JS层错误回调方法  

    // 如果方法需要持续触发页面回调,可以通过修改 PDRPluginResult 对象的keepCallback 属性值来表示当前是否可重复回调, true 表示可以重复回调   false 表示不可重复回调  默认值为false  

    PDRPluginResult * pResult = nil;  

    if([st isEqualToString: @"OK"])  
    {  
        pResult = [PDRPluginResult resultWithStatus:PDRCommandStatusOK messageAsString: msg];  
    }else  
    {  
        // 如果Native代码运行结果和预期不同,需要通过回调通知JS层出现错误,并返回错误提示  
        pResult = [PDRPluginResult resultWithStatus:PDRCommandStatusError messageAsString:msg];  

    }  

    // 通知JS层Native层运行结果  
    [self toCallback:cbId withReslut:[pResult toJSONString]];  

}  

// 调用指纹解锁  
- (void)AuthenticateUser:(PGMethod*)command  
{  
    if (nil == command) {  
        return;  
    }  
    BOOL isSupport = false;  
    NSString* pcbid = [command.arguments objectAtIndex:0];  
    NSError* error = nil;  
    NSString* LocalReason = @"HBuilder指纹验证";  

    // Touch ID 是IOS 8 以后支持的功能  
    if ([PTDeviceOSInfo systemVersion] >= PTSystemVersion8Series) {  
        LAContext* context = [[LAContext alloc] init];  
        if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {  
            isSupport = true;  
            [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:LocalReason reply:^(BOOL success, NSError * _Nullable error) {  
                PDRPluginResult * pResult = nil;  

                if (success) {  

                    pResult = [PDRPluginResult resultWithStatus: PDRCommandStatusOK messageAsDictionary:@{@"state":@(0), @"message":@"成功"}];  
                }  
                else{  
                    NSDictionary* pStringError = nil;  
                    switch (error.code) {  
                        case LAErrorSystemCancel:  
                        {  
                            pStringError = @{@"state":@(-1), @"message":@"系统取消授权(例如其他APP切入)"};  
                            break;  
                        }  
                        case LAErrorUserCancel:  
                        {  
                            pStringError = @{@"state":@(-2), @"message":@"用户取消Touch ID授权"};  
                            break;  
                        }  
                        case LAErrorUserFallback:  
                        {  
                            pStringError  = @{@"state":@(-3), @"message":@"用户选择输入密码"};  
                            break;  
                        }  
                        case LAErrorTouchIDNotAvailable:{  
                            pStringError  = @{@"state":@(-4), @"message":@"设备Touch ID不可用"};  
                            break;  
                        }  
                        case LAErrorTouchIDLockout:{  
                            pStringError  = @{@"state":@(-5), @"message":@"Touch ID被锁"};  
                            break;  
                        }  
                        case LAErrorAppCancel:{  
                            pStringError  = @{@"state":@(-6), @"message":@"软件被挂起取消授权"};  
                            break;  
                        }  
                        default:  
                        {  
                            pStringError  = @{@"state":@(-7), @"message":@"其他错误"};  
                            break;  
                        }  
                    }  
                    pResult = [PDRPluginResult resultWithStatus:PDRCommandStatusError messageAsDictionary:pStringError];  

                }  

                [self toCallback:pcbid withReslut:[pResult toJSONString]];  
            }];  
        }  
        else{  
            NSDictionary* pStringError = nil;  
            switch (error.code) {  
                case LAErrorTouchIDNotEnrolled:  
                {  
                    pStringError  = @{@"state":@(-11), @"message":@"设备Touch ID不可用"};  
                    break;  
                }  
                case LAErrorPasscodeNotSet:  
                {  
                    pStringError  = @{@"state":@(-12), @"message":@"用户未录入Touch ID"};  
                    break;  
                }  

                default:  
                    break;  
            }  
        }  
    }  

    if (!isSupport) {  
        PDRPluginResult* pResult = [PDRPluginResult resultWithStatus:PDRCommandStatusError messageAsString:@"Device Not Support"];  
        [self toCallback:pcbid withReslut:[pResult toJSONString]];  
    }  
}  

@end

5. 原生层AppDelegate.m(在原有函数上新增一行FB的代码,这里仅贴出需要修改的函数的代码)

//  
//  AppDelegate.m  
//  

#import "AppDelegate.h"  
#import "PDRCore.h"  
#import "PDRCommonString.h"  
#import "ViewController.h"  
#import "PDRCoreApp.h"  
#import "DCADManager.h"  
#import "PDRCoreAppManager.h"  
#import <FBSDKCoreKit/FBSDKCoreKit.h>  

// 示例默认带开屏广告,如果不需要广告,可注释下面一行  
#define dcSplashAd  

@interface AppDelegate () <DCADManagerDelgate, PDRCoreDelegate>  
@property (strong, nonatomic) ViewController *h5ViewContoller;  
@end  

@implementation AppDelegate  

@synthesize window = _window;  
#pragma mark -  
#pragma mark app lifecycle  
/*  
 * @Summary:程序启动时收到push消息  
 */  
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  
{  

    BOOL ret = [PDRCore initEngineWihtOptions:launchOptions  
                                  withRunMode:PDRCoreRunModeNormal withDelegate:self];  

    DCADManager *adManager = [DCADManager adManager];  
    UIViewController* adViewController = [adManager getADViewController];  
    adManager.delegate = self;  
    UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];  
    self.window = window;  

    ViewController *viewController = [[ViewController alloc] init];  
    self.h5ViewContoller = viewController;  

    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];  
    self.rootViewController = navigationController;  
    navigationController.navigationBarHidden = YES;  
    if ( adViewController ) {  
        [navigationController pushViewController:adViewController animated:NO];  
    } else {  
        [self startMainApp];  
        self.h5ViewContoller.showLoadingView = YES;  
    }  
    self.window.rootViewController = navigationController;  
    [self.window makeKeyAndVisible];  

    [[FBSDKApplicationDelegate sharedInstance] application:application  
                             didFinishLaunchingWithOptions:launchOptions];  

    return ret;  
}  

#pragma mark -  
#pragma mark URL  

- (BOOL)application:(UIApplication *)application  
            openURL:(NSURL *)url  
  sourceApplication:(NSString *)sourceApplication  
         annotation:(id)annotation  
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options{  
    //NSLog(@"==========here(), url: %@", url);  
    [self application:application handleOpenURL:url];  

    [[FBSDKApplicationDelegate sharedInstance] application:application  
                                                                  openURL:url  
                                            sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]  
                                           annotation:options[UIApplicationOpenURLOptionsAnnotationKey]  
                    ];  

    return YES;  
}

6. Supporting Files/Info.plist文件

收起阅读 »

uni-app 中使用微信小程序第三方 SDK 及资源汇总

高德地图 SDK 小程序

本文以高德微信小程序 SDK 为例,简述如何在 uni-app 中利用微信小程序第三方 SDK,实现 App 端和微信小程序的代码通用。

获取SDK

首先在高德开放平台,注册账号并且申请相关的 key 等信息;
然后下载它的微信小程序版 SDK:微信小程序 SDK
然后填写app包名,申请原生sdk的appkey信息,但不需要下载原生sdk。注意:App侧在Android中使用定位,或者Android、iOS使用地图,仍然需要同时向高德申请原生sdk的key信息,填写在manifest的app sdk配置中。

新建一个 uni-app 项目,新建一个 common 目录,然后将前面下载得到的 amap-wx.js 的文件复制进去。

Tip:这个 common 目录只是举例,并非强制约定。但是,不要放在 static 目录下。

引入SDK

新建的 uni-app 默认会有一个 index 页,在 index.vue 中,引入高德小程序 SDK。

import amap from '../../common/amap-wx.js';  
export default {  
}

在 onLoad 中初始化一个高德小程序 SDK 的实例对象。

import amap from '../../common/amap-wx.js';  
export default {  
    data() {  
        return {  
            amapPlugin: null,  
            key: '这里填写高德开放平台上申请的key'  
        }  
    },  
    onLoad() {  
        this.amapPlugin = new amap.AMapWX({  
            key: this.key  
        });  
    }  
}

使用API

利用高德小程序 SDK,获取当前位置地址信息,以及当前位置的天气情况。

import amap from '../../common/amap-wx.js';  
export default {  
    data() {  
        return {  
            amapPlugin: null,  
            key: '高德key',  
            addressName: '',  
            weather: {  
                hasData: false,  
                data: []  
            }  
        }  
    },  
    onLoad() {  
        this.amapPlugin = new amap.AMapWX({  
            key: this.key  
        });  
    },  
    methods: {  
        getRegeo() {  
            uni.showLoading({  
                title: '获取信息中'  
            });  
            this.amapPlugin.getRegeo({  
                success: (data) => {  
                    console.log(data)  
                    this.addressName = data[0].name;  
                    uni.hideLoading();  
                }  
            });  
        }  
    }  
}

其它 SDK

高德小程序 SDK 类似辅助工具库,使用时在需要的页面中引入即可。

还有一种 SDK 比如阿拉丁、诸葛IO等统计类的 SDK 需要全局引入。小程序是在 app.js 中 引入。在 uni-app 中,则是在 main.js 中引入。

示例

附件中有完整的示例,下载后解压拖进 HBuilderX,填写申请的高德 key 后即可运行体验。

如果大家在使用微信小程序的第三方 SDK 时遇到问题,请在社区单独发帖描述清楚问题,并一定附上项目源码,方便我们测试,感谢配合。

更多 SDK 资源

通用 SDK

App 端和微信小程序均可使用小程序版 SDK

微信小程序

仅适用于微信小程序

这些 SDK 因为当前 uni-app 还未统一app和小程序的直播api而暂时只能在小程序中使用。

补充完善

以上列出来的只是一部分资源信息,还有一些其它的 SDK 可能没有涉及到,欢迎大家继续补充相关信息。

再次强调,因为uni-app的app端其实是一个强化版的小程序引擎,所以支持小程序sdk在app端使用。但这些sdk在h5端、支付宝百度等小程序平台无法使用。

继续阅读 »

本文以高德微信小程序 SDK 为例,简述如何在 uni-app 中利用微信小程序第三方 SDK,实现 App 端和微信小程序的代码通用。

获取SDK

首先在高德开放平台,注册账号并且申请相关的 key 等信息;
然后下载它的微信小程序版 SDK:微信小程序 SDK
然后填写app包名,申请原生sdk的appkey信息,但不需要下载原生sdk。注意:App侧在Android中使用定位,或者Android、iOS使用地图,仍然需要同时向高德申请原生sdk的key信息,填写在manifest的app sdk配置中。

新建一个 uni-app 项目,新建一个 common 目录,然后将前面下载得到的 amap-wx.js 的文件复制进去。

Tip:这个 common 目录只是举例,并非强制约定。但是,不要放在 static 目录下。

引入SDK

新建的 uni-app 默认会有一个 index 页,在 index.vue 中,引入高德小程序 SDK。

import amap from '../../common/amap-wx.js';  
export default {  
}

在 onLoad 中初始化一个高德小程序 SDK 的实例对象。

import amap from '../../common/amap-wx.js';  
export default {  
    data() {  
        return {  
            amapPlugin: null,  
            key: '这里填写高德开放平台上申请的key'  
        }  
    },  
    onLoad() {  
        this.amapPlugin = new amap.AMapWX({  
            key: this.key  
        });  
    }  
}

使用API

利用高德小程序 SDK,获取当前位置地址信息,以及当前位置的天气情况。

import amap from '../../common/amap-wx.js';  
export default {  
    data() {  
        return {  
            amapPlugin: null,  
            key: '高德key',  
            addressName: '',  
            weather: {  
                hasData: false,  
                data: []  
            }  
        }  
    },  
    onLoad() {  
        this.amapPlugin = new amap.AMapWX({  
            key: this.key  
        });  
    },  
    methods: {  
        getRegeo() {  
            uni.showLoading({  
                title: '获取信息中'  
            });  
            this.amapPlugin.getRegeo({  
                success: (data) => {  
                    console.log(data)  
                    this.addressName = data[0].name;  
                    uni.hideLoading();  
                }  
            });  
        }  
    }  
}

其它 SDK

高德小程序 SDK 类似辅助工具库,使用时在需要的页面中引入即可。

还有一种 SDK 比如阿拉丁、诸葛IO等统计类的 SDK 需要全局引入。小程序是在 app.js 中 引入。在 uni-app 中,则是在 main.js 中引入。

示例

附件中有完整的示例,下载后解压拖进 HBuilderX,填写申请的高德 key 后即可运行体验。

如果大家在使用微信小程序的第三方 SDK 时遇到问题,请在社区单独发帖描述清楚问题,并一定附上项目源码,方便我们测试,感谢配合。

更多 SDK 资源

通用 SDK

App 端和微信小程序均可使用小程序版 SDK

微信小程序

仅适用于微信小程序

这些 SDK 因为当前 uni-app 还未统一app和小程序的直播api而暂时只能在小程序中使用。

补充完善

以上列出来的只是一部分资源信息,还有一些其它的 SDK 可能没有涉及到,欢迎大家继续补充相关信息。

再次强调,因为uni-app的app端其实是一个强化版的小程序引擎,所以支持小程序sdk在app端使用。但这些sdk在h5端、支付宝百度等小程序平台无法使用。

收起阅读 »

uniapp个人作品展示和经验分享

总的来说,体验不错,值得入坑,希望 web view这个在多些功能,比如获取当前url,h5+监听太麻烦了,而且卡顿

继续阅读 »

总的来说,体验不错,值得入坑,希望 web view这个在多些功能,比如获取当前url,h5+监听太麻烦了,而且卡顿

收起阅读 »

如何用PHP读取Excel文件数据及内容信息

  在文章开始介绍前,大家需要了解一下PHPExcel是什么?PHPExcel 就是一个用来操作Office Excel 文档的PHP类库。接下来讲解如何利用PHPExcel类库将Excel表格内的信息内容读取出来,完整代码如下:

<?php

$fileName = "1.xls";

if (!file_exists($fileName)) {

return "文件不存在!";

}

// 引入PHPExcel

require_once "./PHPExcel/IOFactory.php";

// 载入当前文件

$phpExcel = PHPExcel_IOFactory::load($fileName);

// 设置为默认表

$phpExcel->setActiveSheetIndex(0);

// 获取表格数量

$sheetCount = $phpExcel->getSheetCount();

// 获取行数

$row = $phpExcel->getActiveSheet()->getHighestRow();

// 获取列数

$column = $phpExcel->getActiveSheet()->getHighestColumn();

echo "表格数目为:$sheetCount" . "表格的行数:$row" . "列数:$column";

$data = [];

// 行数循环

for ($i = 1; $i <= $row; $i++) {

// 列数循环

for ($c = 'A'; $c <= $column; $c++) {

$data[] = $phpExcel->getActiveSheet()->getCell($c . $i)->getValue();

}

echo "

";

print_r($data);

}

  在上述代码中,先if判断Excel表格是否存在,再用require_once引入PHPExcel类库中IOFactory.php这个类,然后使用PHPExcel类库的一个方法载入Excel文件,这里将载入的Excel文件赋值于$phpExcel变量。

  然后再将$phpExcel变量通过分别指定相应的方法来获取Excel表格的数目、行数、列数以及通过setActiveSheetIndex(0)方法设置这个表格为默认(0表示默认)。

  此时输出表格的数目、行数、列数的结果,通过浏览器访问的结果就出来了。然后我们用for循环遍历出表格的所有内容,再定义一个数组$data来接收这些内容。

  本文由专业的郑州app开发公司燚轩科技整理发布,原创不易,如需转载请注明出处。

继续阅读 »

  在文章开始介绍前,大家需要了解一下PHPExcel是什么?PHPExcel 就是一个用来操作Office Excel 文档的PHP类库。接下来讲解如何利用PHPExcel类库将Excel表格内的信息内容读取出来,完整代码如下:

<?php

$fileName = "1.xls";

if (!file_exists($fileName)) {

return "文件不存在!";

}

// 引入PHPExcel

require_once "./PHPExcel/IOFactory.php";

// 载入当前文件

$phpExcel = PHPExcel_IOFactory::load($fileName);

// 设置为默认表

$phpExcel->setActiveSheetIndex(0);

// 获取表格数量

$sheetCount = $phpExcel->getSheetCount();

// 获取行数

$row = $phpExcel->getActiveSheet()->getHighestRow();

// 获取列数

$column = $phpExcel->getActiveSheet()->getHighestColumn();

echo "表格数目为:$sheetCount" . "表格的行数:$row" . "列数:$column";

$data = [];

// 行数循环

for ($i = 1; $i <= $row; $i++) {

// 列数循环

for ($c = 'A'; $c <= $column; $c++) {

$data[] = $phpExcel->getActiveSheet()->getCell($c . $i)->getValue();

}

echo "

";

print_r($data);

}

  在上述代码中,先if判断Excel表格是否存在,再用require_once引入PHPExcel类库中IOFactory.php这个类,然后使用PHPExcel类库的一个方法载入Excel文件,这里将载入的Excel文件赋值于$phpExcel变量。

  然后再将$phpExcel变量通过分别指定相应的方法来获取Excel表格的数目、行数、列数以及通过setActiveSheetIndex(0)方法设置这个表格为默认(0表示默认)。

  此时输出表格的数目、行数、列数的结果,通过浏览器访问的结果就出来了。然后我们用for循环遍历出表格的所有内容,再定义一个数组$data来接收这些内容。

  本文由专业的郑州app开发公司燚轩科技整理发布,原创不易,如需转载请注明出处。

收起阅读 »

uniapp: request竟然没有timeout, 这是基本功能吧 ,既然没有 只好自己写个了, 希望官方提供个

uniapp

更新: 官网找到timeout了 藏的太深 没有和request介绍联系起来 放个链接地址:networktimeout
http://uniapp.dcloud.io/collocation/manifest?id=networktimeout

封装

let globalPostRequest = function (url,data,callback,isWait=true,isPost=true) {  
    if (isWait) {  
        uni.showLoading({  
            title: '正在加载'  
        });  
    }  
    console.log('正在请求:xxxxxxxxx' + url);  
    console.log('参数:' + JSON.stringify(data));  
    let requestName = 'requestTask'+ JSON.stringify(url);  
    let timeout = null;  
    requestName = uni.request({  
        url: 'xxxxxxxxx' + url,  
        data: data,  
        method: isPost == true?'POST':'GET',  
        dataType: 'json',  
        header: {  
            'Accept-Language':'zh-CN,en-US;q=0.8'  
        },  
        success: (data,statusCode) => {  
            console.log('请求成功:'+JSON.stringify(data.data));  
            callback(data.data);  
        },  
        fail:() => {  
            console.log('请求失败fail');  
            const errordata = {"status":false,"code":404,"data":null,"message":"网络繁忙"}  
            callback(errordata);  
        },  
        complete:() => {  
            uni.hideLoading();  
            requestName = null;  
            if (timeout != null) {  
                clearTimeout(timeout);  
                timeout = null;  
            }  
        }  
    });  
    //timeout  
    timeout = setTimeout(() => {  
        if (requestName != null) {  
            requestName.abort();  
            console.log('请求失败abort');  
        }  
    },20000)  
}

调用

users_getMyBook:(userid) => {  
                qx.globalPostRequest('xxxxxxxxx',{  
                    userid:userid  
                },  
                function(data){  
                    if (data&&data.status) {  
                        //成功  
                    } else{  
                        uni.showToast({  
                            title: data.message,  
                            duration: 2000  
                        });  
                    }  
                })
继续阅读 »

更新: 官网找到timeout了 藏的太深 没有和request介绍联系起来 放个链接地址:networktimeout
http://uniapp.dcloud.io/collocation/manifest?id=networktimeout

封装

let globalPostRequest = function (url,data,callback,isWait=true,isPost=true) {  
    if (isWait) {  
        uni.showLoading({  
            title: '正在加载'  
        });  
    }  
    console.log('正在请求:xxxxxxxxx' + url);  
    console.log('参数:' + JSON.stringify(data));  
    let requestName = 'requestTask'+ JSON.stringify(url);  
    let timeout = null;  
    requestName = uni.request({  
        url: 'xxxxxxxxx' + url,  
        data: data,  
        method: isPost == true?'POST':'GET',  
        dataType: 'json',  
        header: {  
            'Accept-Language':'zh-CN,en-US;q=0.8'  
        },  
        success: (data,statusCode) => {  
            console.log('请求成功:'+JSON.stringify(data.data));  
            callback(data.data);  
        },  
        fail:() => {  
            console.log('请求失败fail');  
            const errordata = {"status":false,"code":404,"data":null,"message":"网络繁忙"}  
            callback(errordata);  
        },  
        complete:() => {  
            uni.hideLoading();  
            requestName = null;  
            if (timeout != null) {  
                clearTimeout(timeout);  
                timeout = null;  
            }  
        }  
    });  
    //timeout  
    timeout = setTimeout(() => {  
        if (requestName != null) {  
            requestName.abort();  
            console.log('请求失败abort');  
        }  
    },20000)  
}

调用

users_getMyBook:(userid) => {  
                qx.globalPostRequest('xxxxxxxxx',{  
                    userid:userid  
                },  
                function(data){  
                    if (data&&data.status) {  
                        //成功  
                    } else{  
                        uni.showToast({  
                            title: data.message,  
                            duration: 2000  
                        });  
                    }  
                })
收起阅读 »

html5 postMessage解决跨域、跨窗口消息传递

HTML5

  平时做web开发的时候关于消息传递,除了客户端与服务器传值还有几个经常会遇到以下这些问题,那么对此就来分析解答一下如何做吧:

  1.页面和其打开的新窗口的数据传递

  2.多窗口之间消息传递

  3.页面与嵌套的iframe消息传递

  4.上面三个问题的跨域数据传递

  postMessage()

  这些问题都有一些解决办法,但html5引入的message的API可以更方便、有效、安全的解决这些难题。postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。

  postMessage(data,origin)方法接受两个参数

  1.data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。

  2.origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

源代码如下:

<div style="width:200px; float:left; margin-right:200px;border:solid 1px #333;">
<div id="color">Frame Color</div>
</div>
<div>
<iframe id="child" src="http://lsLib.com/lsLib.html"></iframe>
</div>
我们可以在http://test.com/index.html通过postMessage()方法向跨域的iframe页面http://lsLib.com/lsLib.html传递消息
window.onload=function(){
window.frames[0].postMessage('getcolor','http://lslib.com');
}
接收消息
test.com上面的页面向lslib.com发送了消息,那么在lslib.com页面上如何接收消息呢,监听window的message事件就可以
window.addEventListener('message',function(e){
if(e.source!=window.parent) return;
var color=container.style.backgroundColor;
window.parent.postMessage(color,'');
},false);
这样我们就可以接收任何窗口传递来的消息了,为了安全起见,我们利用这时候的MessageEvent对象判断了一下消息源,MessageEvent是一个这样的东东有几个重要属性:
data:顾名思义,是传递来的message
source:发送消息的窗口对象
origin:发送消息窗口的源(协议+主机+端口号)
这样就可以接收跨域的消息了,我们还可以发送消息回去,方法类似
简单的demo,在这个例子中,左边的div会根据右边iframe内div颜色变化而变化
在例子中页面加载的时候主页面向iframe发送’getColor‘ 请求(参数没实际用处)
window.onload=function(){
window.frames[0].postMessage('getcolor','http://lslib.com');
}
iframe接收消息,并把当前颜色发送给主页面呢
window.addEventListener('message',function(e){
if(e.source!=window.parent) return;
var color=container.style.backgroundColor;
window.parent.postMessage(color,'
');
},false);
主页面接收消息,更改自己div颜色
window.addEventListener('message',function(e){
var color=e.data;
document.getElementById('color').style.backgroundColor=color;
},false);
当点击iframe事触发其变色方法,把最新颜色发送给主页面
function changeColor () {
var color=container.style.backgroundColor;
if(color=='rgb(204, 102, 0)'){
color='rgb(204, 204, 0)';
}else{
color='rgb(204,102,0)';
}
container.style.backgroundColor=color;
window.parent.postMessage(color,'*');
}
主页面还是利用刚才监听message事件的程序处理自身变色
window.addEventListener('message',function(e){
var color=e.data;
document.getElementById('color').style.backgroundColor=color;
},false);

  最后很简单的用法却解决了大问题,据说Facebook已经在使用了,而且这也是html5另一个API——web workers传递消息的方法,那么它的浏览器兼容性怎么样呢?所谓浏览器兼容性几乎变成了IE几开始支持的问题了。。。不过好消息是跟localStorage一样,IE8+都支持了,只不过有些浏览器的低版本(比如FireFox4.0)并不支持window.onmessage=function(){}这种写法,所以建议最好使用事件绑定的写法,为了兼容IE,也要判断是否支持addEventListener。

继续阅读 »

  平时做web开发的时候关于消息传递,除了客户端与服务器传值还有几个经常会遇到以下这些问题,那么对此就来分析解答一下如何做吧:

  1.页面和其打开的新窗口的数据传递

  2.多窗口之间消息传递

  3.页面与嵌套的iframe消息传递

  4.上面三个问题的跨域数据传递

  postMessage()

  这些问题都有一些解决办法,但html5引入的message的API可以更方便、有效、安全的解决这些难题。postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。

  postMessage(data,origin)方法接受两个参数

  1.data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。

  2.origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

源代码如下:

<div style="width:200px; float:left; margin-right:200px;border:solid 1px #333;">
<div id="color">Frame Color</div>
</div>
<div>
<iframe id="child" src="http://lsLib.com/lsLib.html"></iframe>
</div>
我们可以在http://test.com/index.html通过postMessage()方法向跨域的iframe页面http://lsLib.com/lsLib.html传递消息
window.onload=function(){
window.frames[0].postMessage('getcolor','http://lslib.com');
}
接收消息
test.com上面的页面向lslib.com发送了消息,那么在lslib.com页面上如何接收消息呢,监听window的message事件就可以
window.addEventListener('message',function(e){
if(e.source!=window.parent) return;
var color=container.style.backgroundColor;
window.parent.postMessage(color,'');
},false);
这样我们就可以接收任何窗口传递来的消息了,为了安全起见,我们利用这时候的MessageEvent对象判断了一下消息源,MessageEvent是一个这样的东东有几个重要属性:
data:顾名思义,是传递来的message
source:发送消息的窗口对象
origin:发送消息窗口的源(协议+主机+端口号)
这样就可以接收跨域的消息了,我们还可以发送消息回去,方法类似
简单的demo,在这个例子中,左边的div会根据右边iframe内div颜色变化而变化
在例子中页面加载的时候主页面向iframe发送’getColor‘ 请求(参数没实际用处)
window.onload=function(){
window.frames[0].postMessage('getcolor','http://lslib.com');
}
iframe接收消息,并把当前颜色发送给主页面呢
window.addEventListener('message',function(e){
if(e.source!=window.parent) return;
var color=container.style.backgroundColor;
window.parent.postMessage(color,'
');
},false);
主页面接收消息,更改自己div颜色
window.addEventListener('message',function(e){
var color=e.data;
document.getElementById('color').style.backgroundColor=color;
},false);
当点击iframe事触发其变色方法,把最新颜色发送给主页面
function changeColor () {
var color=container.style.backgroundColor;
if(color=='rgb(204, 102, 0)'){
color='rgb(204, 204, 0)';
}else{
color='rgb(204,102,0)';
}
container.style.backgroundColor=color;
window.parent.postMessage(color,'*');
}
主页面还是利用刚才监听message事件的程序处理自身变色
window.addEventListener('message',function(e){
var color=e.data;
document.getElementById('color').style.backgroundColor=color;
},false);

  最后很简单的用法却解决了大问题,据说Facebook已经在使用了,而且这也是html5另一个API——web workers传递消息的方法,那么它的浏览器兼容性怎么样呢?所谓浏览器兼容性几乎变成了IE几开始支持的问题了。。。不过好消息是跟localStorage一样,IE8+都支持了,只不过有些浏览器的低版本(比如FireFox4.0)并不支持window.onmessage=function(){}这种写法,所以建议最好使用事件绑定的写法,为了兼容IE,也要判断是否支持addEventListener。

收起阅读 »