美文网首页
封装调用手机通讯录工具(swift)

封装调用手机通讯录工具(swift)

作者: 深夜幽蓝 | 来源:发表于2019-06-10 09:38 被阅读0次

    在开发的时候我们经常会用到调用系统的通讯录, 直接写代理很容易实现, 网上也很多例子, 例如:

    iOS调用系统通讯录(适配iOS9、iOS10)(转载)

    如果只是一个地方用到还好, 直接在controller里面签代理.

            但是遇到很多地方调用, 每次都签代理是不是感觉很麻烦, 这时候没有有想过封装出来一个工具类, 然后在用的时候直接调用就可以了, 比如一个block回调 (swift叫闭包). 

    图片来自网络

    本次封装的主要目的是把每个用到的地方都变成一个block, 直接回调, 方便以后别的地方用.

    现在问题来了:

    问题1: 通讯录的vc需要当前的vc 给present出来, 需要把当前的vc(context)传到工具类里

    问题2: 代理签到传进来的vc里还是工具类里, 签到传进来的vc没法代理了, 就想办法签到工具类里

     所以大体思路是 写一个类 初始化的时候把vc和另一个参数回调的block都传进来,  然后写一个方法调用通讯录, 把代理签到工具类上,在代理执行的时候返回block

    那么开始写代码, 我的工具类是ContactTool  直接考虑iOS9.0以上的 导入头文件 

    import ContactsUI

    回调的block

    typealias ContactPickBlock = ( _ info: ContactInfo?) -> Void

    定一个属性保留传进来的vc

    private var context:UIViewController

    private var callBack:ContactPickBlock?

    初始化的时候传递或者属性传也可以

    init(context:UIViewController) {        self.context = context    }

    ///调用联系人

        publicfuncjudgeAddressBookPower(didPcik:@escaping ContactPickBlock) {

            //检查权限

           checkAddressBookAuthorization{ [weakself] (isAuthorized)in

                guardlet`self` =selfelse{return}

                ifisAuthorized ==true{

                    self.callAddressBook()

                }else{

                    self.showMsg(msg:"请到设置>隐私>通讯录打开本应用的权限设置")

                }

            }

           self.callBack= didPcik

       }

    检查权限

    ///获取通讯录权限

        privatefunccheckAddressBookAuthorization(handler:@escaping((_:Bool) ->Void)) {

            let contactStore =CNContactStore()

            if CNContactStore.authorizationStatus(for: .contacts) == .notDetermined {

                contactStore.requestAccess(for: .contacts, completionHandler: { (granted, error)in

                    iferror !=nil{

                        print(errorasAny)

                        self.showMsg(msg: error.debugDescription)

                    }elseifgranted ==false{

                        handler(false)

                    }else{

                        handler(true)

                    }

                })

            }else if CNContactStore.authorizationStatus(for: .contacts) == .authorized {

                handler(true)

            }else{

                handler(false)

            }

        }

    调用通讯录

    ///调用通讯录

        privatefunccallAddressBook() {

            let contactPicker = CNContactPickerViewController()

            contactPicker.delegate=self

            contactPicker.displayedPropertyKeys = [CNContactPhoneNumbersKey]

            context.present(contactPicker, animated:true, completion:nil)

        }

    提示信息的Alert

    ///显示信息

        privatefuncshowMsg(msg:String) {

            letalert =UIAlertController(title:"没有权限", message: msg, preferredStyle: .alert)

            alert.addAction(UIAlertAction(title:"知道了", style: .cancel, handler:nil))

            context.present(alert, animated:true)

        }

    接下来实现代理 

    ///代理

    extension ContactTool : CNContactPickerDelegate {

        //取消了

        funccontactPickerDidCancel(_picker:CNContactPickerViewController) {

            self.filtration(nil)

        }

        //选择完联系人

        funccontactPicker(_picker:CNContactPickerViewController, didSelect contactProperty:CNContactProperty) {

            letphoneNumber = contactProperty.valueas!CNPhoneNumber

            context.dismiss(animated:true) {

                // 联系人

                letname = contactProperty.contact.familyName+ contactProperty.contact.givenName

                // 电话

                letphone = phoneNumber.stringValue

                self.filtration(ContactInfo(name: name, phone: phone))

            }

        }

    }

    filtration 这个方法里就是回调的

            self.callBack?(info);

    本以为这样就结束了, 新的问题出现, 当你调用通讯录的时候, 会发现代理根本没有走, 是因为在CNContactPickerViewController出现的时候, ContactTool 就被释放了, 这个怎么办...

    图片来自网络

    那怎么才能让ContactTool 不被释放呢, 必须得有个地方引用他, 用传过来的vc引用他还得写个属性, 思来想去想出一个比较奇葩的放法, 我们经常会提到避免循环引用, 循环引用会造成对象无法释放, 影响内存释放, 那正是我们想到的结果, 不让ContactTool释放, 我们为何不制造一个循环引用呢, 然后在使用之后打开循环释放.

    创建一个类让他持有工具类的对象ContactTool

    class Abbot: NSObject {

      let monk:ContactTool  

         init(monk:ContactTool) {       

         self.monk = monk     

         }   

      }

    在judgeAddressBookPower方法中添加如下代码:

    ///调用联系人

        publicfuncjudgeAddressBookPower(didPcik:@escapingContactPickBlock) {

            //检查权限

            checkAddressBookAuthorization{ [weakself] (isAuthorized)in

                guardlet`self` =selfelse{return}

                ifisAuthorized ==true{

                    self.callAddressBook()

                }else{

                    self.showMsg(msg:"请到设置>隐私>通讯录打开本应用的权限设置")

                }

            }

            self.callBack= didPcik

            //制造循环引用

            letabbot_p =Abbot(monk:self)

            self.abbot= abbot_p

        } 

    然后在filtration里打开循环引用

    ///过滤处理

        private func filtration(_info:ContactInfo?) {

            self.callBack?(info);

            //释放

            DispatchQueue.main.asyncAfter(deadline:DispatchTime.now()+0.1) {

                self.abbot=nil

            }

        }

    这些就是所有逻辑, 虽然只是一个简单的封装, 但通过制造循环, 然后再释放, 貌似让我看到所有的代理都能通过这种方式封装成block, 也是一个通用的思想. 请各位大神指点

    代码:ContactTest

    相关文章

      网友评论

          本文标题:封装调用手机通讯录工具(swift)

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