tableView reloadData doesn't work, delegate methods - ios

I am trying to create new category in 1 view controller (AddCategoryViewController) and show it in table view controller (CategoryViewController). But there's an issue with reloading data.
New category item shows only after turning on and off the app, even when there is tableView.reloadData().
I tried to change the title of navigation in addButtonPressed function and the title changes immediately.
When I was using UIAlertView to add data, tableView.reloadData() worked. So I guess it's something with 2 view controllers and delegate methods?
Thanks for your help <3
show item:
import UIKit
import CoreData
class CategoryViewController: UITableViewController {
#IBOutlet weak var navigation: UINavigationItem!
var categoryArray = [Category]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad() {
super.viewDidLoad()
loadCategory()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return categoryArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "CategoryItemCell")
cell.textLabel?.text = categoryArray[indexPath.row].name
if let randomColor = categoryArray[indexPath.row].color {
cell.textLabel?.textColor = UIColor(hex: randomColor)
}
return cell
}
// MARK: - Table view data source
#IBAction func addPressed(_ sender: UIBarButtonItem) {
let addCategoryVC = storyboard?.instantiateViewController(withIdentifier: "AddCategoryViewController") as! AddCategoryViewController
addCategoryVC.delegate = self
present(addCategoryVC, animated: true, completion: nil)
}
// MARK: - CoreData methods
func saveCategory() {
do {
try context.save()
} catch {
print("Save error: \(error)")
}
tableView.reloadData()
}
func loadCategory(with request: NSFetchRequest<Category> = Category.fetchRequest()) {
do {
categoryArray = try context.fetch(request)
} catch {
print("Load error: \(error)")
}
tableView.reloadData()
}
func addCategory(name: String, description: String) {
let newCategory = Category(context: context.self)
newCategory.name = name
newCategory.descriptionOfCategory = description
newCategory.color = UIColor.random().toHex
saveCategory()
print("name form func: \(name)")
print("description from func: \(description)")
}
}
// MARK: AddCateogry delegate methods
extension CategoryViewController: AddCategoryDelegate {
func addButtonPressed(name: String, description: String) {
addCategory(name: name, description: description)
navigation.title = "I have changed!"
}
}
Add item:
import UIKit
protocol AddCategoryDelegate {
func addButtonPressed(name: String, description: String)
}
class AddCategoryViewController: UIViewController {
var delegate : AddCategoryDelegate!
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var descriptionTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func addCategoryButtonPressed(_ sender: UIButton) {
delegate.addButtonPressed(name: nameTextField.text!, description: descriptionTextField.text!)
dismiss(animated: true, completion: nil)
}
}

You only save the category to coredata inside addCategory , but you have to add the item to the array also , or call loadCategory before tableView.reloadData() inside saveCategory

Related

Cannot assign value of type 'ViewController' to type 'AddContactDelegate?'

