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 {
Related
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
I am new to swift . I am creating table view cell programatically with the sub title . I defined the two label and two function to get the value form API and display it into label control but the problem is the values of label are displayed into correct position . I want to display the title and then below the title is sub title values . Here is the screen shot .
Here is the code table view cell.
extension ViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.rovers.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: StoryCell.identifier , for: indexPath) as? StoryCell
else{ return UITableViewCell()}
let row = indexPath.row
let title = viewModel.getTitle(by: row)
cell.configureCell(title:title)
let Id = viewModel.getId(by: row)
cell.configureCell(Id: Id)
return cell
}
}
Here is code for configure the label .
import UIKit
class StoryCell: UITableViewCell {
static let identifier = "StoryCell"
private lazy var storyTitleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textAlignment = .left
return label
}()
private lazy var storyIdLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textAlignment = .left
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setUpUI()
setUpUIID()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureCell(title: String) {
storyTitleLabel.text = "Status :\(title)"
}
func configureCell(Id: Int) {
storyIdLabel.text = "Id: \(String(Id))"
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
private func setUpUI() {
contentView.addSubview(storyTitleLabel)
// constraints
let safeArea = contentView.safeAreaLayoutGuide
storyTitleLabel.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
storyTitleLabel.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor).isActive = true
storyTitleLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor).isActive = true
storyTitleLabel.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor).isActive = true
}
private func setUpUIID() {
contentView.addSubview(storyIdLabel)
// constraints
let safeArea = contentView.safeAreaLayoutGuide
storyIdLabel.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
storyIdLabel.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor).isActive = true
storyIdLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor).isActive = true
storyIdLabel.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor).isActive = true
}
}
I made some changes in your code check below. Now it should be fine.
private func setUpUI() {
contentView.addSubview(storyTitleLabel)
// constraints
let safeArea = contentView.safeAreaLayoutGuide
storyTitleLabel.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
storyTitleLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor).isActive = true
storyTitleLabel.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor).isActive = true
}
private func setUpUIID() {
contentView.addSubview(storyIdLabel)
let safeArea = contentView.safeAreaLayoutGuide
storyIdLabel.topAnchor.constraint(equalTo: storyTitleLabel.topAnchor).constant = 5
storyIdLabel.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor).isActive = true
storyIdLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor).isActive = true
storyIdLabel.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor).isActive = true
}
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)
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()
}
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.