Looping through phone numbers, creating a custom view and passing it the phone number in swift

350 Views Asked by At

I'm having trouble creating a view programatically inside a for loop from another controller. The parent controller is a tableviewcell and I'm looping through a bunch of phone numbers inside a CNContact object. For each phone number the contact has I wish to create my custom view and add it to the tableviewcell and have it stack vertically.

So far I managed to create the view and add it to the tableviewcell but wasn't able to pass the data. It's the passing of the data from one controller to another that I'm struggling with.

Here is my code:

ContactListTableViewCell.swift

import UIKit
import Contacts

class ContactListTableViewCell: UITableViewCell {

    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var phonenumberView: UIView!

    func configureCell(contact: CNContact) {
        titleLabel.text = "\(contact.givenName) \(contact.familyName)"

        for phoneNumber in contact.phoneNumbers {

            let view = self.createContactListTableViewTelephoneRow(telephone: phoneNumber)
            self.phonenumberView.addSubview(view)


        }


    }

    func createContactListTableViewTelephoneRow(telephone: Any) -> UIView {
        let controller = ContactListTableViewTelephoneRow()
        let view = UINib(nibName: "ContactListTableViewTelephoneRow", bundle: nil).instantiate(withOwner: controller, options: nil)[0] as! UIView
        return view
    }

}

contactListTableViewCell prototype inside Main.storyboard enter image description here

ContactListTableViewTelephoneRow.swift

class ContactListTableViewTelephoneRow: UIView {

    @IBOutlet var view: UIView!
    @IBOutlet weak var telephoneLabel: UILabel!
    @IBOutlet weak var telephoneTypeLabel: UILabel!

    func setData(telephoneLabelText: String, telephoneTypeLabelText: String) {
        telephoneLabel?.text = telephoneLabelText
        telephoneTypeLabel?.text = telephoneTypeLabelText
    }


}

ContactListTableViewTelephoneRow.xib

enter image description here

Any help would be much appreciated. Thank you.

2

There are 2 best solutions below

0
On

Simple way to pass data is you need to crate object in your second controller and pass data from first controller

let vc = self.storyboard!.instantiateViewController(withIdentifier: "Secondcontroller") as! Secondcontroller
    vc.yourObject = object //To pass
    self.present(tabvc, animated: true, completion: nil) // or push
0
On

You will need to cast the view you create using UNib.[...] and pass the data directly to it:

func createContactListTableViewTelephoneRow(telephone: CNLabeledValue<CNPhoneNumber>) -> UIView {
    let nib = UINib(nibName: "ContactListTableViewTelephoneRow", bundle: nil)
    let root = nib.instantiate(withOwner: nil, options: nil)[0]
    let view = root as! ContactListTableViewTelephoneRow

    view.setData(telephoneLabelText: telephone.value.stringValue,
                 telephoneTypeLabelText: telephone.label!) // make sure `telephone.label!` is correct – I never compiled it

    return view
}

Note that I adjusted the signature of createContactListTableViewTelephoneRow(telephone:).

But as an advise overall: I would solve your UI problem in a very different way.

Background: UITableViews heavily reuses (queues/dequeues) cells so that scroll performance is acceptable. Although I assume you use the APIs of UITableViewDataSource correctly loading nibs inside the your cells can become a performance bottleneck very fast.

I would advise against having variable number of ContactListTableViewTelephoneRow in your cell. Instead make it a subclass of UITableViewCell as well. Your view controller of course must handle at least two different types of cells in this case. You can use different sections to still keep the logic fairly easy. Here is a full example: (you would of course need to adjust styling)

import Contacts
import UIKit

class ContactListTelephoneTableViewCell: UITableViewCell {

    @IBOutlet weak var telephoneLabel: UILabel!
    @IBOutlet weak var telephoneTypeLabel: UILabel!

    func configureCell(telephone: CNLabeledValue<CNPhoneNumber>) {
        telephoneLabel.text = telephone.value.stringValue
        telephoneTypeLabel.text = telephone.label!
    }
}

class ContactListTableViewCell: UITableViewCell {

    @IBOutlet weak var titleLabel: UILabel!

    func configureCell(contact: CNContact) {
        titleLabel.text = "\(contact.givenName) \(contact.familyName)"
    }
}


class DataSource: NSObject, UITableViewDataSource {

    var contacts: [CNContact]!

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return contacts[section].phoneNumbers.count + 1 // one extra for given and family name
    }

      func numberOfSections(in tableView: UITableView) -> Int {
        return contacts.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if indexPath.row == 0 {
            return self.tableView(tableView, nameCellForRowAt: indexPath)
        } else {
            return self.tableView(tableView, phoneCellForRowAt: indexPath)
        }
    }

    func tableView(_ tableView: UITableView, nameCellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "name", for: indexPath) as! ContactListTableViewCell
        cell.configureCell(contact: contacts[indexPath.section])
        return cell
    }

    func tableView(_ tableView: UITableView, phoneCellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "phone", for: indexPath) as! ContactListTelephoneTableViewCell

        let contact = contacts[indexPath.section]
        let telephone = contact.phoneNumbers[indexPath.row - 1] // minus one for given and family name
        cell.configureCell(telephone: telephone)

        return cell
    }
}