Using a programmatic custom UILabel in a UITableView - ios

I'm attempting to use a custom UITableViewCell that I created programmatically in a UITableView.
The UITableViewCell class:
class PopulatedCellStyleTableViewCell: UITableViewCell {
private let userDisplayName: UILabel = {
let lbl = UILabel()
lbl.font = UIFont(name: "Baskerville-Bold", size: 16)
lbl.textColor = .purple
lbl.textAlignment = .left
lbl.backgroundColor = UIColor(displayP3Red: 1, green: 1, blue: 1, alpha: 0.5)
return lbl
}()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(userDisplayName)
//from a UIKit extension that allows me to easily add anchors
userDisplayName.anchor(top: self.topAnchor, left: self.leftAnchor, bottom: self.bottomAnchor, right: self.rightAnchor, paddingTop: 5, paddingLeft: 0, paddingBottom: 5, paddingRight: 0, width: self.frame.width, height: self.frame.height, enableInsets: false)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And the UITableViewController class:
class LoveListController: UITableViewController {
var loveList: [String]!
let populatedCell = "populatedCell"
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(PopulatedCellStyleTableViewCell.self, forCellReuseIdentifier: populatedCell)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return loveList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: populatedCell, for: indexPath)
// Configure the cell...
cell.textLabel?.text = loveList[indexPath.row]
return cell
}
}
Upon inspecting the debug view hierarchy, the labels load in, but the actual text is rendered in a UITableViewCellContentView instead of in my UILabel, and I have no idea why.
If this is easily solvable or has previously been discussed, please point me in the right direction and/or teach me how to discover the solution myself!

You need to cast the cell and use your custom userDisplayName not textLabel
let cell = tableView.dequeueReusableCell(withIdentifier: populatedCell, for: indexPath) as! PopulatedCellStyleTableViewCell
cell.userDisplayName.text = loveList[indexPath.row]
plus it's better to add the label to contentView and create the constraints with it
self.contentView.addSubview(userDisplayName)
self.userDisplayName.anchor(top: self.contentView.topAnchor .....

Related

Why doesn't my table view cell class instance run its initializer in my Swift code?

I have a custom subclass of UITableViewCell shown below
class GroupSelectionTableViewCell: UITableViewCell {
// MARK: - Properties
var groupSelectionLabel: UILabel = {
let label = UILabel()
label.textColor = .white
label.backgroundColor = .clear
label.font = UIFont.systemFont(ofSize: 18)
return label
}()
var groupSelectionImageView: UIImageView = {
let iv = UIImageView()
iv.backgroundColor = .clear
iv.setHeight(height: 25)
iv.setWidth(width: 25)
iv.contentMode = .scaleAspectFill
return iv
}()
// MARK: - LifeCycle
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
configureUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Helper Functions
private func configureUI() {
self.addSubview(groupSelectionLabel)
groupSelectionLabel.centerY(inView: self)
groupSelectionLabel.anchor(left: self.leftAnchor, paddingLeft: 12)
self.addSubview(groupSelectionImageView)
groupSelectionImageView.centerY(inView: self)
groupSelectionImageView.anchor(right: self.rightAnchor, paddingRight: 12)
self.backgroundColor = .black
self.selectionStyle = .none
}
}
and a custom subclass of UITableView shown below...
class GroupSelectionView: UITableView {
// MARK: - Properties
private let cellID = "GroupSelectionTableViewCell"
override init(frame: CGRect, style: UITableView.Style) {
super.init(frame: frame, style: style)
backgroundColor = .red
setHeight(height: 450)
setWidth(width: 300)
register(GroupSelectionTableViewCell.self, forCellReuseIdentifier: cellID)
rowHeight = 60
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension GroupSelectionView: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
return
}
}
extension GroupSelectionView: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
6
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! GroupSelectionTableViewCell
cell.groupSelectionLabel.text = "None"
cell.groupSelectionImageView.image = UIImage(named: "Tick")
return cell
}
}
But when I add my table view instance to a UIView() as a subview the table view cell class isn't running its initializer. Am I using the register method correctly? I have tried instantiating the table view subclass and putting a print statement in the initializer of the table view cell subclass but it doesn't get printed. Am I forgetting to set some property on the table view subclass? Do I need to use the Nib version of register instead? Any help would be greatly appreciated.
Forgot to set the dataSource and the delegate!

Display array of images on a certain amount of tableview cells