import UIKit
struct Contact {
var fullname: String
var contactNumber: String
}
class ViewController: UITableViewController {
var contacts = [Contact]()
#IBOutlet var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func handleAdd(_ sender: Any) {
let controller = AddContacts()
controller.delegate = self
self.present(UINavigationController(rootViewController: controller), animated: true, completion: nil)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableview.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = contacts[indexPath.row].fullname
cell.detailTextLabel?.text = contacts[indexPath.row].contactNumber
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contacts.count
}
}
import UIKit
protocol AddContactDelegate {
func addContact(contact: Contact)
}
class AddContacts: UIViewController {
var delegate: AddContactDelegate?
#IBOutlet weak var ContactTextField: UITextField!
#IBOutlet weak var nameTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func save(_ sender: Any) {
guard let fullname = nameTextField.text, nameTextField.hasText else {
print("handle error here")
return
}
guard let contactNumber = ContactTextField.text , ContactTextField.hasText else {
print("enter contact error here")
return
}
let contact = Contact(fullname: fullname, contactNumber: contactNumber)
print(contact.fullname)
print(contact.contactNumber)
delegate?.addContact(contact: contact)
}
}
in viewController: UITableViewController file it shows error like Cannot assign value of type 'ViewController' to type 'AddContactDelegate?' what should do i do to solve these error
import UIKit
struct Contact {
var fullname: String
var contactNumber: String
}
class ViewController: UITableViewController {
var contacts = [Contact]()
#IBOutlet var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func handleAdd(_ sender: Any) {
let controller = AddContacts()
controller.delegate = self
self.present(UINavigationController(rootViewController: controller), animated: true, completion: nil)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableview.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = contacts[indexPath.row].fullname
cell.detailTextLabel?.text = contacts[indexPath.row].contactNumber
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contacts.count
}
}
// add this
extension ViewController: AddContactDelegate {
func addContact(contact: Contact) {
contacts.append(contact)
tableView.reloadData()
}
}
// thats it
import UIKit
protocol AddContactDelegate:AnyObject {
func addContact(contact: Contact)
}
class AddContacts: UIViewController {
weak var delegate: AddContactDelegate?
#IBOutlet weak var ContactTextField: UITextField!
#IBOutlet weak var nameTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func save(_ sender: Any) {
guard let fullname = nameTextField.text, nameTextField.hasText else {
print("handle error here")
return
}
guard let contactNumber = ContactTextField.text , ContactTextField.hasText else {
print("enter contact error here")
return
}
let contact = Contact(fullname: fullname, contactNumber: contactNumber)
print(contact.fullname)
print(contact.contactNumber)
delegate?.addContact(contact: contact)
}
}
You must implement the protocol inside the ViewController.
Why xcode shows you the error is:
protocol ViewDelegate: AnyObject {
func didDoSomething()
}
// Which means - reference with name delegate can store objects that conform to the protocol ViewDelegate
var delegate: ViewDelegate
If you did not conform the object you are trying to store to this reference with the desired protocol, you will not be able to store that object to that reference.
You can look at protocols like contracts, if the protocol is implemented in a specific class, the class must implement the declared methods inside the protocols.
Simply implementing this protocol to your ViewController and adding the method declared in the protocol (contract) will make you achieve what you want.
class MyViewController: ViewDelegate {
func didDoSomething() {
//TODO: Logic for this method
}
}
//Will not give compile errors
let delegate: ViewDelegate = MyViewController()
Just for additional info, you can always implement a delegate in this way
class MyViewController {
//properties
}
//MARK: - ViewDelegate implementation
extension MyViewController: ViewDelegate {
func didDoSomething() {
//TODO: logic
}
}
Hope it helps.

duplicate data showing in UITableView after UIViewController being dismissed

