美文网首页uni-app交流圈uni-appHBuilder开发
通过Native.js访问iOS原生通讯录界面(Contacts

通过Native.js访问iOS原生通讯录界面(Contacts

作者: w_wh | 来源:发表于2018-11-20 17:30 被阅读0次

    网上有关于Native.js访问Android原生通讯录的案例(可以参考:Android调用系统通讯录控件,native.js实现监听startActivityForResult后返回结果),但是关于iOS的一直没找到,所以决定自己写,写的过程中发现果然有坑。下面将一一道来,最后把Android和iOS的统一封装到了一个js文件中,可以很方便的使用(急需使用的小伙伴可以直接点击Demo链接下载:5+App demo链接 , uni-app demo链接)。
    原创文章,欢迎转载.转载请注明出处: https://www.jianshu.com/p/b78b02d64472

    iOS访问通讯录

    在写NJS(Native.js)的代码之前,首先写了一份iOS访问通讯录的代码,以便根据这份代码翻译成对应的NJS代码。由于AddressBookUI在iOS9之后已经被废弃,当前已经是iOS12了,所以决定用iOS9之后的新框架ContactsUI。实现的代码如下:

    //
    //  ViewController.m
    //  ContactsDemo
    //
    //  Created by xian on 2018/11/5.
    //  Copyright © 2018年 xian. All rights reserved.
    //
    
    #import "ViewController.h"
    #import <ContactsUI/ContactsUI.h>
    
    @interface ViewController ()<CNContactPickerDelegate>
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
    }
    
    #pragma mark - 访问通讯录按钮点击事件
    - (IBAction)vistContacts:(UIButton *)sender {
        //通讯录控制器
        CNContactPickerViewController *contactPickerVC = [CNContactPickerViewController new];
        //设置代理
        //contactPickerVC.delegate = self;
        [contactPickerVC setDelegate:self];
        //从当前控制器present到通讯录控制器
        //[self presentViewController:contactPickerVC animated:YES completion:nil];
        //  先获取当前控制器(这样写为了和NJS代码对应)
        UIViewController *currentVC = [self viewControllerByView:self.view];
        [currentVC presentViewController:contactPickerVC animated:YES completion:nil];
    }
    
    #pragma - 根据view获取当前控制器
    /*写这段代码的目的是为了在MUI项目中使用
     *因为在MUI项目中,要想获取当前页面的控制器,要通过webView来获取
     */
    - (UIViewController *)viewControllerByView:(UIView *)view{
        while (view) {
            UIResponder *responder = [view nextResponder];
            if ([responder isKindOfClass:[UIViewController class]]) {
                return (UIViewController *)responder;
            }
            view = [view superview];
        }
        return nil;
    }
    
    #pragma mark - CNContactPickerDelegate 代理方法
    - (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact{
        //姓名/公司
        NSMutableString *name = [NSMutableString string];
        //姓
        NSString *familyName = contact.familyName;
        //名
        NSString *givenName = contact.givenName;
        //公司
        NSString *organizationName = contact.organizationName;
        [name appendString:familyName];
        [name appendString:givenName];
        if (name.length <= 0) {
            [name appendString:organizationName];
        }
        //手机号码
        NSString *phoneNo = @"";
        NSArray *phoneNumbers = contact.phoneNumbers;
        if (phoneNumbers.count > 0) {
            //这里只取第一个手机号
            CNLabeledValue<CNPhoneNumber *> *phone = phoneNumbers.firstObject;
            CNPhoneNumber *phoneNumber = phone.value;
            phoneNo = phoneNumber.stringValue;
        }
        NSLog(@"姓名/公司:%@,手机号码:%@", name, phoneNo);
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    @end
    

    NJS调用iOS原生通讯录(ContactsUI,仅支持模拟器)

    有了上面iOS访问通讯录的OC代码,可以很轻松的翻译成对应的NJS代码,如下:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
        <title></title>
        <script src="js/mui.min.js"></script>
        <link href="css/mui.min.css" rel="stylesheet"/>
        <script type="text/javascript" charset="utf-8">
            mui.init();
            mui.plusReady(function(){
                mui("button")[0].addEventListener("tap", function(){
                    //访问通讯录
                    visitContacts();
                });
            });
            /**
             * 访问通讯录
             */
            function visitContacts(){
                var contactPickerVC = plus.ios.newObject("CNContactPickerViewController");
                //实现代理方法【- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact;】
                //同时生成遵守CNContactPickerDelegate协议的代理对象delegate
                var delegate = plus.ios.implements("CNContactPickerDelegate", {
                    "contactPicker:didSelectContact:":function(picker, contact){
                        console.log(JSON.stringify(picker));
                        console.log(JSON.stringify(contact));
                        //姓名/公司
                        var name = "";
                        //姓氏
                        var familyName = contact.plusGetAttribute("familyName");
                        //名字
                        var givenName = contact.plusGetAttribute("givenName");
                        //公司
                        var organizationName = contact.plusGetAttribute("organizationName");
                        name = familyName+givenName;
                        if (name.length <= 0) {
                            name = organizationName;
                        }
                        //电话号码
                        var phoneNo = "";
                        var phoneNumbers = contact.plusGetAttribute("phoneNumbers");
                        if (phoneNumbers.plusGetAttribute("count") > 0) {
                            var phone = phoneNumbers.plusGetAttribute("firstObject");
                            var phoneNumber = phone.plusGetAttribute("value");
                            phoneNo = phoneNumber.plusGetAttribute("stringValue");
                        }
                        console.log("姓名/公司:"+name+"  手机号码:"+phoneNo);
                        mui.toast("姓名/公司:"+name+"  手机号码:"+phoneNo);
                    }
                });
                //给通讯录控制器contactPickerVC设置代理
                plus.ios.invoke(contactPickerVC, "setDelegate:", delegate);
                //获取当前UIWebView视图
                var currentWebview = plus.ios.currentWebview();
                //根据当前UIWebView视图获取当前控制器
                var currentVC = viewControllerByView(currentWebview);
                //由当前控制器present到通讯录控制器
                plus.ios.invoke(currentVC, "presentViewController:animated:completion:", contactPickerVC, true, null);
            }
            
            /**
             * 供iOS系统调用
             * 根据view获取到当前控制器
             * @param {Object} view
             */
            function viewControllerByView(view){
                if (plus.os.name != "iOS") {
                    return null;
                }
                //UIViewController类对象
                var UIViewController = plus.ios.invoke("UIViewController", "class");
                while(view){
                    var responder = plus.ios.invoke(view, "nextResponder");
                    if (plus.ios.invoke(responder, "isKindOfClass:", UIViewController)) {
                        return responder;
                    }
                    view = plus.ios.invoke(view, "superview");
                }
                return null;
            }
        </script>
    </head>
    <body>
        <header class="mui-bar mui-bar-nav">
            <h1 class="mui-title">调用通讯录</h1>
        </header>
        <div class="mui-content">
            <button type="button" style="display: block; margin: 100px auto;">访问通讯录</button>
        </div>
    </body>
    </html>
    

    好不容易将OC代码翻译成了NJS代码,真机运行之后成功进入了iOS通讯录界面,选中一个联系人后可以正常返回到MUI页面,但是代理方法没有返回任何信息(确定已经成功实现了代理方法,因为假如没有成功实现代理方法的话,选中一个联系人是会跳到详情界面的,感觉这应该是NJS的Bug)。
    最后,我在模拟器上试了一下,成功拿到了联系人信息!更加确信是NJS的Bug,于是到官网报了如下Bug:
    【报Bug】Native.js调用iOS原生通讯录界面(ContactsUI)时,选中联系人,真机无法获取到回调值。

    NJS调用iOS原生通讯录(AddressBookUI,仅支持真机)

    报了Bug后,想了一下何不用AdressBookUI试试呢,虽然苹果官方已经废弃,但是iOS上还是可以正常使用滴,于是决定碰碰运气。还是老套路,先写OC代码,在上面OC代码基础上加上通过AddressBookUI访问通讯录的代码:

    /**
     * 第二种方法(使用AddressBookUI)
     */
    #pragma - 访问通讯录按钮点击事件
    - (IBAction)visitAddressBook:(UIButton *)sender {
        //通讯录控制器
        ABPeoplePickerNavigationController *peoplePickerNavController = [[ABPeoplePickerNavigationController alloc]init];
        //设置代理
        //peoplePickerNavController.peoplePickerDelegate = self;
        [peoplePickerNavController setPeoplePickerDelegate:self];
        //从当前控制器present到通讯录控制器
        //[self presentViewController:peoplePickerNavController animated:YES completion:nil];
        //  先获取当前控制器(这样写为了和NJS代码对应)
        UIViewController *currentVC = [self viewControllerByView:self.view];
        [currentVC presentViewController:peoplePickerNavController animated:YES completion:nil];
    }
    
    #pragma mark - ABPeoplePickerNavigationControllerDelegate 代理方法
    - (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person{
        //姓名/公司
        NSMutableString *name = [NSMutableString string];
        //姓
        NSString *lastName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty));
        //名
        NSString *firstName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
        //公司
        NSString *organizationName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonOrganizationProperty));
        [name appendString:lastName];
        [name appendString:firstName];
        if (name.length <= 0) {
            [name appendString:organizationName];
        }
        
        //电话号码
        NSString *phoneNo = @"";
        ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
        CFIndex count = ABMultiValueGetCount(phoneNumbers);
        if (count > 0) {
            //这里只取第一个手机号
            phoneNo = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phoneNumbers, 0));
        }
        CFRelease(phoneNumbers);
        NSLog(@"姓名/公司:%@,手机号码:%@", name, phoneNo);
    }
    

    注意:上面的代码需要引入AddressBookUI.h文件,为了规范最好让当前控制器遵守ABPeoplePickerNavigationControllerDelegate代理

    将上述OC代码翻译成对应NJS代码时发现了戏剧性的Bug,代理方法【- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person;】返回的数据类型和iOS原生API完全对不上:

    • 本该返回peoplePicker(ABPeoplePickerNavigationController类型)的地方返回的是CNContact类型的对象;
    • 本该返回person(ABRecordRef类型)的地方什么都没有返回;
    • 非常巧的是CNContact刚好是ContactsUI里存储联系人信息的类型;
    • 不从CNContact实例对象里取联系人信息的时候,模拟器和真机都可以正常执行代理方法,一旦从CNContact实例对象中取值,模拟器便不会再执行这个方法,但是真机可以正常执行而且还可以获取选中的联系人信息。

    最后翻译的NJS代码如下:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
        <title></title>
        <script src="js/mui.min.js"></script>
        <link href="css/mui.min.css" rel="stylesheet"/>
        <script type="text/javascript" charset="utf-8">
            mui.init();
            mui.plusReady(function(){
                //访问通讯录
                mui("button")[0].addEventListener("tap", function(){
                    visitAddressBook();
                });
            });
            /**
             * 访问通讯录
             */
            function visitAddressBook(){
                var peoplePickerNavController = plus.ios.newObject("ABPeoplePickerNavigationController");
                //实现代理方法【- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person;】
                //同时生成遵守ABPeoplePickerNavigationControllerDelegate协议的代理对象peoplePickerDelegate
                var peoplePickerDelegate = plus.ios.implements("ABPeoplePickerNavigationControllerDelegate", {
                    "peoplePickerNavigationController:didSelectPerson:":function(peoplePicker, person){
                        //这里的peoplePicker竟然是CNContact实例对象,person是undefined
                        console.log(JSON.stringify(peoplePicker));
                        console.log(JSON.stringify(person));
                        console.log(typeof person);
                        
                        //所以之前的代码不用改
                        var contact = peoplePicker;
                        //姓名/公司
                        var name = "";
                        //姓氏
                        var familyName = contact.plusGetAttribute("familyName");
                        //名字
                        var givenName = contact.plusGetAttribute("givenName");
                        //公司
                        var organizationName = contact.plusGetAttribute("organizationName");
                        name = familyName+givenName;
                        if (name.length <= 0) {
                            name = organizationName;
                        }
                        //电话号码
                        var phoneNo = "";
                        var phoneNumbers = contact.plusGetAttribute("phoneNumbers");
                        if (phoneNumbers.plusGetAttribute("count") > 0) {
                            var phone = phoneNumbers.plusGetAttribute("firstObject");
                            var phoneNumber = phone.plusGetAttribute("value");
                            phoneNo = phoneNumber.plusGetAttribute("stringValue");
                        }
                        console.log("姓名/公司:"+name+"  手机号码:"+phoneNo);
                        mui.toast("姓名/公司:"+name+"  手机号码:"+phoneNo);
                    }
                });
                //给通讯录控制器peoplePickerNavController设置代理
                plus.ios.invoke(peoplePickerNavController, "setPeoplePickerDelegate:", peoplePickerDelegate);
                //获取当前UIWebView视图
                var currentWebview = plus.ios.currentWebview();
                //根据当前UIWebView视图获取当前控制器
                var currentVC = viewControllerByView(currentWebview);
                //由当前控制器present到通讯录控制器
                plus.ios.invoke(currentVC, "presentViewController:animated:completion:", peoplePickerNavController, true, null);
            }
            
            /**
             * 供iOS系统调用
             * 根据view获取到当前控制器
             * @param {Object} view
             */
            function viewControllerByView(view){
                if (plus.os.name != "iOS") {
                    return null;
                }
                //UIViewController类对象
                var UIViewController = plus.ios.invoke("UIViewController", "class");
                while(view){
                    var responder = plus.ios.invoke(view, "nextResponder");
                    if (plus.ios.invoke(responder, "isKindOfClass:", UIViewController)) {
                        return responder;
                    }
                    view = plus.ios.invoke(view, "superview");
                }
                return null;
            }
        </script>
    </head>
    <body>
        <header class="mui-bar mui-bar-nav">
            <h1 class="mui-title">调用通讯录</h1>
        </header>
        <div class="mui-content">
            <button type="button" style="display: block; margin: 100px auto;">访问通讯录</button>
        </div>
    </body>
    </html>
    

    通过上面的NJS代码,总算可以真机访问通讯录了。但是仍然需要注意这是NSJ的Bug,哪天官方修复了这个Bug,这段代码就会失效。

    封装

    下面将iOS模拟器和真机的代码以及Android的代码封装到一个js文件中。使用的时候只需调用下面这个方法(通过回调函数callBack拿到选中的联系人姓名和手机号码):

    • nativeCommon.contacts.getContact(function callBack(name, phoneNumber));

    封装后的代码如下:

    /**
     * nativeCommon,通过Native.js调用原生API
     */
    var nativeCommon = {
        /**
         * 通讯录模块
         */
        contacts:{
            getContact:function(callBack){
                switch (plus.os.name){
                    case "iOS":
                        if (plus.device.model === "iPhoneSimulator") {
                            //模拟器
                            nativeCommon.contacts.ios.visitContacts(function(name, phoneNumber){
                                callBack(name, phoneNumber);
                            });
                        } else {
                            //真机
                            nativeCommon.contacts.ios.visitAddressBook(function(name, phoneNumber){
                                callBack(name, phoneNumber);
                            });
                        }
                        break;
                    case "Android":
                        // Android通过plus.contacts.getAddressBook可弹出通讯录授权提示框
                        plus.contacts.getAddressBook(plus.contacts.ADDRESSBOOK_PHONE, function (addressbook) {
                            nativeCommon.contacts.android.visitContacts(function(name, phoneNumber){
                                callBack(name, phoneNumber);
                            });
                        }, function (e) {
                            plus.nativeUI.alert("Get address book failed: " + e.message);
                        });
                        break;
                    default:
                        break;
                }
            },
            ios:{//供iOS系统调用
                /**
                 * 访问通讯录,将获取的联系人信息通过callBack返回
                 * 仅限模拟器使用(Native.js 的bug)
                 * @param {Object} callBack回调
                 */
                visitContacts: function(callBack){
                    var contactPickerVC = plus.ios.newObject("CNContactPickerViewController");
                    //实现代理方法【- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact;】
                    //同时生成遵守CNContactPickerDelegate协议的代理对象delegate
                    var delegate = plus.ios.implements("CNContactPickerDelegate", {
                        "contactPicker:didSelectContact:":function(picker, contact){
                            console.log(JSON.stringify(picker));
                            console.log(JSON.stringify(contact));
                            //姓名/公司
                            var name = "";
                            //姓氏
                            var familyName = contact.plusGetAttribute("familyName");
                            //名字
                            var givenName = contact.plusGetAttribute("givenName");
                            //公司
                            var organizationName = contact.plusGetAttribute("organizationName");
                            name = familyName+givenName;
                            if (name.length <= 0) {
                                name = organizationName;
                            }
                            //电话号码
                            var phoneNo = "";
                            var phoneNumbers = contact.plusGetAttribute("phoneNumbers");
                            if (phoneNumbers.plusGetAttribute("count") > 0) {
                                var phone = phoneNumbers.plusGetAttribute("firstObject");
                                var phoneNumber = phone.plusGetAttribute("value");
                                phoneNo = phoneNumber.plusGetAttribute("stringValue");
                            }
                            if(callBack){
                                callBack(name, phoneNo);
                            }
                        }
                    });
                    //给通讯录控制器contactPickerVC设置代理
                    plus.ios.invoke(contactPickerVC, "setDelegate:", delegate);
                    //获取当前UIWebView视图
                    var currentWebview = plus.ios.currentWebview();
                    //根据当前UIWebView视图获取当前控制器
                    var currentVC = nativeCommon.contacts.ios.getViewControllerByView(currentWebview);
                    //由当前控制器present到通讯录控制器
                    plus.ios.invoke(currentVC, "presentViewController:animated:completion:", contactPickerVC, true, null);
                },
                /**
                 * 访问通讯录,将获取的联系人信息通过callBack返回
                 * 仅限真机使用(Native.js 的bug)
                 * @param {Object} callBack
                 */
                visitAddressBook:function(callBack){
                    var peoplePickerNavController = plus.ios.newObject("ABPeoplePickerNavigationController");
                    //实现代理方法【- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person;】
                    //同时生成遵守ABPeoplePickerNavigationControllerDelegate协议的代理对象peoplePickerDelegate
                    var peoplePickerDelegate = plus.ios.implements("ABPeoplePickerNavigationControllerDelegate", {
                        "peoplePickerNavigationController:didSelectPerson:":function(peoplePicker, person){
                            //这里的peoplePicker竟然是CNContact实例对象,person是undefined
                            console.log(JSON.stringify(peoplePicker));
                            console.log(JSON.stringify(person));
                            console.log(typeof person);
                            
                            //所以之前的代码不用改
                            var contact = peoplePicker;
                            //姓名
                            var name = "";
                            //姓氏
                            var familyName = contact.plusGetAttribute("familyName");
                            //名字
                            var givenName = contact.plusGetAttribute("givenName");
                            //公司
                            var organizationName = contact.plusGetAttribute("organizationName");
                            name = familyName+givenName;
                            if (name.length <= 0) {
                                name = organizationName;
                            }
                            //电话号码
                            var phoneNo = "";
                            var phoneNumbers = contact.plusGetAttribute("phoneNumbers");
                            if (phoneNumbers.plusGetAttribute("count") > 0) {
                                var phone = phoneNumbers.plusGetAttribute("firstObject");
                                var phoneNumber = phone.plusGetAttribute("value");
                                phoneNo = phoneNumber.plusGetAttribute("stringValue");
                            }
                            if (callBack) {
                                callBack(name, phoneNo);
                            }
                        }
                    });
                    //给通讯录控制器peoplePickerNavController设置代理
                    plus.ios.invoke(peoplePickerNavController, "setPeoplePickerDelegate:", peoplePickerDelegate);
                    //获取当前UIWebView视图
                    var currentWebview = plus.ios.currentWebview();
                    //根据当前UIWebView视图获取当前控制器
                    var currentVC = nativeCommon.contacts.ios.getViewControllerByView(currentWebview);
                    //由当前控制器present到通讯录控制器
                    plus.ios.invoke(currentVC, "presentViewController:animated:completion:", peoplePickerNavController, true, null);
                },
                /**
                 * 根据view获取到当前控制器
                 * @param {Object} view
                 */
                getViewControllerByView: function(view){
                    //UIViewController类对象
                    var UIViewController = plus.ios.invoke("UIViewController", "class");
                    while(view){
                        var responder = plus.ios.invoke(view, "nextResponder");
                        if (plus.ios.invoke(responder, "isKindOfClass:", UIViewController)) {
                            return responder;
                        }
                        view = plus.ios.invoke(view, "superview");
                    }
                    return null;
                }
            },
            android:{//供android系统调用
                visitContacts:function(callBack){
                    var REQUESTCODE = 1000;
                    main = plus.android.runtimeMainActivity();
                    var Intent = plus.android.importClass('android.content.Intent');
                    var ContactsContract = plus.android.importClass('android.provider.ContactsContract');
                    var intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
                    main.onActivityResult = function(requestCode, resultCode, data) { 
                        if (REQUESTCODE == requestCode) {
                            var phoneNumber = "";
                            var resultString = "";
                            var context = main;
                            plus.android.importClass(data);
                            var contactData = data.getData();
                            var resolver = context.getContentResolver();
                            plus.android.importClass(resolver);
                            var cursor = resolver.query(contactData, null, null, null, null);
                            plus.android.importClass(cursor);
                            cursor.moveToFirst();
                            //姓名
                            var givenName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)) || "";
                            var contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
                            var pCursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null);
                            if (pCursor.moveToNext()) {
                                    phoneNumber =   pCursor.getString( pCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                            }
                            if (callBack) {
                                    callBack(givenName, phoneNumber);
                            }
                            cursor.close();
                            pCursor.close();
                        }
                    };
                    main.startActivityForResult(intent, REQUESTCODE);
                }
            }
        }
    }
    

    注意:这里只返回选中联系人的姓名和手机号码,如果有多个号码只返回第一个;假如项目中需要返回所有的手机号码,可以修改对应的代码将手机号码组装成数组返回。

    使用案例:

    使用前要先引入封装的js文件,本文是将其封装在了native.common.js文件中,使用案例的代码如下:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
        <title></title>
        <script src="js/mui.min.js"></script>
        <link href="css/mui.min.css" rel="stylesheet"/>
        <script type="text/javascript" src="js/native.common.js" ></script>
        <script type="text/javascript" charset="utf-8">
            mui.init();
            mui.plusReady(function(){
                mui("button")[0].addEventListener("tap", function(){
                    //访问通讯录
                    nativeCommon.contacts.getContact(function(name, phoneNumber){
                        console.log("姓名/公司:"+name+",电话号码:"+phoneNumber);
                        mui.toast("姓名/公司:"+name+",电话号码:"+phoneNumber);
                    });
                });
            });
        </script>
    </head>
    <body>
        <header class="mui-bar mui-bar-nav">
            <h1 class="mui-title">调用通讯录</h1>
        </header>
        <div class="mui-content">
            <button type="button" style="display: block; margin: 100px auto;">访问通讯录</button>
        </div>
    </body>
    </html>
    

    适配uni-app:

    最近uni-app项目中用到了这一块功能,发现会闪退。经排查发现是“根据view获取到当前控制器”这段代码中通过viewnextResponder方法获取到的responder为空:

      /**
       * 根据view获取到当前控制器
       * @param {Object} view
       */
      getViewControllerByView: function(view){
          if (plus.os.name != "iOS") {
              return null;
          }
          //UIViewController类对象
          var UIViewController = plus.ios.invoke("UIViewController", "class");
          while(view){
              //uni-app中responder为undefined
              var responder = plus.ios.invoke(view, "nextResponder");
              //uni-app中由于responder为undefined,所以调用"isKindOfClass:"方法闪退
              if (plus.ios.invoke(responder, "isKindOfClass:", UIViewController)) {
                  return responder;
              }
              view = plus.ios.invoke(view, "superview");
          }
          return null;
      }
    

    解决方案:

    既然在uni-app中通过当前view获取控制器的方式行不通,那就直接拿到跟控制器,由跟控制器去present到通讯录控制器:

    • 获取跟控制的iOS代码:

      #pragma mark - 获取跟控制
      - (UIViewController *)getRootViewController {
          UIApplication *sharedApplication = [UIApplication sharedApplication];
          id<UIApplicationDelegate> appDelegate = [sharedApplication delegate];
          UIWindow *appWindow = [appDelegate window];
          return [appWindow rootViewController];
      }
      
    • 转换成NJS代码如下:

      /**
       * 获取跟控制器
       */
      getRootViewController: function(){
          //UIApplication类对象
          var UIApplication = plus.ios.invoke("UIApplication", "class");
          var sharedApplication = plus.ios.invoke(UIApplication, "sharedApplication");
          var appDelegate = plus.ios.invoke(sharedApplication, "delegate");
          var appWindow = plus.ios.invoke(appDelegate, "window");
          return plus.ios.invoke(appWindow, "rootViewController");
      },
      
    • 跳转通讯录控制器的代码如下:

      • visitContacts中跳转通讯录的代码:
      /*注释掉由当前控制器跳转通讯录的代码
      //获取当前UIWebView视图
      var currentWebview = plus.ios.currentWebview();
      //根据当前UIWebView视图获取当前控制器
      var currentVC = nativeCommon.contacts.ios.getViewControllerByView(currentWebview);
      //由当前控制器present到通讯录控制器
      plus.ios.invoke(currentVC, "presentViewController:animated:completion:", contactPickerVC, true, null);
      */
      //获取跟控制器
      var rootVc = nativeCommon.contacts.ios.getRootViewController();
      //由跟控制器present到通讯录控制器
      plus.ios.invoke(rootVc, "presentViewController:animated:completion:", contactPickerVC, true, null);
      
      • visitAddressBook中跳转通讯录的代码:
      /*注释掉由当前控制器跳转通讯录的代码
      //获取当前UIWebView视图
      var currentWebview = plus.ios.currentWebview();
      //根据当前UIWebView视图获取当前控制器
      var currentVC = nativeCommon.contacts.ios.getViewControllerByView(currentWebview);
      //由当前控制器present到通讯录控制器
      plus.ios.invoke(currentVC, "presentViewController:animated:completion:", peoplePickerNavController, true, null);
      */
      //获取跟控制器
      var rootVc = nativeCommon.contacts.ios.getRootViewController();
      //由跟控制器present到通讯录控制器
      plus.ios.invoke(rootVc, "presentViewController:animated:completion:", peoplePickerNavController, true, null);
      

    这种由跟控制器present到通讯录控制器的方式不仅适用于uni-app也适用于的5+App。uni-app的demo链接已经放到文章中,有需要的小伙伴可以点击下载。

    5+App demo链接:https://github.com/w-wh/VisitContactsDemo
    uni-app demo链接:https://github.com/w-wh/VisitContactsDemo_uni-app

    相关文章

      网友评论

        本文标题:通过Native.js访问iOS原生通讯录界面(Contacts

        本文链接:https://www.haomeiwen.com/subject/uovxqqtx.html