I have an UIImageView inside my table view cell, named pic. I want to use an array named colors (filled with UIImages), to display on 3 tableview cells.
I have the ViewController class and the tableview cell class listed below. The tableview displays the imageview pic. I assume you would place the color array in cellForRowAt method.
import UIKit
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
var colors:[UIImage] = [
UIImage(named: "blue")!,
UIImage(named: "red")!,
UIImage(named: "red")!
]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 118
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! customtv
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
class customtv: UITableViewCell {
lazy var backView : UIView = {
let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width , height: 110))
view.backgroundColor = .green
return view
}()
lazy var pic : UIImageview = {
let view = UIImageview(frame: CGRect(x: 100, y: 6, width: 100 , height: 100))
view.backgroundColor = .red
return view
}()
override func layoutSubviews() {
backView.clipsToBounds = true
backView.frame = CGRect(x: 0, y: 6, width: bounds.maxX , height: 110)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(animated, animated: true)
addSubview(backView)
addSubview(pic)
}
}
Try with this example below
import UIKit
class ViewController1: UIViewController {
var tableView: UITableView?
var colors:[UIImage] = [
UIImage(named: "blue")!,
UIImage(named: "red")!,
UIImage(named: "red")!
]
override func viewDidLoad() {
super.viewDidLoad()
loadUI()
}
func loadUI() {
tableView = UITableView()
self.view.addSubview(tableView.unsafelyUnwrapped)
tableView?.register(CustomTVC.self, forCellReuseIdentifier: "cell")
tableView?.delegate=self
tableView?.dataSource=self
tableView?.translatesAutoresizingMaskIntoConstraints = false
tableView?.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
tableView?.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
tableView?.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
tableView?.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
}
}
extension ViewController1: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 118
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTVC
cell.pic.image = colors[indexPath.row]
return cell
}
}
class CustomTVC: UITableViewCell {
lazy var backView : UIView = {
let view = UIView()
view.backgroundColor = .green
return view
}()
lazy var pic : UIImageView = {
let view = UIImageView()
view.backgroundColor = .red
return view
}()
override func awakeFromNib() {
super.awakeFromNib()
commonInit()
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override func layoutSubviews() {
backView.clipsToBounds = true
backView.frame = CGRect(x: 0, y: 6, width: bounds.maxX , height: 110)
}
func commonInit() {
contentView.addSubview(backView)
backView.translatesAutoresizingMaskIntoConstraints = false
backView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4).isActive = true
backView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 4).isActive = true
backView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -4).isActive = true
backView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -4).isActive = true
backView.addSubview(pic)
pic.translatesAutoresizingMaskIntoConstraints = false
pic.topAnchor.constraint(equalTo: backView.topAnchor, constant: 4).isActive = true
pic.leftAnchor.constraint(equalTo: backView.leftAnchor, constant: 4).isActive = true
pic.bottomAnchor.constraint(equalTo: backView.bottomAnchor, constant: -4).isActive = true
pic.widthAnchor.constraint(equalTo: pic.heightAnchor).isActive = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(animated, animated: true)
}
}

didEndEditingRowAt not called with custom tableview cell