Problems :
UITableview populate duplicate data even after removing the array when it dismiss
Expected output:
UITableview populate data based on itenary items in array
Actual output:
UITableView populates the correct amount output when the user selects the first location in DiscoverVC but when the user selects another location, the tableview append the itenary data that the user-selected previouly.
Summary:
I have 3VC in my project, first vc (DiscoverVC), will call api to populate data in UICollectionView, I implement UICollectionView Delegate to move to another screen with segue, in prepare segue I pass data from the first vc to second vc (ItenaryVC), in second vc i have 2 view inside it. One normal vc and the second is floating panel (ItenaryFP). When second vc loads up it will make API calls base on Location object that had been pass from first vc and pass the data to the third vc which is floating panel (ItenaryFP) through delegate in ItenaryVC.
PS; I use custom cell for the tableview and have, I also already try to remove the array from viewWillAppear and viewDidDissapear but it's still not working
GIF of how the issues occurs
Here is a summary of my code
DiscoverVC.swift
class DiscoverVC : UIViewController {
//MARK:- IBOutlets
#IBOutlet weak var collectionView: UICollectionView!
private var locationResult = [Location]()
private var selectedAtRow : Int!
//MARK:- Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
renderView()
getLocations()
}
private func renderView() {
collectionView.register(UINib(nibName: R.nib.discoverCell.name, bundle: nil), forCellWithReuseIdentifier: R.reuseIdentifier.discoverCell.identifier)
collectionView.delegate = self
collectionView.dataSource = self
}
private func getLocations(location : String = "locations") {
NetworkManager.shared.getLocations(for: location) { [weak self] location in
switch location {
case .success(let locations):
self?.updateDiscoverUI(with: locations)
case .failure(let error):
print(error.rawValue)
}
}
}
private func updateDiscoverUI(with locations : [Location]) {
DispatchQueue.main.async { [weak self] in
self?.locationResult.append(contentsOf: locations)
self?.collectionView.reloadData()
}
}
}
//MARK:- Delegate
extension DiscoverVC : UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedAtRow = indexPath.row
self.performSegue(withIdentifier: R.segue.discoverVC.goToDetails, sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let destinationVC = segue.destination as? ItenaryVC else { return}
// Passing location object to ItenaryVC
destinationVC.locationName = locationResult[selectedAtRow]
destinationVC.imageURL = locationResult[selectedAtRow].image
// Remove tab bar when push to other vc
destinationVC.hidesBottomBarWhenPushed = true
}
}
ItenaryVC.swift
protocol ItenaryVCDelegate : AnyObject {
func didSendItenaryData(_ itenaryVC : ItenaryVC, with itenary : [[Days]])
func didSendLocationData(_ itenaryVC : ItenaryVC, with location : Location)
}
class ItenaryVC: UIViewController {
#IBOutlet weak var backgroundImage: UIImageView!
var fpc : FloatingPanelController!
var imageURL : URL?
var locationName: Location?
weak var delegate : ItenaryVCDelegate?
//MARK:- Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
setupCard()
setupView()
}
override func viewWillAppear(_ animated: Bool) {
// Call API for data
getItenaries(at: locationName!.itenaryName)
print("ItenaryVC Appear")
}
override func viewWillDisappear(_ animated: Bool) {
locationName = nil
}
}
//MARK:- Network Request
extension ItenaryVC {
func getItenaries(at itenaries : String = "Melaka"){
print("itenaries : \(itenaries)")
NetworkManager.shared.getItenaries(for: itenaries) { [weak self] itenary in
switch itenary {
case .success(let itenary):
// print(itenary)
DispatchQueue.main.async {
// Passing data to itenaryFP
self?.delegate?.didSendItenaryData(self! , with: itenary)
}
print(itenaries.count)
case .failure(let error):
print(error.rawValue)
}
}
}
}
//MARK:- Private methods
extension ItenaryVC {
private func setupView() {
backgroundImage.downloaded(from: imageURL!)
backgroundImage.contentMode = .scaleAspectFill
// Passing data to itenaryFP
delegate?.didSendLocationData(self, with: locationName!)
}
private func setupCard() {
guard let itenaryFlotingPanelVC = storyboard?.instantiateViewController(identifier: "itenaryPanel") as? ItenaryFP else { return}
// Initliase delegate to Floating Panel, create strong reference to Panel
self.delegate = itenaryFlotingPanelVC
fpc = FloatingPanelController()
fpc.set(contentViewController: itenaryFlotingPanelVC)
fpc.addPanel(toParent: self)
fpc.delegate = self
fpc.layout = self
}
}
ItenaryFP.swift
class ItenaryFP: UIViewController{
var itenaries = [[Days]]()
var location : Location?
//MARK:- : Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
print("ItrenaryFP viewDidLoad, itenaries : \(itenaries.count), location : \(location)")
renderView()
}
override func viewWillAppear(_ animated: Bool) {
itenaries.removeAll()
itenaryTableView.reloadData()
}
override func viewWillDisappear(_ animated: Bool) {
DispatchQueue.main.async {
self.itenaries.removeAll()
print("ItenarFP dissapear, itenaries :\(self.itenaries.count), location : \(self.location)")
self.location = nil
self.itenaryTableView.reloadData()
}
}
private func renderView() {
itenaryTableView.register(UINib(nibName: R.nib.itenaryCell.name, bundle: nil), forCellReuseIdentifier: R.nib.itenaryCell.identifier)
itenaryTableView.dataSource = self
itenaryTableView.delegate = self
locDescHC.constant = locDesc.contentSize.height
}
}
//MARK:- Data source
extension ItenaryFP : UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return itenaries.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itenaries[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : ItenaryCell = itenaryTableView.dequeueReusableCell(withIdentifier: R.nib.itenaryCell.identifier, for: indexPath) as! ItenaryCell
let listOfItenaries = itenaries[indexPath.section][indexPath.row]
cell.cellContent(for: listOfItenaries)
return cell
}
}
//MARK:- ItenaryVC Delegate
extension ItenaryFP : ItenaryVCDelegate {
func didSendLocationData(_ itenaryVC: ItenaryVC, with location: Location) {
DispatchQueue.main.async {
self.locationLabel.text = location.locationName
self.locDesc.text = location.description
self.sloganLabel.text = location.slogan
}
}
func didSendItenaryData(_ itenaryVC: ItenaryVC, with itenary: [[Days]]) {
DispatchQueue.main.async {
self.itenaries.append(contentsOf: itenary)
self.itenaryTableView.reloadData()
print("itenary \(self.itenaries.count)")
}
}
}
Use:
self.itenaries = itenary
Instead of:
self.itenaries.append(contentsOf: itenary)

