UITableViewCell delegate not working - ios

Conditions:
Swift 4, Xcode 9.3
Target: iOS 11.3
UI Done Programatically
Using Constraints
Episode is an object that holds the source as a String
Situation:
Here is my custom cell: EpisodeCell.swift
import UIKit
protocol EpisodeCellDelegate {
func didTapPlayButton(url: String)
}
class EpisodeCell: UITableViewCell {
var delegate: EpisodeCellDelegate?
var episode: Episode!
let cellView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.init(hex: "#EBE4D3")
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let episodeTitle: UILabel = {
let label = UILabel()
label.textColor = .darkGray
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let playButton: UIButton = {
let btn = UIButton.init(type: .custom)
btn.setTitle("PLAY", for: .normal)
btn.setTitleColor(.gray, for: .normal)
btn.isUserInteractionEnabled = true
btn.addTarget(self, action: #selector(playPressed), for: .touchUpInside)
return btn
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setup()
}
private func setup(){
self.accessoryType = .none
self.addSubview(cellView)
cellView.addSubview(episodeTitle)
cellView.addSubview(playButton)
cellView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
cellView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
cellView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
cellView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
episodeTitle.topAnchor.constraint(equalTo: cellView.topAnchor, constant: 10).isActive = true
episodeTitle.leadingAnchor.constraint(equalTo: cellView.leadingAnchor, constant: 10).isActive = true
episodeTitle.trailingAnchor.constraint(equalTo: cellView.trailingAnchor).isActive = true
episodeTitle.centerYAnchor.constraint(equalTo: cellView.centerYAnchor).isActive = true
playButton.translatesAutoresizingMaskIntoConstraints = false
playButton.trailingAnchor.constraint(equalTo: cellView.trailingAnchor, constant: -10).isActive = true
playButton.centerYAnchor.constraint(equalTo: cellView.centerYAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("initCoder has not been implemented")
}
#objc func playPressed() {
self.delegate?.didTapPlayButton(url: episode.source)
}
}
And here is how I implemented on my View Controller with the tableview: EpisodesViewController.swift
extension EpisodesViewController: EpisodeCellDelegate {
func didTapPlayButton(url: String) {
print("WOAH: \(url)")
}
}
extension EpisodesViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "episodeCell") as! EpisodeCell
cell.episode = series.episodes![indexPath.row]
cell.episodeTitle.text = ep.episodeName
cell.delegate = self
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (series.episodes?.count)!
}
}
Problem:
I'm having difficulty in working the button to be tapped in a custom table view cell. My tableview conforms to the protocol but it doesn't work.