I have a tableview (SettingsViewController) which I use as a user info view where user info is displayed (name, email, phone #, etc). This is similar to the standard iOS contact page.
Each cell has a textfield which is stretched accross the size of the cell, so that once in "edit" mode the user can update his/her info.
I also have a custom Cell (SettingsCell) which is where i setup the cell with the textfield etc.
SettingsViewController (excluded a lot of tabelview setup code):
class SettingsViewController: UITableViewController{
let cellId = "cellId"
var apiController: APIController?
var firstName: String?
var lastName: String?
var email: String?
var phoneNumber: String?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .mainWhite()
tableView = UITableView(frame: CGRect.zero, style: .grouped)
tableView.register(SettingsCell.self, forCellReuseIdentifier: cellId)
setupNavigation()
}
fileprivate func setupNavigation() {
editButtonItem.action = #selector(showEditing)
editButtonItem.title = "Edit"
editButtonItem.tintColor = .mainWhite()
self.navigationItem.rightBarButtonItem = editButtonItem
}
#objc func showEditing(sender: UIBarButtonItem)
{
if(self.tableView.isEditing == false)
{
self.tableView.isEditing = true
self.navigationItem.rightBarButtonItem?.title = "Save"
self.tableView.reloadData()
}
else
{
self.tableView.isEditing = false
self.navigationItem.rightBarButtonItem?.title = "Edit"
self.tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! SettingsCell
if self.tableView.isEditing == true {
cell.textField.isEnabled = true
if indexPath.section == 2 {
cell.textField.keyboardType = .phonePad
}
} else {
cell.textField.isEnabled = false
}
cell.selectionStyle = .none
filloutUserInfo(indexPath: indexPath, cell: cell)
return cell
}
override func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
return false
}
//THIS NEVER GETTING EXECUTED
override func tableView(_ tableView: UITableView, didEndEditingRowAt indexPath: IndexPath?) {
print("editing done for row \(indexPath?.item)")
}
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return .none
}
}
Settings Cell:
class SettingsCell: UITableViewCell, UITextFieldDelegate {
let textField: UITextField = {
let tf = UITextField()
tf.isEnabled = false
return tf
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
override func layoutSubviews() {
super.layoutSubviews()
addSubview(textField)
textField.anchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 8, paddingBottom: 0, paddingRight: 8, width: 0, height: 0)
textField.addDoneButtonOnKeyboard()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The problem I've now encountered is that after I have gone into edit mode, and changed the text of a given cell, the tableview doesn't actually recognize this. The didEndEditingRowAt never gets called and that print statement is never displayed. I am suspecting that is has something to do with the textfield not being connected to the tableviewcontroller in any way, but I'm not sure how to fix this.
I need to be able to know when a user has finished editing in order to display an alert for improper formatting and disabling the save button.
You need to implement a callback to listen the textField endEditing event from SettingsCell to your ViewController.
To achieve this, here is the updated SettingsCell
class SettingsCell: UITableViewCell, UITextFieldDelegate {
let textField: UITextField = {
let tf = UITextField()
tf.isEnabled = false
tf.addDoneButtonOnKeyboard()
return tf
}()
public var onEndEditing: ((String?) -> Void)?
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
override func layoutSubviews() {
super.layoutSubviews()
textField.removeFromSuperview()
addSubview(textField)
textField.delegate = self
textField.anchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 8, paddingBottom: 0, paddingRight: 8, width: 0, height: 0)
}
func textFieldDidEndEditing(_ textField: UITextField) {
self.onEndEditing?(textField.text)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Now update cellForRowAt to listen the endEditing event as below,
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! SettingsCell
if self.tableView.isEditing == true {
cell.textField.isEnabled = true
cell.onEndEditing = { text in
print("Cell Editing finished with text: \(text)")
}
if indexPath.section == 2 {
cell.textField.keyboardType = .phonePad
}
} else {
cell.textField.isEnabled = false
}
cell.selectionStyle = .none
filloutUserInfo(indexPath: indexPath, cell: cell)
return cell
}

Calling textViewDidBeginEditing when UITextView is Edited in Custom Cell

I can't seem to get my textViewDidBeginEditing function to call when my textview is being edited in my ViewController.
Here's the code for my custom cell:
class NoteDetailCell: UITableViewCell {
let noteText: UITextView = {
let label = UITextView()
label.font = UIFont(name: "Nunito",size: 16)
label.backgroundColor = UIColor(r: 244, g: 244, b: 255)
label.text = "Have to check out this food place. The best burgers in town."
label.textColor = UIColor(r: 88, g: 88, b: 88)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .`default`, reuseIdentifier: reuseIdentifier)
self.selectionStyle = UITableViewCellSelectionStyle.none
addSubview(noteText)
noteText.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
noteText.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 8).isActive = true
noteText.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -8).isActive = true
}
}
Here's my code for my tableview:
class TripNoteViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextViewDelegate {
var noteDetails: UITableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
noteDetails.frame = CGRect(x: 0, y: 65, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height-140)
noteDetails.delegate = self
noteDetails.dataSource = self
noteDetails.separatorColor = UIColor.clear
noteDetails.register(NoteDetailCell.self, forCellReuseIdentifier: "detailsCell")
noteDetails.register(NoteOriginCell.self, forCellReuseIdentifier: "visitTrip")
noteDetails.register(NoteActionsCell.self, forCellReuseIdentifier: "noteActions")
}
func textViewDidBeginEditing(noteText: UITextView) {
print("noteText text")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: detailsCell) as! NoteDetailCell
cell.noteText.isScrollEnabled = false
cell.noteText.delegate = self
return cell
}
}
I would like to call the textViewDidBeginEditing function from my ViewController when the textfield in my custom cell is being edited. Any suggestions on what I'm getting wrong?
You have not implemented the method defined by the UITextViewDelegate protocol. Change:
func textViewDidBeginEditing(noteText: UITextView) {
print("noteText text")
}
to:
func textViewDidBeginEditing(_ noteText: UITextView) {
print("noteText text")
}
Since you have the wrong signature, it's not called.