Why wont my tabe view reload after i save data data and call the tableview.reloadData() function

I am confused to why my table view is not reloading after i call the tableView.reloadData() function. Here is what you should know about my project. The initial view controller is a tableViewController and when you click the add button in the navigation bar it pulls up presents the "addItemViewController". It is presented overCurrentContext. Everything to this point works fine, but the part that doesn't work is when you fill out the info in the pop up I created and push the button to save it it saves to the core data fill but when i reload it it doesnt even call that. When i close the app and reload it the data shows up but it doesnt show up when i add it and call the same function.
import UIKit
import CoreData
protocol reloadTableView: class {
func reloadTableView()
}
class TableViewController: UITableViewController {
//Global Variables
let addItemVC = AddItemController()
var itemArray = [Item]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad() {
super.viewDidLoad()
addItemVC.delegate = self
loadItems()
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.cellIdentifier, for: indexPath) as! Cell
let array = itemArray[indexPath.row]
cell.dateCreated.text = array.dateCreated
cell.workoutLabel.text = array.workoutName
cell.weightLifted.text = array.weight
return cell
}
//MARK: - Add Button Pressed
#IBAction func addItemPressed(_ sender: UIBarButtonItem) {
let storyboard = UIStoryboard(name: "AddItem", bundle: nil)
let addItemVC = storyboard.instantiateViewController(identifier: "AddItemController")
addItemVC.isModalInPresentation = true
addItemVC.modalPresentationStyle = .overCurrentContext
addItemVC.modalTransitionStyle = .crossDissolve
addItemVC.navigationController?.isNavigationBarHidden = true
present(addItemVC, animated: true, completion: nil)
}
//MARK: - Create and Load Functions
func saveData() {
do {
try context.save()
} catch {
print("Error Saving Data \(error)")
}
tableView.reloadData()
}
func loadItems() {
let request: NSFetchRequest<Item> = Item.fetchRequest()
do {
itemArray = try context.fetch(request)
} catch {
print("error")
}
tableView.reloadData()
}
}
//MARK:// - Add Item Vc Delegate
extension TableViewController: reloadTableView {
func reloadTableView() {
do {
try context.save()
} catch {
print("Error Saving Data \(error)")
}
let request: NSFetchRequest<Item> = Item.fetchRequest()
do {
itemArray = try context.fetch(request)
} catch {
print("error")
}
tableView.reloadData()
print("There are", itemArray.count, "in the item array")
print(itemArray.last?.workoutName)
//the print statement are not showing up in console
}
}
and the second file
import UIKit
import CoreData
class AddItemController: UIViewController {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var delegate: reloadTableView?
#IBOutlet weak var viewContainer: UIView!
#IBOutlet weak var exercise: UITextField!
#IBOutlet weak var weight: UITextField!
#IBOutlet weak var reps: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addMaxPressed(_ sender: UIButton) {
if exercise.text != "" && weight.text != "" && reps.text != "" {
let newItem = Item(context: context)
let formatter = DateFormatter()
newItem.dateCreated = formatter.formattedDate()
newItem.weight = weight.text
newItem.reps = reps.text
newItem.workoutName = exercise.text
dismiss(animated: true) {
self.delegate?.reloadTableView()
}
}
}
#IBAction func exitPressed(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
}
//MARK: - UITextField func
extension AddItemController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return true
}
}
You're setting the delegate on the wrong instance of the add item view controller.
You create one instance with...
let addItemVC = AddItemController()
...and another with...
let addItemVC = storyboard.instantiateViewController(identifier: "AddItemController")
You set the delegate on the first of those, but present the second. That means when you get to...
self.delegate?.reloadTableView()
...of the presented controller, nothing happens.
If you're not using the first instance, get rid of it and set the delegate in the same section where you set the presentation style, etc.
When you put ? after an optional, it means you don't want to know whether it did what you asked or not. Obviously, you do want to know so you should test the value instead and print a message if the value isn't what you expect.

