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

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

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))