I have 2 view controllers. 1st is ViewController in which there is a label and select contact button. When I press select contacts button it present MyContactViewController there is a table view in which I fetched contacts from simulator device using import contacts. I can also select multiple row . After selecting multiple rows when i press Done button . This MyContactViewController get Dismiss and brings me back to the ViewController and all the names of the contacts comes in label. Now problem is this When I again click select Contact button then previously selected cell should appear selected. Below is my code of MyContactViewController and viewController.
import UIKit
class ViewController: UIViewController, DataPassProtocol{
@IBOutlet weak var contactNameLabel: UILabel!
var getName = [String]()
var getNameArray = ""
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func selectContactsBtn(_ sender: UIButton) {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyBoard.instantiateViewController(withIdentifier: "MyContactsViewController")
as! MyContactsViewController
vc.dataPass = self
present(vc, animated: true, completion: nil)
}
func passName(name: [String]) {
getName.append(contentsOf: name)
showData()
}
func showData() {
for index in 0...self.getName.count-1 {
getNameArray = getNameArray + self.getName[index]
}
contactNameLabel.text = getNameArray
getNameArray = ""
}}
code of MyContactViewController
import UIKit
import Contacts
protocol DataPassProtocol{
func passName(name: [String])
}
class MyContactsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate{
@IBOutlet weak var contactSearchBar: UISearchBar!
var contactList = [String]()
var dataPass: DataPassProtocol?
var filterdata = [String]()
var selectedContactName = [String]()
var contactName = [String]()
var searching = false
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.allowsMultipleSelection = true
contactSearchBar.delegate = self
tableView.register(UINib(nibName: "ContactNameTableViewCell", bundle: nil),
forCellReuseIdentifier: "ContactNameTableViewCell")
// Do any additional setup after loading the view.
self.fetchContactData()
}
private func fetchContactData(){
let store = CNContactStore()
store.requestAccess(for: .contacts) { (granted, err) in
if let err = err {
print("failed to fetch Contacts", err)
return
}
if granted{
print("Access Allowed")
let keys = [CNContactGivenNameKey, CNContactFamilyNameKey]
let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
do {
request.sortOrder = CNContactSortOrder.userDefault
try store.enumerateContacts(with: request, usingBlock:
{(contact,stopPointerIfYouWantToStopEnumerating) in
let full_name = contact.givenName + " " + contact.familyName
let contact_model = full_name
self.contactList.append(contact_model)
})
self.tableView.reloadData()
}
catch let err{
print("Failed to fetch contacts", err)
}
} else {
print("Access Denied")
}
}
}
@IBAction func doneBtn(_ sender: UIButton) {
let dataToBeSent = selectedContactName.joined(separator: ", ")
self.dataPass?.passName(name: [dataToBeSent])
dismiss(animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching{
return filterdata.count
}else{
return contactList.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ContactNameTableViewCell", for: indexPath) as! ContactNameTableViewCell
if searching{
cell.nameLabel.text = filterdata[indexPath.row]
}else{
cell.nameLabel.text = contactList[indexPath.row]
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 40
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! ContactNameTableViewCell
if searching{
selectedContactName.append(filterdata[indexPath.row])
} else {
selectedContactName.append(contactList[indexPath.row])
}
cell.checkImage.image = UIImage(named: "unchecked")
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! ContactNameTableViewCell
if selectedContactName.contains(contactList[indexPath.row]){
selectedContactName.remove(at: selectedContactName.firstIndex(of:
contactList[indexPath.row])!)
cell.checkImage.image = UIImage(named: "box")
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == "" {
searching = false
tableView.reloadData()
} else {
searching = true
filterdata = contactList.filter({$0.contains(searchBar.text ?? "")})
tableView.reloadData()
}
}
}
2
Answers
What you need to do is first inject your current selection to your contacts view controller.
Your data source between the two controllers is completely out of sync though. Expected there should be some
Contact
structure where each of them seem to support an array of those. But you use contacts as an array of names as strings in one view controller. In the other view controller things are even worse where you seem to have an array of strings where each string represents multiple contacts by showing their names in a coma separated stringselectedContactName.joined(separator: ", ")
.So with just this data it is unsafe to pass the data between the two view controllers. You could add another method to your protocol and add more properties but… Perhaps you should try to cleanup your interface…
I will only write some parts that are relevant for you to be put on the right track. But most of the code that you posted will still need changes to use such data source. Not that those changes are big but there are many of them:
Use a structure to represent your contact. Just the data you actually need for your UI. Don’t put
CNContact
stuff in it but do still copy the ID fromCNContact
to it so that you can match results.A protocol that used for delegate approach usually looks like this:
The view controller to select your contacts:
This is how it should be used:
I hope most of the stuff here is descriptive enough. If you have specific question please do ask.
You can get previously selected contact in ContactsViewController by making selectedContactName array global as below and
checking it in the cellForRow method whether selectedContactName contains contacts or not.
and in viewController you should have to blank getName array to remove duplicate contact as below.