tableview sent me an error when I tried to present a uiview in front of it

I'm new with Xcode and Swift, following a tutorial and I found a problem when I called a UIView in front of a tableview so the user can create something new
NOTE: I already tried what this link shows with no luck to resolve my issue
I am using Xcode 11.3.1 and Swift
This is my code
Channel Model
import Foundation
struct Channel : Decodable {
public private(set) var channelTitle: String!
public private(set) var channelDescription: String!
public private(set) var id: String!
}
Class ChannelCell
import UIKit
class ChannelCell: UITableViewCell {
// Outlets
#IBOutlet weak var channelName: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if selected {
self.layer.backgroundColor = UIColor(white: 1, alpha: 0.2).cgColor
} else {
self.layer.backgroundColor = UIColor.clear.cgColor
}
}
func configureCell(channel: Channel) {
let title = channel.channelTitle ?? ""
channelName.text = "#\(title)"
}
}
Channel View Controller
import UIKit
class ChannelVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
// Outlets
#IBOutlet weak var loginBtn: UIButton!
#IBOutlet weak var userImg: CircleImage!
#IBOutlet weak var tableView: UITableView!
#IBAction func prepareForUnwind(segue: UIStoryboardSegue) {}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self <<<<<<<<< here I get the error message ***
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
tableView.dataSource = self
self.revealViewController()?.rearViewRevealWidth = self.view.frame.size.width - 60
NotificationCenter.default.addObserver(self, selector: #selector(ChannelVC.userDataDidChange(_:)), name: NOTIF_USER_DATA_DID_CHANGE, object: nil)
}
override func viewDidAppear(_ animated: Bool) {
setupUserInfo()
}
When I pressed the add channel button comes the problem
#IBAction func addChannelPressed(_ sender: Any) {
if AuthService.instance.isLoggedIn {
let addChannel = ChannelVC()
addChannel.modalPresentationStyle = .custom
present(addChannel, animated: true, completion: nil)
} else {
performSegue(withIdentifier: TO_LOGIN, sender: nil)
}
}
#IBAction func loginBtnPressed(_ sender: Any) {
if AuthService.instance.isLoggedIn {
let profile = ProfileVC()
profile.modalPresentationStyle = .custom
present(profile, animated: true, completion: nil)
} else {
performSegue(withIdentifier: TO_LOGIN, sender: nil)
}
}
#objc func userDataDidChange(_ notif: Notification) {
setupUserInfo()
}
func setupUserInfo() {
if AuthService.instance.isLoggedIn {
loginBtn.setTitle(UserDataService.instance.name, for: .normal)
userImg.image = UIImage(named: UserDataService.instance.avatarName)
userImg.backgroundColor = UserDataService.instance.returnUIColor(components: UserDataService.instance.avatarColor)
} else {
loginBtn.setTitle("Login", for: .normal)
userImg.image = UIImage(named: "menuProfileIcon")
userImg.backgroundColor = UIColor.clear
}
}
// Protocols for UITableViewDataSource
// # of sections
// # rows in the section
// function to setup the cells
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "channelCell", for: indexPath) as? ChannelCell {
I double check the reusable identifier is OK
let channel = MessageService.instance.channels[indexPath.row]
cell.configureCell(channel: channel)
return cell
} else {
return UITableViewCell()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if MessageService.instance.channels.count == 0 {
tableView.setEmptyView(title: "Message!", message: "You donĀ“t have any channel, create a new one")
}
return MessageService.instance.channels.count
}
}
this is the view I want to show when I click on the addChannel function
view to present
and this is the debug area
debug area
You are making a very common mistake. The line
let addChannel = ChannelVC()
creates a new instance of the controller which is not the instance in the storyboard. Therefore the outlets are not connected and the code crashes.
Replace it with (adjust the identifier accordingly)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let addChannel = storyboard.instantiateViewController(withIdentifier: "ChannelVC") as! ChannelVC
or create a segue.

