我有两个视图控制器。第一个是ViewController,其中有一个标签和选择联系人按钮。当我按下选择联系人按钮时,它显示MyContactViewController,有一个表视图,我在其中使用导入联系人从模拟器设备获取联系人。我也可以选择多行。选择多行后,当我按下"完成"按钮。这个MyContactViewController被Dismiss,并将我带回ViewController,所有联系人的名称都在标签中。现在的问题是,当我再次单击"选择联系人"按钮时,之前选择的单元格应该显示为已选中。下面是我的MyContactViewController和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 = ""
}}
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()
}
}
}
您可以在ContactsViewController中获取以前选择的联系人,方法是将selectedContactName数组设置为全局,如下所示在cellForRow方法中检查selectedContactName是否包含联系人。
import UIKit
import Contacts
protocol DataPassProtocol{
func passName(name: [String])
}
var selectedContactName = [String]()
class MyContactsViewController: UIViewController ,UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate{
@IBOutlet weak var contactSearchBar: UISearchBar!
var contactList = [String]()
var dataPass: DataPassProtocol?
var filterdata = [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()
// Do any additional setup after loading the view.
}
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)
})
DispatchQueue.main.async {
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
cell.selectionStyle = .none
var contactDict = [String]()
if searching{
contactDict = filterdata
}else{
contactDict = contactList
}
cell.nameLabel.text = contactDict[indexPath.row]
if(selectedContactName.contains(contactDict[indexPath.row]))
{
cell.checkImage.image = UIImage(named: "check")
}
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
print(selectedContactName)
var contactDict = [String]()
if searching{
contactDict = filterdata
}else{
contactDict = contactList
}
if(selectedContactName.contains(contactDict[indexPath.row]))
{
selectedContactName.remove(at: selectedContactName.firstIndex(of:
contactDict[indexPath.row])!)
cell.checkImage.image = nil
}
else{
selectedContactName.append(contactDict[indexPath.row])
cell.checkImage.image = UIImage(named: "check")
}
tableView.deselectRow(at: indexPath, animated: false)
}
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()
}
}
}
在viewController中,您必须清空getName数组才能删除重复的联系人,如下所示。
func passName(name: [String]) {
getName = []
getName.append(contentsOf: name)
showData()
}
您需要做的是首先将当前选择注入到联系人视图控制器中。
不过,两个控制器之间的数据源完全不同步。预期应该有一些Contact
结构,其中每个结构似乎都支持这些结构的数组。但是,在一个视图控制器中,您可以使用联系人作为字符串形式的名称数组。在另一个视图控制器中,情况更糟,因为您似乎有一个字符串数组,其中每个字符串通过在昏迷分隔的字符串selectedContactName.joined(separator: ", ")
中显示联系人的名称来表示多个联系人。
因此,只有这些数据,在两个视图控制器之间传递数据是不安全的。您可以向协议中添加另一个方法并添加更多属性,但是。。。也许你应该试着清理你的界面。。。
我只会写一些与你相关的部分,让你走上正轨。但您发布的大多数代码仍需要更改才能使用此类数据源。并不是说这些变化很大,但有很多变化:
使用一个结构来表示您的联系人。只是UI实际需要的数据。不要把CNContact
的东西放在里面,但仍然要把ID从CNContact
复制到里面,这样你就可以匹配结果。
struct Contact {
let id: String
let displayName: String
}
用于委托方法的协议通常如下所示:
protocol MyContactViewControllerDelegate: AnyObject {
func myContactViewController(_ sender: MyContactViewController, didSelectContacts contacts: [Contact])
}
用于选择联系人的视图控制器:
class MyContactViewController: UIViewController {
var allContacts: [Contact] = [] // All contacts that may be shown
var selectedContacts: [Contact] = [] // Contacts that are currently selected
weak var delegate: MyContactViewControllerDelegate?
@IBOutlet private var tableView: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
reload()
}
private func reload() {
Contact.fetchAllContacts { contacts in // This method does all the things with contact list from your address book or whatever...
self.allContacts = contacts
self.tableView?.reloadData()
self.applyTableViewCellSelection()
}
}
// This method needs to be called when data source changes. Either new contacts are received or when filter is applied
private func applyTableViewCellSelection() {
let currentlyShownContacts = allContacts // TODO: decide based on applied filter
let selectedContactIDList: [String] = self.selectedContacts.map { $0.id } // ID list of all selected contacts
// Get indices of all contacts that are selected so that we may convert it to table view index path
let selectedIndices: [Int] = {
// This can all be done in a single line but let's keep it like this for demo
let allContacts = currentlyShownContacts
let indexedContacts = allContacts.enumerated()
let filteredIndexedContacts = indexedContacts.filter { selectedContactIDList.contains($0.element.id) }
let listOfIndices: [Int] = filteredIndexedContacts.map { $0.offset }
return listOfIndices
}()
// Select all indices in table view
selectedIndices.forEach { index in
self.tableView?.selectRow(at: IndexPath(row: index, section: 0), animated: false, scrollPosition: .none)
}
// Deselect all previously selected
self.tableView?.indexPathsForSelectedRows?.forEach { indexPath in
if selectedIndices.contains(indexPath.row) == false {
self.tableView?.deselectRow(at: indexPath, animated: false)
}
}
}
@objc private func onDonePressed() {
delegate?.myContactViewController(self, didSelectContacts: selectedContacts)
}
}
这就是它应该如何使用:
class ViewController: UIViewController, MyContactViewControllerDelegate {
private var selectedContacts: [Contact] = []
@objc private func manageContactsPressed() {
let controller: MyContactViewController = MyContactViewController.fromStoryboard() // The storyboard logic goes here
controller.delegate = self
controller.selectedContacts = selectedContacts
present(controller, animated: true, completion: nil)
}
func myContactViewController(_ sender: MyContactViewController, didSelectContacts contacts: [Contact]) {
selectedContacts = contacts
// TODO: reload relevant UI here
}
}
我希望这里的大部分内容都是描述性的。如果你有具体的问题,请一定要问。