You should do lazy initialisation for control inside the tableviewcell. Below code does the magic for me. Just change the below part of the code alone
lazy var cellView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var episodeTitle: UILabel = {
let label = UILabel()
label.textColor = .darkGray
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
lazy var playButton: UIButton = {
let btn = UIButton.init(type: .custom)
btn.setTitle("PLAY", for: .normal)
btn.setTitleColor(.gray, for: .normal)
btn.isUserInteractionEnabled = true
btn.addTarget(self, action: #selector(playPressed), for: .touchUpInside)
return btn
}()

You shouldn't have the action that a cell performs inside the cell. Cells can be reused, and a cell that was in row 1 can end up in row 4 at any time. Instead of your playPressed() routine, use the traditional didSelectRowAtIndexPath call, which uses the IndexPath, not the cell itself, to determine the action to take.

Related

How could UILabel always be nil -- Unexpectedly found nil while implicitly unwrapping an Optional value

As many people encountered, I tried to build tableView. I found many similar questions but it seems answers are not helping. I would be very grateful if anyone could help me. The problem I encountered:
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
This is a description Xcode gives me
Here's what I did:
(1) I connected Labels in the storyboard to the class it related to, which should be right as it's not hollow.
(2) I used tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath), and I tried to print cell I got, all cells aren't nil and belongs to CollegeTableViewCell, which is correct.
(3) I changed the identifier of tableViewCell to Cell which matches, and I changed it's class to CollegeTableViewCell too.
My program crashed directly when it executes following code. I only works when I make labels optional. So the problem is what did I do wrong so that labels in cell are always nil?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CollegeTableViewCell
let college = colleges[indexPath.row]
cell.collegeName.text = college.name // <-CRASH
cell.collegeGeo.text = college.city + ", " + college.state
return cell
}
Following is my CollegeTableViewCell class:
class CollegeTableViewCell: UITableViewCell {
#IBOutlet weak var collegeName: UILabel!
#IBOutlet weak var collegeGeo: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
EDIT: more codes related to this problem.
class CollegeChooseViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
var colleges = [CollegeInfo]()
let searchController = UISearchController(searchResultsController: nil)
let collegeApiUrl = "https://api.collegeai.com/v1/api/autocomplete/colleges?api_key=b47484dd6e228ea2cc5e1bf6ca&query="
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.register(CollegeTableViewCell.self, forCellReuseIdentifier: "Cell")
getColleges(contentInSearch: "MIT")
}
func getColleges(contentInSearch: String) {
guard let url = URL(string: (collegeApiUrl + contentInSearch)) else { return }
URLSession.shared.fetchData(for: url) {(result: Result<Initial, Error>) in
switch result {
case .success(let initial):
self.colleges = initial.collegeList
DispatchQueue.main.async {
self.tableView.reloadData()
}
case .failure(let error):
print("failed fetching college list from API: \(error)")
}
}
}
}
extension URLSession {
func fetchData<T: Decodable>(for url: URL, completion: #escaping (Result<T, Error>) -> Void) {
self.dataTask(with: url) { (data, response, error) in
if let error = error {
completion(.failure(error))
}
if let data = data {
do {
let object = try JSONDecoder().decode(T.self, from: data)
completion(.success(object))
} catch let decoderError {
completion(.failure(decoderError))
}
}
}.resume()
}
}
extension CollegeChooseViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CollegeTableViewCell
let college = colleges[indexPath.row]
cell.collegeName.text = college.name // <-CRASH
cell.collegeGeo.text = college.city + ", " + college.state
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(colleges.count)
return colleges.count
}
}
class CollegeTableViewCell: UITableViewCell {
#IBOutlet weak var collegeName: UILabel!
#IBOutlet weak var collegeGeo: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(true, animated: true)
}
}
This is a sample of your tableview programmatically way... If I don't know where your data comes from, I used a simulation of your arrays... conform your controller to UITableViewDelegate and Datasource:
class YourController: UIViewController, UITableViewDelegate, UITableViewDataSource
Now set tableView and constraints
var name = ["Mike", "Jhon", "Carl", "Steve", "Elon", "Bill", "Bruce"] // simulation of your array
var city = ["Milano", "New Yor", "Paris", "Los Angeles", "Madrid", "Amsterdam", "Tokyo"] // simulation of your array
var state = ["Italia", "USA", "France", "USA", "Spain", "Holland", "Japan"] // simulation of your array
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .darkBlue
tableView.backgroundColor = .white
tableView.register(CollegeTableViewCell.self, forCellReuseIdentifier: "cellId") // register cell
tableView.delegate = self
tableView.dataSource = self
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.separatorColor = .lightGray
view.addSubview(tableView)
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
// this is my extension to configure navigation bar, you can configure it as you want
configureNavigationBar(largeTitleColor: .red, backgoundColor: .black, tintColor: .red, title: "Sample", preferredLargeTitle: true)
}
After that set your tableView Delegate and DataSource:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
// Mark: - set number of rows with your array.count
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return name.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let name = name[indexPath.row]
let city = city[indexPath.row]
let state = state[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! CollegeTableViewCell
cell.collegeName.text = name
cell.collegeGeo.text = "\(city), \(state)"
return cell
}
This is how your cell look like:
class CollegeTableViewCell: UITableViewCell {
let collegeName: UILabel = {
let label = UILabel()
label.textColor = .white
label.font = .systemFont(ofSize: 16, weight: .semibold)
label.backgroundColor = .clear
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let collegeGeo: UILabel = {
let label = UILabel()
label.textColor = .white
label.font = .systemFont(ofSize: 14, weight: .semibold)
label.backgroundColor = .clear
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.backgroundColor = .ultraDark
let stackView = UIStackView(arrangedSubviews: [collegeName, collegeGeo]) // use stack view for automatic table view dimension
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.spacing = 2
stackView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20).isActive = true
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20).isActive = true
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
this is the result:
EDIT based on new information full code and Json decoder:
struct CollegeInfo: Decodable {
let collegeList: [MyDataResults]
}
struct MyDataResults: Decodable {
let id: String
let name: String
let city: String
let state: String
}
class tableController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var myData = [MyDataResults]() // simulation of your array
let urlString = "https://api.collegeai.com/v1/api/autocomplete/colleges?api_key=b47484dd6e228ea2cc5e1bf6ca&query="
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .darkBlue
tableView.backgroundColor = .white
tableView.register(CollegeTableViewCell.self, forCellReuseIdentifier: "cellId") // register cell
tableView.delegate = self
tableView.dataSource = self
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.separatorColor = .lightGray
view.addSubview(tableView)
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
// this is my extension to configure navigation bar, you can configure it as you want
configureNavigationBar(largeTitleColor: .red, backgoundColor: .black, tintColor: .red, title: "Sample", preferredLargeTitle: true)
fetchJson { [weak self] (res) in
switch res {
case .success(let dataResults):
dataResults.forEach { (dataresult) in
self?.myData.removeAll()
DispatchQueue.main.asyncAfter(deadline: .now() + 0) {
self?.myData = dataresult.collegeList
self?.tableView.reloadData()
}
}
case .failure(let err):
print("Failed to fetch json", err)
}
}
}
fileprivate func fetchJson(completion: #escaping (Result<[CollegeInfo], Error >) -> ()) {
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { data, resp, err in
if let err = err {
completion(.failure(err))
return
}
do {
guard let data = data else { return }
let results = try JSONDecoder().decode(CollegeInfo.self, from: data)
//succesful
completion(.success([results]))
} catch let jsonErr {
completion(.failure(jsonErr))
}
}.resume()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
// Mark: - set number of rows with your array.count
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myResults = myData[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! CollegeTableViewCell
cell.collegeName.text = myResults.name
cell.collegeGeo.text = "\(myResults.city), \(myResults.state)"
return cell
}
The cell:
class CollegeTableViewCell: UITableViewCell {
let collegeName: UILabel = {
let label = UILabel()
label.textColor = .white
label.font = .systemFont(ofSize: 16, weight: .semibold)
label.backgroundColor = .clear
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let collegeGeo: UILabel = {
let label = UILabel()
label.textColor = .white
label.font = .systemFont(ofSize: 14, weight: .semibold)
label.backgroundColor = .clear
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.backgroundColor = .ultraDark
let stackView = UIStackView(arrangedSubviews: [collegeName, collegeGeo]) // use stack view for automatic table view dimension
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.spacing = 2
stackView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20).isActive = true
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20).isActive = true
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The result:
you're using the wrong bundle name for dequeueReusableCell.
instead of cell use CollegeTableViewCell
it should be :
let cell = tableView.dequeueReusableCell(withIdentifier: "CollegeTableViewCell", for: indexPath) as! CollegeTableViewCell

UITableView Custom Cell Height Not Working

I'm learning how to use UIKit programmatically, and I created a custom UITableViewCell, but my row height isn't registering.
I keep getting this error
[Warning] Warning once only: Detected a case where constraints ambiguously
suggest a height of zero for a table view cell's content view.
We're considering the collapse unintentional and using standard height instead.
I can't seem to understand/find where the issue with my code is, even after googling the error.
ViewController:
import UIKit
class ViewController: UIViewController {
let contacts = ContactAPI.getContacts()
let tableView = UITableView()
var safeArea: UILayoutGuide!
//let contentView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
safeArea = view.layoutMarginsGuide
setUpTable()
setUpNavigation()
}
func setUpTable(){
view.addSubview(tableView)
//populate with data
tableView.dataSource = self
tableView.register(TableViewCell.self, forCellReuseIdentifier: "cell")
//turn off autoresizing
tableView.translatesAutoresizingMaskIntoConstraints = false
//Layout Configs
tableView.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
func setUpNavigation(){
navigationItem.title = "Contacts"
self.navigationController?.navigationBar.barTintColor = .gray
self.navigationController?.navigationBar.isTranslucent = false
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
}
}
extension ViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contacts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
//cell.textLabel?.text = contacts[indexPath.row].name
cell.contact = contacts[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
}
}
CustomCell:
class TableViewCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?){
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(profileImage)
subView.addSubview(nameLabel)
subView.addSubview(jobTitleLabel)
self.contentView.addSubview(subView)
self.contentView.addSubview(flagImage)
profileImage.centerYAnchor.constraint(equalTo:self.contentView.centerYAnchor).isActive = true
profileImage.leadingAnchor.constraint(equalTo:self.contentView.leadingAnchor, constant:10).isActive = true
profileImage.widthAnchor.constraint(equalToConstant:70).isActive = true
profileImage.heightAnchor.constraint(equalToConstant:70).isActive = true
subView.centerYAnchor.constraint(equalTo:self.contentView.centerYAnchor).isActive = true
subView.leadingAnchor.constraint(equalTo:self.profileImage.trailingAnchor, constant:10).isActive = true
subView.trailingAnchor.constraint(equalTo:self.contentView.trailingAnchor, constant:-10).isActive = true
subView.heightAnchor.constraint(equalToConstant:40).isActive = true
nameLabel.topAnchor.constraint(equalTo:self.subView.topAnchor).isActive = true
nameLabel.leadingAnchor.constraint(equalTo:self.subView.leadingAnchor).isActive = true
nameLabel.trailingAnchor.constraint(equalTo:self.subView.trailingAnchor).isActive = true
jobTitleLabel.topAnchor.constraint(equalTo:self.nameLabel.bottomAnchor).isActive = true
jobTitleLabel.leadingAnchor.constraint(equalTo:self.subView.leadingAnchor).isActive = true
jobTitleLabel.topAnchor.constraint(equalTo:self.nameLabel.bottomAnchor).isActive = true
jobTitleLabel.leadingAnchor.constraint(equalTo:self.subView.leadingAnchor).isActive = true
flagImage.widthAnchor.constraint(equalToConstant:26).isActive = true
flagImage.heightAnchor.constraint(equalToConstant:26).isActive = true
flagImage.trailingAnchor.constraint(equalTo:self.contentView.trailingAnchor, constant:-20).isActive = true
flagImage.centerYAnchor.constraint(equalTo:self.contentView.centerYAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
}
//Profile Image
let profileImage: UIImageView = {
let img = UIImageView()
img.contentMode = .scaleAspectFill
img.translatesAutoresizingMaskIntoConstraints = false
img.layer.cornerRadius = 35
img.clipsToBounds = true
return img
}()
//Name
let nameLabel: UILabel = {
let label = UILabel()
label.font = UIFont.boldSystemFont(ofSize: 20)
label.textColor = .gray
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
//Job Title
let jobTitleLabel: UILabel = {
let label = UILabel()
label.font = UIFont.boldSystemFont(ofSize: 14)
label.textColor = .white
label.backgroundColor = .gray
label.layer.cornerRadius = 5
label.clipsToBounds = true
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
//Container for Name and Job Title
let subView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.clipsToBounds = true
return view
}()
//Country Flag
let flagImage: UIImageView = {
let img = UIImageView()
img.contentMode = .scaleAspectFill
img.translatesAutoresizingMaskIntoConstraints = false
img.layer.cornerRadius = 13
img.clipsToBounds = true
return img
}()
var contact:Contact? {
didSet{
guard let contactItem = contact else {return}
if let name = contactItem.name{
profileImage.image = UIImage(named: name)
nameLabel.text = name
}
if let jobTitle = contactItem.jobTitle{
jobTitleLabel.text = "\(jobTitle)"
}
if let country = contactItem.country{
flagImage.image = UIImage(named: country)
}
}
}
}
You haven't implemented the UITableViewDelegate
In your setupTable() add the line
tableView.delegate = self
Then change your extension from
extension ViewController: UITableViewDataSource {
to extension ViewController: UITableViewDataSource, UITableViewDelegate {

Swift - Adding target to UIButton in UITableView not working

I have a UITableView, and in each of the UITableViewCells, I have a button, which should perform an action when selected. If the user decides the select the actual cell, I handle another action within the didSelectRowAt function. I am not sure if I am missing something, but my button does not work when selected, but performs the didSelectRowAt function.
The layout and constraints work just fine. It is just not recognizing the tap, and then pushes to the didSelectRowAt
UIViewController Code (Clipped)
class MyProfile: UIViewController, UITableViewDelegate, UITableViewDataSource {
let tableView : UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.separatorStyle = .none
tableView.backgroundColor = UIColor.white
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
tableView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 10).isActive = true
tableView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 10).isActive = true
view.backgroundColor = UIColor.white
tableView.delegate = self
tableView.dataSource = self
tableView.register(ProfileCell.self, forCellReuseIdentifier: "profileCell")
// data and backend stuff to reload tableview has been removed, so there is not too much code
// Do any additional setup after loading the view.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.myServices.text = "My Services"
return myJobs.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "profileCell", for: indexPath) as! ProfileCell
cell.selectionStyle = .none
cell.deleteButton.addTarget(self, action: #selector(deletePostPressed), for: .touchUpInside)
cell.editButton.addTarget(self, action: #selector(editPostPressed), for: .touchUpInside)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.navigationController?.pushViewController(ViewPostController(), animated: true)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 175
}
#objc func deletePostPressed() {
print("delete")
}
#objc func editPostPressed() {
print("edit")
self.present(Home(), animated: true, completion: nil)
}
}
UITableViewCell Code (Clipped)
class ProfileCell: UITableViewCell {
let editButton : UIButton = {
let button = UIButton(type: .system)
button.setTitle("Edit", for: .normal)
button.setTitleColor(UIColor.mainBlue, for: .normal)
button.backgroundColor = UIColor(red: 240/255, green: 240/255, blue: 240/255, alpha: 1)
button.isUserInteractionEnabled = true
button.translatesAutoresizingMaskIntoConstraints = false
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.mainBlue.cgColor
return button
}()
let deleteButton : UIButton = {
let button = UIButton(type: .system)
button.setTitle("Delete", for: .normal)
button.setTitleColor(UIColor.mainBlue, for: .normal)
button.backgroundColor = UIColor(red: 240/255, green: 240/255, blue: 240/255, alpha: 1)
button.isUserInteractionEnabled = true
button.translatesAutoresizingMaskIntoConstraints = false
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.mainBlue.cgColor
return button
}()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.selectionStyle = .none
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
isUserInteractionEnabled = true
addSubview(editButton)
editButton.layer.cornerRadius = 15
editButton.layer.masksToBounds = true
editButton.topAnchor.constraint(equalTo: saleNumber.bottomAnchor, constant: 7).isActive = true
editButton.rightAnchor.constraint(equalTo: informationView.rightAnchor, constant: -15).isActive = true
editButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
editButton.widthAnchor.constraint(equalToConstant: 85).isActive = true
addSubview(deleteButton)
deleteButton.layer.cornerRadius = 15
deleteButton.layer.masksToBounds = true
deleteButton.topAnchor.constraint(equalTo: saleNumber.bottomAnchor, constant: 7).isActive = true
deleteButton.rightAnchor.constraint(equalTo: editButton.leftAnchor, constant: -15).isActive = true
deleteButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
deleteButton.widthAnchor.constraint(equalToConstant: 85).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I am not sure what else could be the problem. I have declared delegates, corrected everything, and made sure everything else that needed to be initialized was, and still it doesn't work.
It's a matter of hierarchy, you need to add the buttons to the contentView. If you just add a subview to the view, it goes behind the contentView of the UITableViewCell.
Visually, where the selected one is the contentView of the cell, you can see that the actual button is behind it:
So contentView.addSubview(editButton) and contentView.addSubview(deleteButton) does the job.

How to pressed a UIButton within a customized TableviewCell?

I have been looking all throughout SO on how to interact with a UIButton within a customized tableview cell. All of the answers I have seen are using IBOutlets, however I have not seen a way to do this fully programmatically. I am use to interacting with buttons via button.addTarget. Here are my two ViewControllers, one being the customized tableviewcell and the other being the ViewController.
Here is my customized. I tried using a protocol delegate route, however this has failed.
import UIKit
#objc protocol TableViewNew {
func onClickCell()
}
class NewMoveTableViewCell: UITableViewCell {
var cellDelegate: TableViewNew?
static let identifier = "NewTableViewCell"
private let myImageView: UIImageView = {
let imageView = UIImageView()
imageView.clipsToBounds = true
imageView.contentMode = .scaleAspectFill
imageView.layer.masksToBounds = true
imageView.backgroundColor = .purple
imageView.layer.cornerRadius = 80/2
return imageView
}()
private let myLabel : UILabel = {
let label = UILabel()
label.text = "test"
label.backgroundColor = .blue
label.textColor = .systemPink
label.adjustsFontSizeToFitWidth = true
label.textAlignment = .center
return label
}()
private let button: UIButton = {
let button = UIButton()
button.setTitle("Invite", for: .normal)
button.backgroundColor = .systemPink
button.layer.cornerRadius = 10
return button
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(myImageView)
addSubview(myLabel)
addSubview(button)
setImageConstratins()
setTitleLabelConstraints()
setButton()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setImageConstratins() {
myImageView.translatesAutoresizingMaskIntoConstraints = false
myImageView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
myImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12).isActive = true
myImageView.heightAnchor.constraint(equalToConstant: 80).isActive = true
myImageView.widthAnchor.constraint(equalToConstant: 80).isActive = true
}
func setTitleLabelConstraints() {
myLabel.translatesAutoresizingMaskIntoConstraints = false
myLabel.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
myLabel.leadingAnchor.constraint(equalTo: myImageView.trailingAnchor, constant: 5).isActive = true
myLabel.heightAnchor.constraint(equalToConstant: 80).isActive = true
//myLabel.trailingAnchor.constraint(equalTo: button.leadingAnchor, constant: -12).isActive = true
}
func setButton() {
button.translatesAutoresizingMaskIntoConstraints = false
button.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
button.leadingAnchor.constraint(equalTo: myLabel.trailingAnchor, constant: -5).isActive = true
button.heightAnchor.constraint(equalToConstant: 80).isActive = true
button.widthAnchor.constraint(equalToConstant: 150).isActive = true
button.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12).isActive = true
}
public func configure(with name: String, label: String) {
myLabel.text = label
myImageView.image = UIImage(named: name)
}
override func prepareForReuse() {
super.prepareForReuse()
myLabel.text = nil
myImageView.image = nil
}
#objc func didTapButton(_ sender: Any) {
cellDelegate?.onClickCell()
}
}
Secondly, here is the ViewController that the TableView is within.
import UIKit
class NewMoveViewController: UIViewController {
private let tableView: UITableView = {
let tableView = UITableView()
tableView.rowHeight = 100
return tableView
}()
private var collectionView: UICollectionView?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = CGSize(width: 50, height: 50)
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView?.register(NewMoveCollectionViewCell.self, forCellWithReuseIdentifier: NewMoveCollectionViewCell.identifier)
collectionView?.showsHorizontalScrollIndicator = false
title = "Add to Group"
tableView.register(NewMoveTableViewCell.self, forCellReuseIdentifier: NewMoveTableViewCell.identifier)
tableView.delegate = self
tableView.dataSource = self
collectionView?.backgroundColor = .systemBlue
collectionView?.dataSource = self
collectionView?.delegate = self
guard let myCollection = collectionView else {
return
}
view.addSubview(myCollection)
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView?.frame = CGRect(x: 0, y: 100, width: view.frame.size.width, height: 50)
tableView.frame = CGRect(x: 0, y: 200, width: view.frame.size.width, height: view.frame.size.height)
}
}
extension NewMoveViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: NewMoveTableViewCell.identifier, for: indexPath) as! NewMoveTableViewCell
cell.cellDelegate = self
cell.configure(with: "", label: "test")
return cell
}
func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
return false
}
}
extension NewMoveViewController : UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NewMoveCollectionViewCell.identifier, for: indexPath) as! NewMoveCollectionViewCell
return cell
}
}
extension NewMoveViewController : TableViewNew {
func onClickCell() {
print("Pressed")
}
I conformed this ViewController to the protocol from the customized cell and put the function within the cell's cellForRowAt function. When I use this route, I run my app and everything comes up fine, however when I try to click on the customized tableviewcell within the viewcontroller, nothing happens. Any help would be greatly appreciated.
you need to add the action handler to your button:
button.addTarget(self, action: #selector(didTapButton(sender:)), for: .touchUpInside)

How to put a floating action button in a tableView in swift in iOS?

I am trying to use an floating action button in iOS to impose on a table view so that I can add items in the tableview with that . please help me with the code.
Here is the complete code for it. It has been done without using storyboard.
TableView:
import UIKit
class ViewController: UIViewController, UITableViewDataSource {
let nameArray = ["India","Usa","UK"]
let tableView: UITableView = {
let table = UITableView()
table.translatesAutoresizingMaskIntoConstraints = false
return table
}()
let btnFloating : UIButton = {
let floating = UIButton()
floating.translatesAutoresizingMaskIntoConstraints = false
floating .backgroundColor = .cyan
floating.setTitle("ADD", for: .normal)
return floating
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
tableView.addSubview(btnFloating)
tableView.dataSource = self
setuoConstrain()
//Set the action of add button
btnFloating.addTarget(self, action: #selector(btnAddTapp(sender:)), for: .touchUpInside)
}
func setuoConstrain(){
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
//Constrain For Button :
btnFloating.heightAnchor.constraint(equalToConstant: 64).isActive = true
btnFloating.widthAnchor.constraint(equalToConstant: 64).isActive = true
btnFloating.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -24).isActive = true
btnFloating.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -36).isActive = true
}
//This function is for add button . What action you want , can put inside this function
#objc func btnAddTapp(sender: UIButton){
print("add button tapp")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return nameArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let nameCell = NameTableCell(style: .default, reuseIdentifier: "NameTableCell")
nameCell.lblName.text = nameArray[indexPath.row]
return nameCell
}
}
TableViewCell:
import UIKit
class NameTableCell: UITableViewCell {
let lblName: UILabel = {
let name = UILabel()
name.translatesAutoresizingMaskIntoConstraints = false
return name
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.addSubview(lblName)
constrain()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func constrain(){
lblName.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
}
}
func setupFloatingActionButton() {
Floaty.global.button.buttonImage = UIImage(named: "icon-social")
Floaty.global.button.buttonColor = UIColor.white
let facebookItem = FloatyItem()
facebookItem.icon = UIImage(named: "icon-facebook")
facebookItem.title = "Facebook"
Floaty.global.button.addItem(item: facebookItem)
let gmailItem = FloatyItem()
Floaty.global.button.addItem("Gmail", icon: UIImage(named: "icon-gmail"), handler: {_
in
print("Gmail Button tapp")
})
let twitterItem = FloatyItem()
Floaty.global.button.addItem("Twitter", icon: UIImage(named: "icon-twitter"), handler: {_ in
print("twitter Button tapp")
})
//Floaty.global.button.animationSpeed = 0.50
Floaty.global.button.openAnimationType = .fade
//Floaty.global.button.rotationDegrees = 90.00
Floaty.global.show()
}

Resources