condition on segue identifier in protocol

I have two view controllers 1st name is ViewController and 2nd Name is ContactVC. I have 3 buttons on 1st viewcontroller when i click on a button open 2nd viewController. In 2nd view controller i open phone contacts when i select any contact that contact name should be set as a button title. I have done with 1st button but from 2nd and 3rd button it does not works. Below is the code of 1st ViewController
import UIKit
import ContactsUI
class ViewController: UIViewController,CNContactPickerDelegate {
#IBOutlet weak var con1: UIButton!
#IBOutlet weak var con2: UIButton!
#IBOutlet weak var con3: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Contact1Segue"
{
(segue.destination as! ContactVC).delegate = self
}
else if segue.identifier == "Contact2Segue"
{
(segue.destination as! ContactVC).delegate = self
}
else if segue.identifier == "Contact3Segue"
{
(segue.destination as! ContactVC).delegate = self
}
}
func findContacts() -> [CNContact]
{
let store = CNContactStore()
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactImageDataKey,
CNContactPhoneNumbersKey] as [Any]
let fetchRequest = CNContactFetchRequest(keysToFetch: keysToFetch as! [CNKeyDescriptor])
var contacts = [CNContact]()
do {
try store.enumerateContacts(with: fetchRequest, usingBlock: { ( contact, stop) -> Void in
contacts.append(contact)
})
}
catch let error as NSError {
print(error.localizedDescription)
}
return contacts
}
func contactPickerDidCancel(picker: CNContactPickerViewController)
{
print("Cancel Contact Picker")
}
}
extension ViewController: ContactVCDelegate
{
func updateData(data: String)
{
self.con1.setTitle(data, for: .normal)
self.con2.setTitle(data, for: .normal)
self.con3.setTitle(data, for: .normal)
}
}
Below is the 2nd ViewController Code
import UIKit
import ContactsUI
class ContactVC: UIViewController, CNContactPickerDelegate, UITableViewDataSource, UITableViewDelegate {
var contacts = [CNContact]()
var Name:String?
var delegate: ContactVCDelegate?
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
DispatchQueue.global(qos: .background).async
{
let a = ViewController()
self.contacts = a.findContacts()
OperationQueue.main.addOperation
{
self.tableView!.reloadData()
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
print("Count:\(self.contacts.count)")
return self.contacts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0
{
let cell = tableView.dequeueReusableCell(withIdentifier: "SearchRID", for: indexPath)
return cell
}
else
{
let cell = tableView.dequeueReusableCell(withIdentifier: "CellRID", for: indexPath)
let contact = contacts[indexPath.row] as CNContact
cell.textLabel!.text = "\(contact.givenName) \(contact.familyName)"
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("section:\(indexPath.section), row:\(indexPath.row)")
let allcontact = self.contacts[indexPath.row] as CNContact
Name = allcontact.givenName + allcontact.familyName
self.delegate?.updateData(data: Name!)
print("Name:\(Name)")
_ = self.navigationController?.popViewController(animated: true)
dismiss(animated: true, completion: nil)
}
//MARK:- CNContactPickerDelegate Method
func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
contacts.forEach({contact in
for number in contact.phoneNumbers
{
let phonenum = number.value as CNPhoneNumber
print("NUmber is = \(phonenum)")
}
})
}
}
protocol ContactVCDelegate
{
func updateData(data: String)
}
Update your protocol:
protocol ContactVCDelegate
{
func updateData(buttonId:int, data: String)
}
Have a field in your second view controller with buttonId.
And set this value while preparing segue:
(segue.destination as! ContactVC).buttonId = 1
Your Update function:
func updateData(buttonId:int, data: String)
{
switch(buttonId){
case 1:
self.con1.setTitle(data, for: .normal)
break
case 2:
self.con2.setTitle(data, for: .normal)
break
case 3:
self.con3.setTitle(data, for: .normal)
break
}
}
In second view controller, onDidSelect:
self.delegate?.updateData(buttonId:buttonId,data: Name!)

Resources