在開發(fā)的時候我們經(jīng)常會用到調(diào)用系統(tǒng)的通訊錄, 直接寫代理很容易實(shí)現(xiàn), 網(wǎng)上也很多例子, 例如:
iOS調(diào)用系統(tǒng)通訊錄(適配iOS9败晴、iOS10)(轉(zhuǎn)載)
如果只是一個地方用到還好, 直接在controller里面簽代理.
? ? ? ? 但是遇到很多地方調(diào)用, 每次都簽代理是不是感覺很麻煩, 這時候沒有有想過封裝出來一個工具類, 然后在用的時候直接調(diào)用就可以了, 比如一個block回調(diào) (swift叫閉包).?
本次封裝的主要目的是把每個用到的地方都變成一個block, 直接回調(diào), 方便以后別的地方用.
現(xiàn)在問題來了:
問題1: 通訊錄的vc需要當(dāng)前的vc 給present出來, 需要把當(dāng)前的vc(context)傳到工具類里
問題2: 代理簽到傳進(jìn)來的vc里還是工具類里, 簽到傳進(jìn)來的vc沒法代理了, 就想辦法簽到工具類里
?所以大體思路是 寫一個類 初始化的時候把vc和另一個參數(shù)回調(diào)的block都傳進(jìn)來, ?然后寫一個方法調(diào)用通訊錄, 把代理簽到工具類上,在代理執(zhí)行的時候返回block
那么開始寫代碼, 我的工具類是ContactTool ?直接考慮iOS9.0以上的 導(dǎo)入頭文件?
import ContactsUI
回調(diào)的block
typealias ContactPickBlock = ( _ info: ContactInfo?) -> Void
定一個屬性保留傳進(jìn)來的vc
private var context:UIViewController
private var callBack:ContactPickBlock?
初始化的時候傳遞或者屬性傳也可以
init(context:UIViewController) {? ? ? ? self.context = context ? ?}
///調(diào)用聯(lián)系人
? ? publicfuncjudgeAddressBookPower(didPcik:@escaping ContactPickBlock) {
? ? ? ? //檢查權(quán)限
? ? ? ?checkAddressBookAuthorization{ [weakself] (isAuthorized)in
? ? ? ? ? ? guardlet`self` =selfelse{return}
? ? ? ? ? ? ifisAuthorized ==true{
? ? ? ? ? ? ? ? self.callAddressBook()
? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? self.showMsg(msg:"請到設(shè)置>隱私>通訊錄打開本應(yīng)用的權(quán)限設(shè)置")
? ? ? ? ? ? }
? ? ? ? }
? ? ? ?self.callBack= didPcik
? ?}
檢查權(quán)限
///獲取通訊錄權(quán)限
? ? 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)
? ? ? ? }
? ? }
調(diào)用通訊錄
///調(diào)用通訊錄
? ? privatefunccallAddressBook() {
? ? ? ? let contactPicker = CNContactPickerViewController()
? ? ? ? contactPicker.delegate=self
? ? ? ? contactPicker.displayedPropertyKeys = [CNContactPhoneNumbersKey]
? ? ? ? context.present(contactPicker, animated:true, completion:nil)
? ? }
提示信息的Alert
///顯示信息
? ? privatefuncshowMsg(msg:String) {
? ? ? ? letalert =UIAlertController(title:"沒有權(quán)限", message: msg, preferredStyle: .alert)
? ? ? ? alert.addAction(UIAlertAction(title:"知道了", style: .cancel, handler:nil))
? ? ? ? context.present(alert, animated:true)
? ? }
接下來實(shí)現(xiàn)代理?
///代理
extension ContactTool : CNContactPickerDelegate {
? ? //取消了
? ? funccontactPickerDidCancel(_picker:CNContactPickerViewController) {
? ? ? ? self.filtration(nil)
? ? }
? ? //選擇完聯(lián)系人
? ? funccontactPicker(_picker:CNContactPickerViewController, didSelect contactProperty:CNContactProperty) {
? ? ? ? letphoneNumber = contactProperty.valueas!CNPhoneNumber
? ? ? ? context.dismiss(animated:true) {
? ? ? ? ? ? // 聯(lián)系人
? ? ? ? ? ? letname = contactProperty.contact.familyName+ contactProperty.contact.givenName
? ? ? ? ? ? // 電話
? ? ? ? ? ? letphone = phoneNumber.stringValue
? ? ? ? ? ? self.filtration(ContactInfo(name: name, phone: phone))
? ? ? ? }
? ? }
}
filtration 這個方法里就是回調(diào)的
? ? ? ? self.callBack?(info);
本以為這樣就結(jié)束了, 新的問題出現(xiàn), 當(dāng)你調(diào)用通訊錄的時候, 會發(fā)現(xiàn)代理根本沒有走, 是因?yàn)樵贑NContactPickerViewController出現(xiàn)的時候,?ContactTool 就被釋放了, 這個怎么辦...
那怎么才能讓ContactTool 不被釋放呢, 必須得有個地方引用他, 用傳過來的vc引用他還得寫個屬性, 思來想去想出一個比較奇葩的放法, 我們經(jīng)常會提到避免循環(huán)引用,?循環(huán)引用會造成對象無法釋放, 影響內(nèi)存釋放, 那正是我們想到的結(jié)果, 不讓ContactTool釋放, 我們?yōu)楹尾恢圃煲粋€循環(huán)引用呢, 然后在使用之后打開循環(huán)釋放.
創(chuàng)建一個類讓他持有工具類的對象ContactTool
class Abbot: NSObject {
? let monk:ContactTool ?
?????init(monk:ContactTool) { ? ? ??
? ? ?self.monk = monk ? ??
?????} ??
? }
在judgeAddressBookPower方法中添加如下代碼:
///調(diào)用聯(lián)系人
? ? publicfuncjudgeAddressBookPower(didPcik:@escapingContactPickBlock) {
? ? ? ? //檢查權(quán)限
? ? ? ? checkAddressBookAuthorization{ [weakself] (isAuthorized)in
? ? ? ? ? ? guardlet`self` =selfelse{return}
? ? ? ? ? ? ifisAuthorized ==true{
? ? ? ? ? ? ? ? self.callAddressBook()
? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? self.showMsg(msg:"請到設(shè)置>隱私>通訊錄打開本應(yīng)用的權(quán)限設(shè)置")
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? self.callBack= didPcik
? ? ? ? //制造循環(huán)引用
? ? ? ? letabbot_p =Abbot(monk:self)
? ? ? ? self.abbot= abbot_p
? ? }?
然后在filtration里打開循環(huán)引用
///過濾處理
? ? private func filtration(_info:ContactInfo?) {
? ? ? ? self.callBack?(info);
? ? ? ? //釋放
? ? ? ? DispatchQueue.main.asyncAfter(deadline:DispatchTime.now()+0.1) {
? ? ? ? ? ? self.abbot=nil
? ? ? ? }
? ? }
這些就是所有邏輯, 雖然只是一個簡單的封裝, 但通過制造循環(huán), 然后再釋放, 貌似讓我看到所有的代理都能通過這種方式封裝成block, 也是一個通用的思想. 請各位大神指點(diǎn)
代碼:ContactTest