|
|
@ -33,7 +33,10 @@ public enum SubtitleCellValue{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@available(iOS 9.0, *)
|
|
|
|
@available(iOS 9.0, *)
|
|
|
|
open class ContactsPicker: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
|
|
|
|
open class ContactsPicker: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@IBOutlet var tableView: UITableView!
|
|
|
|
|
|
|
|
@IBOutlet var searchBar: UISearchBar!
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - Properties
|
|
|
|
// MARK: - Properties
|
|
|
|
|
|
|
|
|
|
|
@ -42,7 +45,6 @@ open class ContactsPicker: UITableViewController, UISearchResultsUpdating, UISea
|
|
|
|
let contactsManager: OWSContactsManager
|
|
|
|
let contactsManager: OWSContactsManager
|
|
|
|
let collation = UILocalizedIndexedCollation.current()
|
|
|
|
let collation = UILocalizedIndexedCollation.current()
|
|
|
|
let contactStore = CNContactStore()
|
|
|
|
let contactStore = CNContactStore()
|
|
|
|
lazy var resultSearchController = UISearchController()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Data Source State
|
|
|
|
// Data Source State
|
|
|
|
lazy var sections = [[CNContact]]()
|
|
|
|
lazy var sections = [[CNContact]]()
|
|
|
@ -66,8 +68,9 @@ open class ContactsPicker: UITableViewController, UISearchResultsUpdating, UISea
|
|
|
|
super.viewDidLoad()
|
|
|
|
super.viewDidLoad()
|
|
|
|
title = NSLocalizedString("INVITE_FRIENDS_PICKER_TITLE", comment: "Navbar title")
|
|
|
|
title = NSLocalizedString("INVITE_FRIENDS_PICKER_TITLE", comment: "Navbar title")
|
|
|
|
|
|
|
|
|
|
|
|
// Don't obscure table header (search bar) with table index
|
|
|
|
searchBar.placeholder = NSLocalizedString("INVITE_FRIENDS_PICKER_SEARCHBAR_PLACEHOLDER", comment: "Search")
|
|
|
|
tableView.sectionIndexBackgroundColor = UIColor.clear
|
|
|
|
// Prevent content form going under the navigation bar
|
|
|
|
|
|
|
|
self.edgesForExtendedLayout = []
|
|
|
|
|
|
|
|
|
|
|
|
// Auto size cells for dynamic type
|
|
|
|
// Auto size cells for dynamic type
|
|
|
|
tableView.estimatedRowHeight = 60.0
|
|
|
|
tableView.estimatedRowHeight = 60.0
|
|
|
@ -78,7 +81,7 @@ open class ContactsPicker: UITableViewController, UISearchResultsUpdating, UISea
|
|
|
|
registerContactCell()
|
|
|
|
registerContactCell()
|
|
|
|
initializeBarButtons()
|
|
|
|
initializeBarButtons()
|
|
|
|
reloadContacts()
|
|
|
|
reloadContacts()
|
|
|
|
initializeSearchBar()
|
|
|
|
updateSearchResults(searchText: "")
|
|
|
|
|
|
|
|
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(self.didChangePreferredContentSize), name: NSNotification.Name.UIContentSizeCategoryDidChange, object: nil)
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(self.didChangePreferredContentSize), name: NSNotification.Name.UIContentSizeCategoryDidChange, object: nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -86,20 +89,6 @@ open class ContactsPicker: UITableViewController, UISearchResultsUpdating, UISea
|
|
|
|
func didChangePreferredContentSize() {
|
|
|
|
func didChangePreferredContentSize() {
|
|
|
|
self.tableView.reloadData()
|
|
|
|
self.tableView.reloadData()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func initializeSearchBar() {
|
|
|
|
|
|
|
|
self.resultSearchController = ( {
|
|
|
|
|
|
|
|
let controller = UISearchController(searchResultsController: nil)
|
|
|
|
|
|
|
|
controller.searchResultsUpdater = self
|
|
|
|
|
|
|
|
controller.dimsBackgroundDuringPresentation = false
|
|
|
|
|
|
|
|
controller.searchBar.sizeToFit()
|
|
|
|
|
|
|
|
controller.searchBar.delegate = self
|
|
|
|
|
|
|
|
controller.hidesNavigationBarDuringPresentation = false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.tableView.tableHeaderView = controller.searchBar
|
|
|
|
|
|
|
|
return controller
|
|
|
|
|
|
|
|
})()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func initializeBarButtons() {
|
|
|
|
func initializeBarButtons() {
|
|
|
|
let cancelButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.cancel, target: self, action: #selector(onTouchCancelButton))
|
|
|
|
let cancelButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.cancel, target: self, action: #selector(onTouchCancelButton))
|
|
|
@ -117,9 +106,9 @@ open class ContactsPicker: UITableViewController, UISearchResultsUpdating, UISea
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - Initializers
|
|
|
|
// MARK: - Initializers
|
|
|
|
|
|
|
|
|
|
|
|
override init(style: UITableViewStyle) {
|
|
|
|
init() {
|
|
|
|
contactsManager = Environment.getCurrent().contactsManager
|
|
|
|
contactsManager = Environment.getCurrent().contactsManager
|
|
|
|
super.init(style: style)
|
|
|
|
super.init(nibName: nil, bundle: nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
required public init?(coder aDecoder: NSCoder) {
|
|
|
|
required public init?(coder aDecoder: NSCoder) {
|
|
|
@ -132,13 +121,13 @@ open class ContactsPicker: UITableViewController, UISearchResultsUpdating, UISea
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
convenience public init(delegate: ContactsPickerDelegate?, multiSelection : Bool) {
|
|
|
|
convenience public init(delegate: ContactsPickerDelegate?, multiSelection : Bool) {
|
|
|
|
self.init(style: .plain)
|
|
|
|
self.init()
|
|
|
|
multiSelectEnabled = multiSelection
|
|
|
|
multiSelectEnabled = multiSelection
|
|
|
|
contactsPickerDelegate = delegate
|
|
|
|
contactsPickerDelegate = delegate
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
convenience public init(delegate: ContactsPickerDelegate?, multiSelection : Bool, subtitleCellType: SubtitleCellValue) {
|
|
|
|
convenience public init(delegate: ContactsPickerDelegate?, multiSelection : Bool, subtitleCellType: SubtitleCellValue) {
|
|
|
|
self.init(style: .plain)
|
|
|
|
self.init()
|
|
|
|
multiSelectEnabled = multiSelection
|
|
|
|
multiSelectEnabled = multiSelection
|
|
|
|
contactsPickerDelegate = delegate
|
|
|
|
contactsPickerDelegate = delegate
|
|
|
|
subtitleCellValue = subtitleCellType
|
|
|
|
subtitleCellValue = subtitleCellType
|
|
|
@ -192,7 +181,6 @@ open class ContactsPicker: UITableViewController, UISearchResultsUpdating, UISea
|
|
|
|
contacts.append(contact)
|
|
|
|
contacts.append(contact)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.sections = collatedContacts(contacts)
|
|
|
|
self.sections = collatedContacts(contacts)
|
|
|
|
self.tableView.reloadData()
|
|
|
|
|
|
|
|
} catch let error as NSError {
|
|
|
|
} catch let error as NSError {
|
|
|
|
Logger.error("\(self.TAG) Failed to fetch contacts with error:\(error)")
|
|
|
|
Logger.error("\(self.TAG) Failed to fetch contacts with error:\(error)")
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -213,22 +201,22 @@ open class ContactsPicker: UITableViewController, UISearchResultsUpdating, UISea
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - Table View DataSource
|
|
|
|
// MARK: - Table View DataSource
|
|
|
|
|
|
|
|
|
|
|
|
override open func numberOfSections(in tableView: UITableView) -> Int {
|
|
|
|
open func numberOfSections(in tableView: UITableView) -> Int {
|
|
|
|
return self.collation.sectionTitles.count
|
|
|
|
return self.collation.sectionTitles.count
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
|
|
open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
|
|
let dataSource = resultSearchController.isActive ? filteredSections : sections
|
|
|
|
let dataSource = filteredSections
|
|
|
|
|
|
|
|
|
|
|
|
return dataSource[section].count
|
|
|
|
return dataSource[section].count
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - Table View Delegates
|
|
|
|
// MARK: - Table View Delegates
|
|
|
|
|
|
|
|
|
|
|
|
override open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
|
|
open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
|
|
let cell = tableView.dequeueReusableCell(withIdentifier: contactCellReuseIdentifier, for: indexPath) as! ContactCell
|
|
|
|
let cell = tableView.dequeueReusableCell(withIdentifier: contactCellReuseIdentifier, for: indexPath) as! ContactCell
|
|
|
|
|
|
|
|
|
|
|
|
let dataSource = resultSearchController.isActive ? filteredSections : sections
|
|
|
|
let dataSource = filteredSections
|
|
|
|
let cnContact = dataSource[indexPath.section][indexPath.row]
|
|
|
|
let cnContact = dataSource[indexPath.section][indexPath.row]
|
|
|
|
let contact = Contact(contact: cnContact)
|
|
|
|
let contact = Contact(contact: cnContact)
|
|
|
|
|
|
|
|
|
|
|
@ -247,7 +235,7 @@ open class ContactsPicker: UITableViewController, UISearchResultsUpdating, UISea
|
|
|
|
return cell
|
|
|
|
return cell
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override open func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
|
|
|
|
open func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
|
|
|
|
let cell = tableView.cellForRow(at: indexPath) as! ContactCell
|
|
|
|
let cell = tableView.cellForRow(at: indexPath) as! ContactCell
|
|
|
|
let deselectedContact = cell.contact!
|
|
|
|
let deselectedContact = cell.contact!
|
|
|
|
|
|
|
|
|
|
|
@ -256,7 +244,7 @@ open class ContactsPicker: UITableViewController, UISearchResultsUpdating, UISea
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
|
|
open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
|
|
let cell = tableView.cellForRow(at: indexPath) as! ContactCell
|
|
|
|
let cell = tableView.cellForRow(at: indexPath) as! ContactCell
|
|
|
|
let selectedContact = cell.contact!
|
|
|
|
let selectedContact = cell.contact!
|
|
|
|
|
|
|
|
|
|
|
@ -269,23 +257,22 @@ open class ContactsPicker: UITableViewController, UISearchResultsUpdating, UISea
|
|
|
|
|
|
|
|
|
|
|
|
if !multiSelectEnabled {
|
|
|
|
if !multiSelectEnabled {
|
|
|
|
//Single selection code
|
|
|
|
//Single selection code
|
|
|
|
resultSearchController.isActive = false
|
|
|
|
|
|
|
|
self.dismiss(animated: true) {
|
|
|
|
self.dismiss(animated: true) {
|
|
|
|
self.contactsPickerDelegate?.contactsPicker(self, didSelectContact: selectedContact)
|
|
|
|
self.contactsPickerDelegate?.contactsPicker(self, didSelectContact: selectedContact)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override open func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
|
|
|
|
open func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
|
|
|
|
return collation.section(forSectionIndexTitle: index)
|
|
|
|
return collation.section(forSectionIndexTitle: index)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override open func sectionIndexTitles(for tableView: UITableView) -> [String]? {
|
|
|
|
open func sectionIndexTitles(for tableView: UITableView) -> [String]? {
|
|
|
|
return collation.sectionIndexTitles
|
|
|
|
return collation.sectionIndexTitles
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
|
|
|
open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
|
|
|
let dataSource = resultSearchController.isActive ? filteredSections : sections
|
|
|
|
let dataSource = filteredSections
|
|
|
|
|
|
|
|
|
|
|
|
if dataSource[section].count > 0 {
|
|
|
|
if dataSource[section].count > 0 {
|
|
|
|
return collation.sectionTitles[section]
|
|
|
|
return collation.sectionTitles[section]
|
|
|
@ -307,33 +294,25 @@ open class ContactsPicker: UITableViewController, UISearchResultsUpdating, UISea
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - Search Actions
|
|
|
|
// MARK: - Search Actions
|
|
|
|
|
|
|
|
open func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
|
|
|
|
|
|
|
updateSearchResults(searchText: searchText)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
open func updateSearchResults(for searchController: UISearchController) {
|
|
|
|
open func updateSearchResults(searchText: String) {
|
|
|
|
if let searchText = resultSearchController.searchBar.text , searchController.isActive {
|
|
|
|
let predicate: NSPredicate
|
|
|
|
|
|
|
|
if searchText.characters.count == 0 {
|
|
|
|
let predicate: NSPredicate
|
|
|
|
filteredSections = sections
|
|
|
|
if searchText.characters.count == 0 {
|
|
|
|
} else {
|
|
|
|
filteredSections = sections
|
|
|
|
do {
|
|
|
|
} else {
|
|
|
|
predicate = CNContact.predicateForContacts(matchingName: searchText)
|
|
|
|
do {
|
|
|
|
let filteredContacts = try contactStore.unifiedContacts(matching: predicate,keysToFetch: allowedContactKeys)
|
|
|
|
predicate = CNContact.predicateForContacts(matchingName: searchText)
|
|
|
|
|
|
|
|
let filteredContacts = try contactStore.unifiedContacts(matching: predicate, keysToFetch: allowedContactKeys)
|
|
|
|
|
|
|
|
filteredSections = collatedContacts(filteredContacts)
|
|
|
|
filteredSections = collatedContacts(filteredContacts)
|
|
|
|
} catch let error as NSError {
|
|
|
|
} catch let error as NSError {
|
|
|
|
Logger.error("\(self.TAG) updating search results failed with error: \(error)")
|
|
|
|
Logger.error("\(self.TAG) updating search results failed with error: \(error)")
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
self.tableView.reloadData()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
open func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
|
|
|
|
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
|
|
|
self.tableView.reloadData()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.tableView.reloadData()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@available(iOS 9.0, *)
|
|
|
|
@available(iOS 9.0, *)
|
|
|
|