UITableViewCell behaves differently in iOS 11

I have done a UITableViewCell programmatically and it worked just fine with iOS 10. But after updating with iOS 11 and XCode 9, it behaves differently. The layout looks scrambled as below.
But if I tap on the cell then it rearranges and looks fine as below.
Here the code for UITableViewCell
import UIKit
import SnapKit
class AboutCell: UITableViewCell {
let roleLabel : UILabel = {
var tablelabel = UILabel()
tablelabel.font = UIFont (name: "Avenir Next Medium", size: 22)
tablelabel.textAlignment = .center
tablelabel.clipsToBounds = true
tablelabel.translatesAutoresizingMaskIntoConstraints = false
return tablelabel
}()
let nameLabel : UILabel = {
let tablelabel = UILabel()
tablelabel.font = UIFont (name: "Avenir Next Medium", size: 16)
tablelabel.textAlignment = .center
tablelabel.clipsToBounds = true
tablelabel.translatesAutoresizingMaskIntoConstraints = false
return tablelabel
}()
let webUrlLabel : UILabel = {
let tablelabel = UILabel()
tablelabel.font = UIFont (name: "Avenir Next Medium", size: 16)
tablelabel.textAlignment = .center
tablelabel.clipsToBounds = true
tablelabel.translatesAutoresizingMaskIntoConstraints = false
return tablelabel
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
override func layoutSubviews() {
super.layoutSubviews()
roleLabel.frame = CGRect(x: 0, y: 10, width: self.contentView.bounds.size.width-20, height: 25)
nameLabel.frame = CGRect(x: 0, y: roleLabel.frame.origin.y+25, width: self.bounds.size.width-20, height: 25)
webUrlLabel.frame = CGRect(x: 0, y: nameLabel.frame.origin.y+25, width: self.bounds.size.width-20, height: 25)
}
func setupViews(){
contentView.addSubview(roleLabel)
contentView.addSubview(nameLabel)
contentView.addSubview(webUrlLabel)
}
func setValuesForCell(contributor : Contributors){
roleLabel.text = contributor.contributorRole
nameLabel.text = contributor.contributorName
webUrlLabel.text = contributor.contributorWeb
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
and the extension I wrote for TableView delegate and datasource
extension AboutController : UITableViewDelegate, UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contributorList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : AboutCell = tableView.dequeueReusableCell(withIdentifier: self.cellid, for: indexPath) as! AboutCell
cell.selectionStyle = .default
let contributor : Contributors = contributorList[indexPath.row]
cell.setValuesForCell(contributor: contributor)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
tableView.deselectRow(at: indexPath, animated: true)
}
}
and the ViewDidLoad method
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 100.0
tableView.rowHeight = 100
tableView.register(AboutCell.self, forCellReuseIdentifier: self.cellid)
view.addSubview(tableView)
tableView.snp.makeConstraints { (make) in
make.top.right.bottom.left.equalToSuperview()
}
let mayu = Contributors(contibutorRole: "Developer", contributorName: "J Mayooresan", contributorWeb: "http://jaymayu.com")
let janie = Contributors(contibutorRole: "Voice Artist", contributorName: "M Jananie", contributorWeb: "http://jaymayu.com")
let arjun = Contributors(contibutorRole: "Aathichudi Content", contributorName: "Arjunkumar", contributorWeb: "http://laymansite.com")
let artist = Contributors(contibutorRole: "Auvaiyar Art", contributorName: "Alvin", contributorWeb: "https://www.fiverr.com/alvincadiz18")
contributorList = [mayu, arjun, janie, artist]
tableView.delegate = self
tableView.dataSource = self
self.tableView.reloadData()
}
Since you're laying out your tableView using autolayout, you also need to ensure translatesAutoresizingMaskIntoConstraints is set to false.
SnapKit should be setting the tableView's translatesAutoresizingMaskIntoConstraints to false for you already.
Since you're laying out the cells manually (using frames in layoutSubviews). Setting the cells subview's translatesAutoresizingMaskIntoConstraints to false is likely not needed.
See Apple Docs here for translatesAutoresizingMaskIntoConstraints

Resources