diff --git a/Contacts Picker.xcodeproj/project.pbxproj b/Contacts Picker.xcodeproj/project.pbxproj index 6d93bb0..9f0c0ed 100644 --- a/Contacts Picker.xcodeproj/project.pbxproj +++ b/Contacts Picker.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + A54DCE411F18BD2A00320D5C /* CircularSelected_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A54DCE3D1F18BD2A00320D5C /* CircularSelected_icon@2x.png */; }; + A54DCE421F18BD2A00320D5C /* CircularSelected_icon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = A54DCE3E1F18BD2A00320D5C /* CircularSelected_icon@3x.png */; }; + A54DCE431F18BD2A00320D5C /* CircularUnselected_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A54DCE3F1F18BD2A00320D5C /* CircularUnselected_icon@2x.png */; }; + A54DCE441F18BD2A00320D5C /* CircularUnselected_icon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = A54DCE401F18BD2A00320D5C /* CircularUnselected_icon@3x.png */; }; E210822E1D916C6F002CCAD5 /* EPContactsPicker.podspec in Resources */ = {isa = PBXBuildFile; fileRef = E210822D1D916C6F002CCAD5 /* EPContactsPicker.podspec */; }; F4C1C9421BDF8AB7001AA643 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C1C9411BDF8AB7001AA643 /* AppDelegate.swift */; }; F4C1C9491BDF8AB7001AA643 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4C1C9481BDF8AB7001AA643 /* Assets.xcassets */; }; @@ -23,6 +27,10 @@ /* Begin PBXFileReference section */ 4B7552C01CD28ADA00C638F1 /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = ""; }; + A54DCE3D1F18BD2A00320D5C /* CircularSelected_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "CircularSelected_icon@2x.png"; sourceTree = ""; }; + A54DCE3E1F18BD2A00320D5C /* CircularSelected_icon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "CircularSelected_icon@3x.png"; sourceTree = ""; }; + A54DCE3F1F18BD2A00320D5C /* CircularUnselected_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "CircularUnselected_icon@2x.png"; sourceTree = ""; }; + A54DCE401F18BD2A00320D5C /* CircularUnselected_icon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "CircularUnselected_icon@3x.png"; sourceTree = ""; }; E210822D1D916C6F002CCAD5 /* EPContactsPicker.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = EPContactsPicker.podspec; sourceTree = ""; }; F4C1C93E1BDF8AB7001AA643 /* Contacts Picker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Contacts Picker.app"; sourceTree = BUILT_PRODUCTS_DIR; }; F4C1C9411BDF8AB7001AA643 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -50,6 +58,17 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + A54DCE3C1F18BC6E00320D5C /* Resources */ = { + isa = PBXGroup; + children = ( + A54DCE3D1F18BD2A00320D5C /* CircularSelected_icon@2x.png */, + A54DCE3E1F18BD2A00320D5C /* CircularSelected_icon@3x.png */, + A54DCE3F1F18BD2A00320D5C /* CircularUnselected_icon@2x.png */, + A54DCE401F18BD2A00320D5C /* CircularUnselected_icon@3x.png */, + ); + name = Resources; + sourceTree = ""; + }; F4C1C9351BDF8AB7001AA643 = { isa = PBXGroup; children = ( @@ -91,6 +110,7 @@ F4C1C9791BDF9227001AA643 /* EPContactCell.xib */, F4C1C97A1BDF9227001AA643 /* EPContactsPicker.swift */, F4C1C97B1BDF9227001AA643 /* EPExtensions.swift */, + A54DCE3C1F18BC6E00320D5C /* Resources */, ); name = EPContactsPicker; path = Pods; @@ -127,8 +147,9 @@ TargetAttributes = { F4C1C93D1BDF8AB7001AA643 = { CreatedOnToolsVersion = 7.0; + DevelopmentTeam = CHZJAHSJ6N; LastSwiftMigration = 0800; - ProvisioningStyle = Manual; + ProvisioningStyle = Automatic; }; }; }; @@ -158,7 +179,11 @@ F4C1C94C1BDF8AB7001AA643 /* LaunchScreen.storyboard in Resources */, F4C1C9491BDF8AB7001AA643 /* Assets.xcassets in Resources */, F4C1C9741BDF8BE3001AA643 /* Main.storyboard in Resources */, + A54DCE411F18BD2A00320D5C /* CircularSelected_icon@2x.png in Resources */, + A54DCE431F18BD2A00320D5C /* CircularUnselected_icon@2x.png in Resources */, F4C1C97F1BDF9227001AA643 /* EPContactCell.xib in Resources */, + A54DCE441F18BD2A00320D5C /* CircularUnselected_icon@3x.png in Resources */, + A54DCE421F18BD2A00320D5C /* CircularSelected_icon@3x.png in Resources */, E210822E1D916C6F002CCAD5 /* EPContactsPicker.podspec in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -294,11 +319,14 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "Contacts Picker/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.prabaharan.eppicker.Contacts-Picker"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 3.0; }; name = Debug; @@ -307,11 +335,14 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "Contacts Picker/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.prabaharan.eppicker.Contacts-Picker"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 3.0; }; name = Release; diff --git a/Contacts Picker/Assets.xcassets/AppIcon.appiconset/Contents.json b/Contacts Picker/Assets.xcassets/AppIcon.appiconset/Contents.json index 36d2c80..1d060ed 100644 --- a/Contacts Picker/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Contacts Picker/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,5 +1,15 @@ { "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, { "idiom" : "iphone", "size" : "29x29", @@ -30,6 +40,16 @@ "size" : "60x60", "scale" : "3x" }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, { "idiom" : "ipad", "size" : "29x29", @@ -59,6 +79,11 @@ "idiom" : "ipad", "size" : "76x76", "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" } ], "info" : { diff --git a/Contacts Picker/Assets.xcassets/Contents.json b/Contacts Picker/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Contacts Picker/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Contacts Picker/Base.lproj/Main.storyboard b/Contacts Picker/Base.lproj/Main.storyboard index f699d2d..5b6cf22 100644 --- a/Contacts Picker/Base.lproj/Main.storyboard +++ b/Contacts Picker/Base.lproj/Main.storyboard @@ -1,7 +1,11 @@ - - + + + + + - + + @@ -13,24 +17,49 @@ - + - + + + + + + + + - + - - + + diff --git a/Contacts Picker/ViewController.swift b/Contacts Picker/ViewController.swift index 54a3cc3..18e9aa8 100644 --- a/Contacts Picker/ViewController.swift +++ b/Contacts Picker/ViewController.swift @@ -20,13 +20,25 @@ class ViewController: UIViewController, EPPickerDelegate { // Dispose of any resources that can be recreated. } - @IBAction func onTouchShowMeContactsButton(_ sender: AnyObject) { + @IBAction func onTouchShowMeEmailContactsButton(_ sender: AnyObject) { - let contactPickerScene = EPContactsPicker(delegate: self, multiSelection:true, subtitleCellType: SubtitleCellValue.email) + let contactPickerScene = EPContactsPicker(delegate: self, multiSelection: true, subtitleCellType: .email, selectAllContactsOnLoad: true, sendInvitesButtonEnabled: true) let navigationController = UINavigationController(rootViewController: contactPickerScene) self.present(navigationController, animated: true, completion: nil) } + @IBAction func onTouchShowMePhoneContactsButton(_ sender: Any) { + let contactPickerScene = EPContactsPicker(delegate: self, multiSelection: true, subtitleCellType: .phoneNumber, selectAllContactsOnLoad: false, sendInvitesButtonEnabled: true) + let navigationController = UINavigationController(rootViewController: contactPickerScene) + self.present(navigationController, animated: true, completion: nil) + } + @IBAction func onTouchShowMeAllContactsButton(_ sender: Any) { + let contactPickerScene = EPContactsPicker(delegate: self, multiSelection: false) + let navigationController = UINavigationController(rootViewController: contactPickerScene) + self.present(navigationController, animated: true, completion: nil) + } + + //MARK: EPContactsPicker delegates func epContactPicker(_: EPContactsPicker, didContactFetchFailed error : NSError) diff --git a/EPContactsPicker.podspec b/EPContactsPicker.podspec index fbfe086..22cebb4 100644 --- a/EPContactsPicker.podspec +++ b/EPContactsPicker.podspec @@ -1,8 +1,8 @@ Pod::Spec.new do |s| - s.name = "EPContactsPicker" - s.version = "2.0.2" - s.summary = "A contacts picker component for iOS written in swift using new contacts framwork" - s.description = <<-DESC +s.name = "EPContactsPicker" +s.version = "2.0.4" +s.summary = "A contacts picker component for iOS written in swift using new contacts framwork" +s.description = <<-DESC Features 1. Single selection and multiselection option 2. Making the secondary data to show as requested(Phonenumbers, Emails, Birthday and Organisation) @@ -11,17 +11,18 @@ Features 5. EPContact object to get the properties of the contacts DESC - s.homepage = "https://github.com/ipraba/EPContactsPicker" - s.license = 'MIT' - s.author = { "Prabaharan" => "mailprabaharan.e@gmail.com" } - s.source = { :git => "https://github.com/ipraba/EPContactsPicker.git", :tag => s.version.to_s } - s.platform = :ios, '9.0' - s.requires_arc = true - s.source_files = 'Pods' - s.frameworks = 'Contacts', 'ContactsUI' - s.resources = ["Pods/EPContactCell.xib"] - s.resource_bundles = { - 'EPContactsPicker' => ['Pods/**/*.xib'] - } +s.homepage = "https://github.com/ipraba/EPContactsPicker" +s.license = 'MIT' +s.author = { "Prabaharan" => "mailprabaharan.e@gmail.com" } +s.source = { :git => "https://github.com/ipraba/EPContactsPicker.git", :tag => s.version.to_s } +s.platform = :ios, '9.0' +s.requires_arc = true +s.frameworks = 'Contacts', 'ContactsUI' +s.source_files = "Pods/*.{swift}" +s.resources = ["Pods/*.xib", "Pods/*.png"] +s.resource_bundles = { +'EPContactsPicker' => ['Pods/**/*.{xib,png}'] +} + end diff --git a/Pods/CircularSelected_icon@2x.png b/Pods/CircularSelected_icon@2x.png new file mode 100644 index 0000000..80dfc44 Binary files /dev/null and b/Pods/CircularSelected_icon@2x.png differ diff --git a/Pods/CircularSelected_icon@3x.png b/Pods/CircularSelected_icon@3x.png new file mode 100644 index 0000000..b9d5389 Binary files /dev/null and b/Pods/CircularSelected_icon@3x.png differ diff --git a/Pods/CircularUnselected_icon@2x.png b/Pods/CircularUnselected_icon@2x.png new file mode 100644 index 0000000..dd5b1c4 Binary files /dev/null and b/Pods/CircularUnselected_icon@2x.png differ diff --git a/Pods/CircularUnselected_icon@3x.png b/Pods/CircularUnselected_icon@3x.png new file mode 100644 index 0000000..b37db46 Binary files /dev/null and b/Pods/CircularUnselected_icon@3x.png differ diff --git a/Pods/EPConstants.swift b/Pods/EPConstants.swift index 3cc4300..1d257d7 100644 --- a/Pods/EPConstants.swift +++ b/Pods/EPConstants.swift @@ -38,4 +38,15 @@ struct EPGlobalConstants { static let alphabets = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","#"] //# indicates the names with numbers and blank spaces } +//MARK: "Invite" button constants + struct SendInvitesButton { + static let height:CGFloat = 44 + static let titleColor = UIColor.init(colorLiteralRed: 0, green: 122.0/255.0, blue:1.0, alpha:1.0) + static let titleFont = UIFont(name: "HelveticaNeue-Medium", size: 16.0) + } +//MARK: "SelectContacts" button constants + struct SelectAllContactsButton { + static let selectAllContactString = "Select All" + static let deselectAllContactsString = "Deselect All" + } } diff --git a/Pods/EPContact.swift b/Pods/EPContact.swift index 199f5bb..49d1c71 100644 --- a/Pods/EPContact.swift +++ b/Pods/EPContact.swift @@ -56,7 +56,10 @@ open class EPContact { } for emailAddress in contact.emailAddresses { - guard let emailLabel = emailAddress.label else { continue } + var emailLabel = "email" + if emailAddress.label != nil { + emailLabel = emailAddress.label! + } let email = emailAddress.value as String emails.append((email,emailLabel)) diff --git a/Pods/EPContactCell.swift b/Pods/EPContactCell.swift index 7944249..002c0db 100644 --- a/Pods/EPContactCell.swift +++ b/Pods/EPContactCell.swift @@ -15,8 +15,32 @@ class EPContactCell: UITableViewCell { @IBOutlet weak var contactImageView: UIImageView! @IBOutlet weak var contactInitialLabel: UILabel! @IBOutlet weak var contactContainerView: UIView! + @IBOutlet weak var checkMarkView: UIImageView! + + func getCheckMarkImage(named: String) -> UIImage { + let podBundle = Bundle(for: self.classForCoder) + var image = UIImage() + if let checkMarkImage = UIImage(named: named, in: podBundle, compatibleWith: nil){ + image = checkMarkImage + }else{ + assertionFailure("Could not load checkmark image") + } + return image + } + var contact: EPContact? + var contactSelected: Bool = false{ + didSet{ + var image = UIImage() + if contactSelected { + image = getCheckMarkImage(named: "CircularSelected_icon") + }else{ + image = getCheckMarkImage(named: "CircularUnselected_icon") + } + checkMarkView.image = image + } + } override func awakeFromNib() { diff --git a/Pods/EPContactCell.xib b/Pods/EPContactCell.xib index bb7ca02..1fda316 100644 --- a/Pods/EPContactCell.xib +++ b/Pods/EPContactCell.xib @@ -1,29 +1,34 @@ - - + + + + + - + + + - + - + @@ -31,33 +36,45 @@ + - + + + + + + + + - + - + + + + diff --git a/Pods/EPContactsPicker.swift b/Pods/EPContactsPicker.swift index d4d333b..7436937 100644 --- a/Pods/EPContactsPicker.swift +++ b/Pods/EPContactsPicker.swift @@ -33,7 +33,7 @@ public enum SubtitleCellValue{ case organization } -open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate { +open class EPContactsPicker: UIViewController, UISearchResultsUpdating, UISearchBarDelegate, UITableViewDelegate, UITableViewDataSource { // MARK: - Properties @@ -43,30 +43,43 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS var orderedContacts = [String: [CNContact]]() //Contacts ordered in dicitonary alphabetically var sortedContactKeys = [String]() - var selectedContacts = [EPContact]() + var selectedContacts = [EPContact](){ + didSet { + if sendInvitesButton != nil { + updateSendInvitesButtonTitle() + updateContactSelectionButton() + } + } + } + var filteredContacts = [CNContact]() var subtitleCellValue = SubtitleCellValue.phoneNumber var multiSelectEnabled: Bool = false //Default is single selection contact + var shouldSelectAllContactsOnLoad = false //If we need all contacts selected on controller load select true + var tableView = UITableView() + var sendInvitesButton: UIBarButtonItem? + var totalNumberOfLoadedContacts = 0 + // MARK: - Lifecycle Methods override open func viewDidLoad() { super.viewDidLoad() self.title = EPGlobalConstants.Strings.contactsTitle - - registerContactCell() + initializeTableView() inititlizeBarButtons() initializeSearchBar() reloadContacts() + } func initializeSearchBar() { self.resultSearchController = ( { let controller = UISearchController(searchResultsController: nil) controller.searchResultsUpdater = self - controller.dimsBackgroundDuringPresentation = false - controller.hidesNavigationBarDuringPresentation = false + controller.dimsBackgroundDuringPresentation = true// + controller.hidesNavigationBarDuringPresentation = true controller.searchBar.sizeToFit() controller.searchBar.delegate = self self.tableView.tableHeaderView = controller.searchBar @@ -79,12 +92,35 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS self.navigationItem.leftBarButtonItem = cancelButton if multiSelectEnabled { - let doneButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.done, target: self, action: #selector(onTouchDoneButton)) - self.navigationItem.rightBarButtonItem = doneButton - + let selectAllContactsButton = UIBarButtonItem(title: EPGlobalConstants.SelectAllContactsButton.selectAllContactString, style: UIBarButtonItemStyle.plain, target: self, action: #selector(onTouchSelectionButton)) + self.navigationItem.rightBarButtonItem = selectAllContactsButton } } + func initializeTableView(){ + tableView = UITableView(frame: view.bounds, style: .plain) + tableView.delegate = self + tableView.dataSource = self + registerContactCell() + view.addSubview(tableView) + } + + func initializeToolBarWithButton(){ + let toolBar = UIToolbar(frame: CGRect(x: 0, + y: view.frame.height - EPGlobalConstants.SendInvitesButton.height, + width: view.frame.width, + height: EPGlobalConstants.SendInvitesButton.height)) + let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) + sendInvitesButton = UIBarButtonItem(title: "", style: .plain, target: self, action: #selector(onTouchSendInvitesButton)) + updateSendInvitesButtonTitle() + let attributes = [NSFontAttributeName:EPGlobalConstants.SendInvitesButton.titleFont!] + sendInvitesButton!.setTitleTextAttributes(attributes, for: []) + toolBar.items = [space, sendInvitesButton!, space] + toolBar.backgroundColor = UIColor.white + tableView.frame = CGRect(x: 0, y: 0, width: tableView.frame.width, height: tableView.frame.height - toolBar.frame.height) + view.addSubview(toolBar) + } + fileprivate func registerContactCell() { let podBundle = Bundle(for: self.classForCoder) @@ -105,6 +141,7 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS tableView.register(cellNib, forCellReuseIdentifier: "Cell") } } + override open func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() @@ -118,18 +155,30 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS } convenience public init(delegate: EPPickerDelegate?, multiSelection : Bool) { - self.init(style: .plain) + self.init() self.multiSelectEnabled = multiSelection contactDelegate = delegate } convenience public init(delegate: EPPickerDelegate?, multiSelection : Bool, subtitleCellType: SubtitleCellValue) { - self.init(style: .plain) + self.init() self.multiSelectEnabled = multiSelection contactDelegate = delegate subtitleCellValue = subtitleCellType } + convenience public init(delegate: EPPickerDelegate?, multiSelection : Bool, subtitleCellType: SubtitleCellValue, selectAllContactsOnLoad: Bool, sendInvitesButtonEnabled: Bool) { + self.init() + self.multiSelectEnabled = multiSelection + contactDelegate = delegate + subtitleCellValue = subtitleCellType + if selectAllContactsOnLoad { + shouldSelectAllContactsOnLoad = true + } + if sendInvitesButtonEnabled { + initializeToolBarWithButton() + } + } // MARK: - Contact Operations @@ -137,9 +186,13 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS getContacts( {(contacts, error) in if (error == nil) { DispatchQueue.main.async(execute: { + if self.shouldSelectAllContactsOnLoad { + self.selectAllContacts() + } self.tableView.reloadData() }) } + self.totalNumberOfLoadedContacts = contacts.count }) } @@ -188,22 +241,18 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS do { try contactsStore?.enumerateContacts(with: contactFetchRequest, usingBlock: { (contact, stop) -> Void in - //Ordering contacts based on alphabets in firstname - contactsArray.append(contact) - var key: String = "#" - //If ordering has to be happening via family name change it here. - if let firstLetter = contact.givenName[0..<1] , firstLetter.containsAlphabets() { - key = firstLetter.uppercased() - } - var contacts = [CNContact]() - - if let segregatedContact = self.orderedContacts[key] { - contacts = segregatedContact - } - contacts.append(contact) - self.orderedContacts[key] = contacts - - }) + switch self.subtitleCellValue { + case .email: + if !contact.emailAddresses.isEmpty { + self.insert(contact: contact, to: &contactsArray) + } + case .phoneNumber: + if !contact.phoneNumbers.isEmpty { + self.insert(contact: contact, to: &contactsArray) + } + default: + self.insert(contact: contact, to: &contactsArray) + } }) self.sortedContactKeys = Array(self.orderedContacts.keys).sorted(by: <) if self.sortedContactKeys.first == "#" { self.sortedContactKeys.removeFirst() @@ -219,6 +268,23 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS } } + func insert(contact: CNContact, to contactsArray: inout [CNContact]){ + //Ordering contacts based on alphabets in firstname + contactsArray.append(contact) + var key: String = "#" + //If ordering has to be happening via family name change it here. + if let firstLetter = contact.givenName[0..<1] , firstLetter.containsAlphabets() { + key = firstLetter.uppercased() + } + var contacts = [CNContact]() + + if let segregatedContact = self.orderedContacts[key] { + contacts = segregatedContact + } + contacts.append(contact) + self.orderedContacts[key] = contacts + } + func allowedContactKeys() -> [CNKeyDescriptor]{ //We have to provide only the keys which we have to access. We should avoid unnecessary keys when fetching the contact. Reducing the keys means faster the access. return [CNContactNamePrefixKey as CNKeyDescriptor, @@ -234,14 +300,31 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS ] } + func selectAllContacts(){ + let array = Array(orderedContacts.values) + selectedContacts.removeAll() + for contactArray in array{ + for contact in contactArray { + let ePContact = EPContact(contact: contact) + selectedContacts.append(ePContact) + } + } + tableView.reloadData() + } + + func deselectAllContacts() { + selectedContacts.removeAll() + tableView.reloadData() + } + // MARK: - Table View DataSource - override open func numberOfSections(in tableView: UITableView) -> Int { + public func numberOfSections(in tableView: UITableView) -> Int { if resultSearchController.isActive { return 1 } return sortedContactKeys.count } - override open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if resultSearchController.isActive { return filteredContacts.count } if let contactsForSection = orderedContacts[sortedContactKeys[section]] { return contactsForSection.count @@ -251,9 +334,9 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS // MARK: - Table View Delegates - override open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! EPContactCell - cell.accessoryType = UITableViewCellAccessoryType.none + cell.contactSelected = false //Convert CNContact to EPContact let contact: EPContact @@ -264,32 +347,31 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS assertionFailure() return UITableViewCell() } - - contact = EPContact(contact: contactsForSection[(indexPath as NSIndexPath).row]) + contact = EPContact(contact: contactsForSection[(indexPath as NSIndexPath).row]) // } if multiSelectEnabled && selectedContacts.contains(where: { $0.contactId == contact.contactId }) { - cell.accessoryType = UITableViewCellAccessoryType.checkmark + cell.contactSelected = true } cell.updateContactsinUI(contact, indexPath: indexPath, subtitleType: subtitleCellValue) return cell } - override open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let cell = tableView.cellForRow(at: indexPath) as! EPContactCell let selectedContact = cell.contact! if multiSelectEnabled { //Keeps track of enable=ing and disabling contacts - if cell.accessoryType == UITableViewCellAccessoryType.checkmark { - cell.accessoryType = UITableViewCellAccessoryType.none + if cell.contactSelected == true { + cell.contactSelected = false selectedContacts = selectedContacts.filter(){ return selectedContact.contactId != $0.contactId } } else { - cell.accessoryType = UITableViewCellAccessoryType.checkmark + cell.contactSelected = true selectedContacts.append(selectedContact) } } @@ -304,22 +386,22 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS } } - override open func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 60.0 } - override open func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int { + public func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int { if resultSearchController.isActive { return 0 } tableView.scrollToRow(at: IndexPath(row: 0, section: index), at: UITableViewScrollPosition.top , animated: false) return sortedContactKeys.index(of: title)! } - override open func sectionIndexTitles(for tableView: UITableView) -> [String]? { + public func sectionIndexTitles(for tableView: UITableView) -> [String]? { if resultSearchController.isActive { return nil } return sortedContactKeys } - override open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + public func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { if resultSearchController.isActive { return nil } return sortedContactKeys[section] } @@ -332,45 +414,92 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS }) } - func onTouchDoneButton() { + func onTouchSendInvitesButton() { dismiss(animated: true, completion: { self.contactDelegate?.epContactPicker(self, didSelectMultipleContacts: self.selectedContacts) }) } + func onTouchSelectionButton() { + if totalNumberOfLoadedContacts > selectedContacts.count { + selectAllContacts() + }else{ + deselectAllContacts() + } + } + // MARK: - Search Actions open func updateSearchResults(for searchController: UISearchController) { if let searchText = resultSearchController.searchBar.text , searchController.isActive { - let predicate: NSPredicate + var predicate: NSPredicate? if searchText.characters.count > 0 { predicate = CNContact.predicateForContacts(matchingName: searchText) + let store = CNContactStore() + do { + filteredContacts = try store.unifiedContacts(matching: predicate!, + keysToFetch: allowedContactKeys()) + //print("\(filteredContacts.count) count") + switch self.subtitleCellValue { + case .email: + filteredContacts = filteredContacts.filter{!$0.emailAddresses.isEmpty} + default: + break + } + self.tableView.reloadData() + + } + catch { + print("Error!") + } } else { - predicate = CNContact.predicateForContactsInContainer(withIdentifier: contactsStore!.defaultContainerIdentifier()) - } - - let store = CNContactStore() - do { - filteredContacts = try store.unifiedContacts(matching: predicate, - keysToFetch: allowedContactKeys()) - //print("\(filteredContacts.count) count") - - self.tableView.reloadData() - - } - catch { - print("Error!") + let array = Array(orderedContacts.values) + filteredContacts.removeAll() + for contactArray in array{ + for contact in contactArray { + filteredContacts.append(contact) + } + } } } } + + open func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { - DispatchQueue.main.async(execute: { self.tableView.reloadData() }) } + open func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { + + } + + //MARK: - Update Appearance Of Send Invites Button + + func updateSendInvitesButtonTitle() { + let numberOfInvites = selectedContacts.count + var title = "" + if numberOfInvites > 0 { + title = "Send \(numberOfInvites) \(numberOfInvites == 1 ? "Invite" : "Invites")" + sendInvitesButton!.isEnabled = true + }else{ + title = "Select Contacts" + sendInvitesButton!.isEnabled = false + } + sendInvitesButton!.title = title + } + + func updateContactSelectionButton(){ + let contactSelectionButton = self.navigationItem.rightBarButtonItem + if totalNumberOfLoadedContacts > selectedContacts.count { + contactSelectionButton?.title = EPGlobalConstants.SelectAllContactsButton.selectAllContactString + }else{ + contactSelectionButton?.title = EPGlobalConstants.SelectAllContactsButton.deselectAllContactsString + } + } + }