UITableViewCell subclass displaying itself twice on top of itself - swift

Excuse the terrible title. The best way to explain this is to show it visually.
So I have a custom UITableViewCell, and when the view controller (with the table view) is loaded, the cell ends up looking like this:
and I have no idea why. It DOES fix itself if I tap on it, as shown here:
I’ve also noticed that this only happens when the cell is not immediately present on screen when the view controller loads (aka, you have to scroll down to it). If it was the first cell in the table view, it appears correctly.
code:
class CustomTableViewCell: UITableViewCell {
let label1 = UILabel()
let label2 = UILabel()
let label3 = UILabel()
let label4 = UILabel()
let label5 = UILabel()
let label6 = UILabel()
let label7 = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
label1.translatesAutoresizingMaskIntoConstraints = false
label1.text = "Test"
label1.font = UIFont.systemFont(ofSize: 20, weight: .bold)
addSubview(label1)
label2.translatesAutoresizingMaskIntoConstraints = false
label2.text = "Test"
label2.textColor = .secondaryLabel
label2.font = UIFont.systemFont(ofSize: 13)
addSubview(label2)
label3.translatesAutoresizingMaskIntoConstraints = false
label3.text = "Test"
label3.font = UIFont.systemFont(ofSize: 17)
addSubview(label3)
label4.translatesAutoresizingMaskIntoConstraints = false
label4.text = "Test"
label4.textColor = .secondaryLabel
label4.font = UIFont.systemFont(ofSize: 13)
addSubview(label4)
label5.translatesAutoresizingMaskIntoConstraints = false
label5.text = "Test"
label5.font = UIFont.systemFont(ofSize: 17)
addSubview(label5)
label6.translatesAutoresizingMaskIntoConstraints = false
label6.text = "Test"
label6.textColor = .secondaryLabel
label6.font = UIFont.systemFont(ofSize: 13)
addSubview(label6)
label7.translatesAutoresizingMaskIntoConstraints = false
label7.text = "Test"
label7.font = UIFont.systemFont(ofSize: 17)
addSubview(label7)
NSLayoutConstraint.activate([
label1.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 20),
label1.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 16),
label1.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -16),
label2.topAnchor.constraint(equalTo: label1.bottomAnchor, constant: 20),
label2.leadingAnchor.constraint(equalTo: label1.leadingAnchor),
label2.trailingAnchor.constraint(equalTo: label1.trailingAnchor),
label3.topAnchor.constraint(equalTo: label2.bottomAnchor, constant: 3),
label3.leadingAnchor.constraint(equalTo: label2.leadingAnchor),
label3.trailingAnchor.constraint(equalTo: label2.trailingAnchor),
label4.topAnchor.constraint(equalTo: label3.bottomAnchor, constant: 20),
label4.leadingAnchor.constraint(equalTo: label3.leadingAnchor),
label4.trailingAnchor.constraint(equalTo: label3.trailingAnchor),
label5.topAnchor.constraint(equalTo: label4.bottomAnchor, constant: 3),
label5.leadingAnchor.constraint(equalTo: label4.leadingAnchor),
label5.trailingAnchor.constraint(equalTo: label4.trailingAnchor),
label6.topAnchor.constraint(equalTo: label5.bottomAnchor, constant: 20),
label6.leadingAnchor.constraint(equalTo: label5.leadingAnchor),
label6.trailingAnchor.constraint(equalTo: label5.trailingAnchor),
label7.topAnchor.constraint(equalTo: label6.bottomAnchor, constant: 3),
label7.leadingAnchor.constraint(equalTo: label6.leadingAnchor),
label7.trailingAnchor.constraint(equalTo: label6.trailingAnchor),
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
--
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
return cell
}

My dear friend, I can not see any bottomAnchor constraints :). Why don't you check this out. Maybe it helps you to understand how autolayout works in programmatically. If you have any question about it, please reach out. Have fun :)
PS: You should addsubview into contentView. Do not add any view directly to cell superview. :)
class TestTableViewCell: UITableViewCell {
private lazy var labelFirst: UILabel = {
let temp = UILabel()
temp.translatesAutoresizingMaskIntoConstraints = false
temp.font = UIFont(name: "System", size: 12)
temp.text = "Test"
return temp
}()
private lazy var labelSecond: UILabel = {
let temp = UILabel()
temp.translatesAutoresizingMaskIntoConstraints = false
temp.font = UIFont(name: "System", size: 15)
temp.text = "Test"
return temp
}()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
addMajorViews()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func addMajorViews() {
contentView.addSubview(labelFirst)
contentView.addSubview(labelSecond)
NSLayoutConstraint.activate([
labelFirst.topAnchor.constraint(equalTo: contentView.topAnchor),
labelFirst.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
labelFirst.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
labelSecond.topAnchor.constraint(equalTo: labelFirst.bottomAnchor),
labelSecond.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
labelSecond.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
labelSecond.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])
}
}

Related

change height of TableViewHeader and included UILabel after button click

When I want to set a new long status by pressing the button, my profileStatusLabel height and TableViewHeader height as well don't change.
P.S. sorry about my English, if there are some mistakes
**ProfileHeaderView: UIView
**
import UIKit
...
private lazy var profileStatusLabel: UILabel = {
let profileStatusLabel = UILabel()
profileStatusLabel.numberOfLines = 0
profileStatusLabel.text = "Looking for a big, young, good looking, able to cook female gorilla"
profileStatusLabel.textColor = .gray
profileStatusLabel.font = profileNameLabel.font.withSize(14)
profileStatusLabel.textAlignment = .left
profileStatusLabel.sizeToFit()
profileStatusLabel.translatesAutoresizingMaskIntoConstraints = false
return profileStatusLabel
}()
private lazy var setStatusButton: UIButton = {
let setStatusButton = UIButton()
setStatusButton.backgroundColor = .systemBlue
setStatusButton.layer.cornerRadius = 4
setStatusButton.layer.shadowOffset = CGSize(width: 4, height: 4)
setStatusButton.layer.shadowOpacity = 0.7
setStatusButton.layer.shadowRadius = 4
setStatusButton.layer.shadowColor = UIColor.black.cgColor
setStatusButton.setTitle("Set status", for: .normal)
setStatusButton.setTitleColor(.white, for: .normal)
setStatusButton.titleLabel?.font = setStatusButton.titleLabel?.font.withSize(14)
setStatusButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
setStatusButton.translatesAutoresizingMaskIntoConstraints = false
return setStatusButton
}()
private lazy var statusText: String = {
return statusText
}()
private func setupView() {
addSubview(profileImageView)
addSubview(profileNameLabel)
addSubview(profileStatusLabel)
addSubview(statusTextField)
addSubview(setStatusButton)
}
private func setupConstraints() {
NSLayoutConstraint.activate([
profileNameLabel.topAnchor.constraint(equalTo: topAnchor, constant: 27),
profileNameLabel.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 20),
profileNameLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
profileNameLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 30),
profileStatusLabel.topAnchor.constraint(equalTo: profileNameLabel.bottomAnchor, constant: 10),
profileStatusLabel.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 20),
profileStatusLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
profileStatusLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 49),
statusTextField.topAnchor.constraint(equalTo: profileStatusLabel.bottomAnchor, constant: 10),
statusTextField.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 20),
statusTextField.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
statusTextField.heightAnchor.constraint(equalToConstant: 40),
setStatusButton.topAnchor.constraint(equalTo: statusTextField.bottomAnchor, constant: 16),
setStatusButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
setStatusButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
setStatusButton.heightAnchor.constraint(equalToConstant: 50),
bottomAnchor.constraint(equalTo: setStatusButton.bottomAnchor, constant: 16),
])
}
#objc private func statusTextChanged(_ textField: UITextField) {
statusText = statusTextField.text!
}
#objc private func buttonAction() {
guard statusTextField.text != nil else {
print("Text the status before press the button")
return
}
profileStatusLabel.text = statusText
profileStatusLabel.updateConstraintsIfNeeded()
self.setNeedsUpdateConstraints()
self.layoutIfNeeded()
}
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .systemGray4
setupView()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
**MyCustomHeader: UITableViewHeaderFooterView
**
import UIKit
class MyCustomHeader: UITableViewHeaderFooterView {
var profileHeaderView = ProfileHeaderView()
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
configureContents()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configureContents() {
contentView.addSubview(profileHeaderView)
profileHeaderView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
contentView.leadingAnchor.constraint(equalTo: profileHeaderView.leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: profileHeaderView.trailingAnchor),
contentView.widthAnchor.constraint(equalTo: profileHeaderView.widthAnchor),
contentView.heightAnchor.constraint(equalTo: profileHeaderView.heightAnchor),
contentView.topAnchor.constraint(equalTo: profileHeaderView.topAnchor),
])
}
}
**ProfileViewController
**
import UIKit
class ProfileViewController: UIViewController, UIGestureRecognizerDelegate {
let postList = [robberyPost, eatingPost, elephantPost, camelPost]
var profileTableView = UITableView()
let myCustomHeader = MyCustomHeader()
let headerID = "headerId"
let headerID2 = "headerId2"
let cellID = "cellId"
let collectionCellID = "collectionCellId"
...
func setupTableView() {
profileTableView.contentInsetAdjustmentBehavior = .never
profileTableView.register(PhotosTableViewCell.self, forCellReuseIdentifier: PhotosTableViewCell.cellID)
profileTableView.register(PostTableViewCell.self, forCellReuseIdentifier: PostTableViewCell.cellID)
profileTableView.delegate = self
profileTableView.dataSource = self
profileTableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(profileTableView)
}
func setupMyCustomHeader() {
profileTableView.register(MyCustomHeader.self, forHeaderFooterViewReuseIdentifier: headerID)
}
private func setupConstraints() {
NSLayoutConstraint.activate([
profileTableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
profileTableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
profileTableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
profileTableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])
}
...
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemGray6
setupTableView()
setupMyCustomHeader()
setupConstraints()
}
}
extension ProfileViewController: UITableViewDelegate, UITableViewDataSource {
// MARK: - Table view data source
...
// MARK: - Table view delegate
...
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if #available(iOS 15, *) {
tableView.sectionHeaderTopPadding = 0
}
if section == 0 {
return UITableView.automaticDimension
} else {
return 50
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerID) as! MyCustomHeader
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(profileImageViewClicked(_ :)))
tapRecognizer.numberOfTapsRequired = 1
tapRecognizer.numberOfTouchesRequired = 1
tapRecognizer.delegate = self
header.profileHeaderView.profileImageView.isUserInteractionEnabled = true
header.profileHeaderView.profileImageView.addGestureRecognizer(tapRecognizer)
return header
} else {
let header2 = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerID2) as! OneMoreCustomHeader
return header2
}
}
...
}
Probably I should use updateConstraints() function, but can't find the right way.
I added this code to buttonAction, buy it doesn't work
profileStatusLabel.updateConstraintsIfNeeded()
self.setNeedsUpdateConstraints()
self.layoutIfNeeded()
Any time you change the height of a table view element - cell, section header or footer view, etc - you must tell the table view to re-layout its elements.
This is commonly done with a closure.
For example, you would add this property to your custom header view class:
// closure so table layout can be updated
var contentChanged: (() -> ())?
and then set that closure in viewForHeaderInSection:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerID) as! MyCustomHeader
// set the closure
header.contentChanged = {
// tell the table view to re-layout itself so the header height can be changed
tableView.performBatchUpdates(nil)
}
return header
} else {
let header2 = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerID2) as! OneMoreCustomHeader
return header2
}
}
and when you tap the "Set status" button to update the profileStatusLabel, you would use the closure to "call back" to the controller:
contentChanged?()
With the code you posted, you are embedding ProfileHeaderView in MyCustomHeader, which complicates things a little because you will need a closure in MyCustomHeader that works with another closure in ProfileHeaderView.
There really is no need to do that -- you can put all of your UI elements directly in MyCustomHeader to avoid that issue.
Here is a complete, runnable example. I changed your MyCustomHeader as described... as well as added some other code that you will probably end up needing (see the comments):
class MyCustomHeader: UITableViewHeaderFooterView {
// closure to inform the controller the status text changed
// so we can update the data and
// so the table layout can be updated
var contentChanged: ((String) -> ())?
// presumably, we'll be setting the statusText and the name from a dataSource
public var statusText: String = "" {
didSet {
profileStatusLabel.text = statusText
}
}
public var name: String = "" {
didSet {
profileNameLabel.text = name
}
}
private lazy var profileImageView: UIImageView = {
let v = UIImageView()
if let img = UIImage(systemName: "person.crop.circle") {
v.image = img
}
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
private lazy var profileNameLabel: UILabel = {
let profileStatusLabel = UILabel()
profileStatusLabel.numberOfLines = 0
profileStatusLabel.text = ""
profileStatusLabel.textColor = .black
profileStatusLabel.textAlignment = .left
profileStatusLabel.translatesAutoresizingMaskIntoConstraints = false
return profileStatusLabel
}()
private lazy var profileStatusLabel: UILabel = {
let profileStatusLabel = UILabel()
profileStatusLabel.numberOfLines = 0
profileStatusLabel.text = ""
profileStatusLabel.textColor = .gray
profileStatusLabel.textAlignment = .left
profileStatusLabel.translatesAutoresizingMaskIntoConstraints = false
return profileStatusLabel
}()
private lazy var setStatusButton: UIButton = {
let setStatusButton = UIButton()
setStatusButton.backgroundColor = .systemBlue
setStatusButton.layer.cornerRadius = 4
setStatusButton.layer.shadowOffset = CGSize(width: 4, height: 4)
setStatusButton.layer.shadowOpacity = 0.7
setStatusButton.layer.shadowRadius = 4
setStatusButton.layer.shadowColor = UIColor.black.cgColor
setStatusButton.setTitle("Set status", for: .normal)
setStatusButton.setTitleColor(.white, for: .normal)
setStatusButton.setTitleColor(.lightGray, for: .highlighted)
setStatusButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
setStatusButton.translatesAutoresizingMaskIntoConstraints = false
return setStatusButton
}()
private lazy var statusTextField: UITextField = {
let statusTextField = UITextField()
statusTextField.text = ""
statusTextField.borderStyle = .roundedRect
statusTextField.backgroundColor = .white
statusTextField.translatesAutoresizingMaskIntoConstraints = false
return statusTextField
}()
private func setupView() {
contentView.addSubview(profileImageView)
contentView.addSubview(profileNameLabel)
contentView.addSubview(profileStatusLabel)
contentView.addSubview(statusTextField)
contentView.addSubview(setStatusButton)
}
private func setupConstraints() {
NSLayoutConstraint.activate([
profileImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
profileImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
profileImageView.widthAnchor.constraint(equalToConstant: 160.0),
profileImageView.heightAnchor.constraint(equalTo: profileImageView.widthAnchor),
profileNameLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 27),
profileNameLabel.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 20),
profileNameLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
profileNameLabel.heightAnchor.constraint(equalToConstant: 30),
profileStatusLabel.topAnchor.constraint(equalTo: profileNameLabel.bottomAnchor, constant: 10),
profileStatusLabel.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 20),
profileStatusLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
profileStatusLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 49),
statusTextField.topAnchor.constraint(equalTo: profileStatusLabel.bottomAnchor, constant: 10),
statusTextField.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 20),
statusTextField.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
statusTextField.heightAnchor.constraint(equalToConstant: 40),
setStatusButton.topAnchor.constraint(equalTo: statusTextField.bottomAnchor, constant: 16),
setStatusButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
setStatusButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
setStatusButton.heightAnchor.constraint(equalToConstant: 50),
contentView.bottomAnchor.constraint(equalTo: setStatusButton.bottomAnchor, constant: 16),
])
}
#objc private func buttonAction() {
guard let stText = statusTextField.text else {
print("Text the status before press the button")
return
}
statusTextField.resignFirstResponder()
// update statusText property
// which will also set the text in the label
statusText = stText
// call the closure, passing back the new text
contentChanged?(stText)
}
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
contentView.backgroundColor = .systemGray4
setupView()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class ProfileViewController: UIViewController, UIGestureRecognizerDelegate {
// presumably, this will be loaded from saved data, along with the rest of the table data
var dataSourceStatusText: String = "Looking for a big, young, good looking, able to cook female gorilla"
var profileTableView = UITableView()
let headerID = "headerId"
let cellID = "cellId"
func setupTableView() {
profileTableView.contentInsetAdjustmentBehavior = .never
profileTableView.register(UITableViewCell.self, forCellReuseIdentifier: cellID)
profileTableView.delegate = self
profileTableView.dataSource = self
profileTableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(profileTableView)
}
func setupMyCustomHeader() {
profileTableView.register(MyCustomHeader.self, forHeaderFooterViewReuseIdentifier: headerID)
}
private func setupConstraints() {
NSLayoutConstraint.activate([
profileTableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
profileTableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
profileTableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
profileTableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemGray6
setupTableView()
setupMyCustomHeader()
setupConstraints()
}
}
extension ProfileViewController: UITableViewDelegate, UITableViewDataSource {
// let's use 10 sections each with 5 rows so we can scroll the header out-of-view
func numberOfSections(in tableView: UITableView) -> Int {
return 10
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)
c.textLabel?.text = "\(indexPath)"
return c
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if #available(iOS 15, *) {
tableView.sectionHeaderTopPadding = 0
}
if section == 0 {
return UITableView.automaticDimension
} else {
return 50
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerID) as! MyCustomHeader
header.name = "#KillaGorilla"
header.statusText = dataSourceStatusText
// set the closure
header.contentChanged = { [weak self] newStatus in
guard let self = self else { return }
// update the status text (probably also saving it somewhere?)
// if we don't do this, and the section header scrolls out of view,
// the *original* status text will be shown
self.dataSourceStatusText = newStatus
// tell the table view to re-layout itself so the header height can be changed
tableView.performBatchUpdates(nil)
}
return header
} else {
// this would be your other section header view
// for now, let's just use a label
let v = UILabel()
v.backgroundColor = .yellow
v.text = "Section Header: \(section)"
return v
}
}
}
Give that a try.

Simulate a cell like in between two cells and below two cells

I am using tableview and collection view inside to create this screen.
If you look closer in the design..i will have to create an effect of cells going above below or top cell with a corner radius...which simulates like the cells are below and in between two cells...
The same goes for the top area which has a corner radius.
Please guide through a proper way to design this entire screen.
(I am using storyboard and UIKit only...i dont want to use swiftUI)
Various ways to approach this -- here's one...
Four cell types:
"Top" cell
"Categories" cell
"Top Products" cell
"Suggestions" cell
In each cell class, add a UIView with rounded corners - this will hold the "content" of each cell.
Behind the rounded corners view, add a "Top Color" UIView and a a "Bottom Color" UIView.
It looks like this:
When running, we get this result:
Here is some sample code:
class MyBaseCell: UITableViewCell {
var topBotColors: [UIColor] = [.white, .white] {
didSet {
topColorView.backgroundColor = topBotColors[0]
botColorView.backgroundColor = topBotColors[1]
}
}
let theStack: UIStackView = {
let v = UIStackView()
v.axis = .vertical
v.spacing = 12
return v
}()
let topColorView = UIView()
let botColorView = UIView()
let roundedCornerView = UIView()
var topConstraint: NSLayoutConstraint!
var botConstraint: NSLayoutConstraint!
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
[topColorView, botColorView, roundedCornerView].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(v)
}
theStack.translatesAutoresizingMaskIntoConstraints = false
roundedCornerView.addSubview(theStack)
let g = contentView.layoutMarginsGuide
topConstraint = theStack.topAnchor.constraint(equalTo: roundedCornerView.topAnchor, constant: 12.0)
botConstraint = theStack.bottomAnchor.constraint(equalTo: roundedCornerView.bottomAnchor, constant: -12.0)
NSLayoutConstraint.activate([
topColorView.topAnchor.constraint(equalTo: contentView.topAnchor),
topColorView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
topColorView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
botColorView.topAnchor.constraint(equalTo: topColorView.bottomAnchor),
botColorView.heightAnchor.constraint(equalTo: topColorView.heightAnchor),
botColorView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
botColorView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
botColorView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
roundedCornerView.topAnchor.constraint(equalTo: g.topAnchor),
roundedCornerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
roundedCornerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
roundedCornerView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
theStack.leadingAnchor.constraint(equalTo: roundedCornerView.leadingAnchor),
theStack.trailingAnchor.constraint(equalTo: roundedCornerView.trailingAnchor),
topConstraint, botConstraint,
])
self.backgroundColor = .clear
contentView.backgroundColor = .clear
}
}
class TopCell: MyBaseCell {
override func commonInit() {
super.commonInit()
// let's add 1 tall label
let v = UILabel()
v.textAlignment = .center
v.text = "Top Cell"
v.textColor = .white
v.font = .systemFont(ofSize: 24.0, weight: .bold)
theStack.addArrangedSubview(v)
// avoid auot-layout complaints
let c = v.heightAnchor.constraint(equalToConstant: 80.0)
c.priority = .required - 1
c.isActive = true
}
}
class CatsCell: MyBaseCell {
override func commonInit() {
super.commonInit()
// let's add a few labels
for i in 1...4 {
let v = UILabel()
v.textAlignment = .center
v.text = "Categories Label \(i)"
theStack.addArrangedSubview(v)
}
roundedCornerView.backgroundColor = .white
roundedCornerView.layer.cornerRadius = 24
}
}
class TopProdsCell: MyBaseCell {
override func commonInit() {
super.commonInit()
// let's add a few labels
for i in 1...3 {
let v = UILabel()
v.textAlignment = .center
v.text = "Top Prods Label \(i)"
v.textColor = .white
theStack.addArrangedSubview(v)
}
roundedCornerView.backgroundColor = .clear
// increase top/bottom "spacing"
topConstraint.constant = 24.0
botConstraint.constant = -24.0
}
}
class SuggestionsCell: MyBaseCell {
override func commonInit() {
super.commonInit()
// let's add a few labels
for i in 1...6 {
let v = UILabel()
v.textAlignment = .center
v.text = "Suggested Label \(i)"
theStack.addArrangedSubview(v)
}
roundedCornerView.backgroundColor = .white
roundedCornerView.layer.cornerRadius = 24
}
}
class SampleTableVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
let tableView = UITableView()
let myGray: UIColor = .gray
let myBlue: UIColor = UIColor(red: 0.25, green: 0.30, blue: 0.65, alpha: 1.0)
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
tableView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
tableView.register(TopCell.self, forCellReuseIdentifier: "topCell")
tableView.register(CatsCell.self, forCellReuseIdentifier: "catCell")
tableView.register(TopProdsCell.self, forCellReuseIdentifier: "prodCell")
tableView.register(SuggestionsCell.self, forCellReuseIdentifier: "suggCell")
tableView.dataSource = self
tableView.delegate = self
let bkv = UIView()
bkv.backgroundColor = myGray
tableView.backgroundView = bkv
tableView.separatorStyle = .none
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let c = tableView.dequeueReusableCell(withIdentifier: "topCell", for: indexPath) as! TopCell
c.topBotColors = [myGray, myGray]
return c
}
if indexPath.row == 1 {
let c = tableView.dequeueReusableCell(withIdentifier: "catCell", for: indexPath) as! CatsCell
c.topBotColors = [myGray, myBlue]
return c
}
if indexPath.row == 2 {
let c = tableView.dequeueReusableCell(withIdentifier: "prodCell", for: indexPath) as! TopProdsCell
c.topBotColors = [myBlue, myBlue]
return c
}
let c = tableView.dequeueReusableCell(withIdentifier: "suggCell", for: indexPath) as! SuggestionsCell
c.topBotColors = [myBlue, myGray]
return c
}
}

Creating custom table View with Cell properties programmatically

I am new to swift . I am trying to create table view and cell programmatically. I want to display the three label properties one below to another . I added the content view with respective label properties . I have set the row height but when I run the app is overlapping.
Here is the my Table view code with view controller .
class RoomViewController: UIViewController {
var coordinator: RoomBaseCoordinator?
init(coordinator: RoomBaseCoordinator) {
super.init(nibName: nil, bundle: nil)
self.coordinator = coordinator
title = "People"
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private let roomviewModel = RoomViewModel()
private var subscribers = Set<AnyCancellable>()
var activityIndicator = UIActivityIndicatorView(style: .medium)
private lazy var tableView: UITableView = {
let tableview = UITableView()
tableview.translatesAutoresizingMaskIntoConstraints = false
tableview.dataSource = self
tableview.prefetchDataSource = self
tableview.showsVerticalScrollIndicator = false
tableview.register(RoomCellTableViewCell.self, forCellReuseIdentifier: RoomCellTableViewCell.identifier)
return tableview
}()
override func viewDidLoad() {
super.viewDidLoad()
activityIndicator.startAnimating()
setUpUI()
setUpBinding()
self.activityIndicator.stopAnimating()
}
private func setUpUI() {
view.backgroundColor = .white
title = "Room List "
view.addSubview(tableView)
tableView.rowHeight = 100
// create constraints
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
tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
// Creating constrain for Indecator
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(activityIndicator)
activityIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
activityIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
private func setUpBinding() {
roomviewModel
.$rooms
.receive(on : RunLoop.main)
.sink { [weak self ] _ in
self?.tableView.reloadData()
}
.store(in: &subscribers)
roomviewModel.getRoom()
}
}
extension RoomViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return roomviewModel.rooms.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: RoomCellTableViewCell.identifier, for: indexPath) as? RoomCellTableViewCell
else { return UITableViewCell() }
let row = indexPath.row
let room = roomviewModel.rooms[row]
cell.configureCell(createdAt: room.createdAt, IsOccupied: room.isOccupied, maxOccupancy: String(room.maxOccupancy), id: room.id)
return cell
}
}
extension RoomViewController: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
roomviewModel.getRoom()
}
}
Here is the cell .
class RoomCellTableViewCell: UITableViewCell {
static let identifier = "RoomCell"
private lazy var createdAtLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.boldSystemFont(ofSize: 15)
label.numberOfLines = 0
label.textAlignment = .left
return label
}()
private lazy var IsOccupiedLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.boldSystemFont(ofSize: 15)
label.numberOfLines = 0
//label.backgroundColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)
label.textAlignment = .left
return label
}()
private lazy var maxOccupancyLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.boldSystemFont(ofSize: 15)
label.numberOfLines = 0
label.textAlignment = .left
return label
}()
private lazy var idLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.boldSystemFont(ofSize: 15)
label.numberOfLines = 0
label.textAlignment = .left
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// self.contentView.addSubview(containerView)
contentView.addSubview(createdAtLabel)
contentView.addSubview(IsOccupiedLabel)
contentView.addSubview(maxOccupancyLabel)
createdAtLabel.centerYAnchor.constraint(equalTo:self.contentView.centerYAnchor).isActive = true
createdAtLabel.leadingAnchor.constraint(equalTo:self.contentView.leadingAnchor, constant:10).isActive = true
createdAtLabel.widthAnchor.constraint(equalToConstant:50).isActive = true
createdAtLabel.heightAnchor.constraint(equalToConstant:50).isActive = true
contentView.centerYAnchor.constraint(equalTo:self.contentView.centerYAnchor).isActive = true
contentView.leadingAnchor.constraint(equalTo:self.createdAtLabel.trailingAnchor, constant:10).isActive = true
contentView.trailingAnchor.constraint(equalTo:self.contentView.trailingAnchor, constant:-10).isActive = true
contentView.heightAnchor.constraint(equalToConstant:50).isActive = true
IsOccupiedLabel.topAnchor.constraint(equalTo:self.contentView.topAnchor).isActive = true
IsOccupiedLabel.leadingAnchor.constraint(equalTo:self.contentView.leadingAnchor).isActive = true
IsOccupiedLabel.trailingAnchor.constraint(equalTo:self.contentView.trailingAnchor).isActive = true
maxOccupancyLabel.topAnchor.constraint(equalTo:self.IsOccupiedLabel.bottomAnchor).isActive = true
maxOccupancyLabel.leadingAnchor.constraint(equalTo:self.contentView.leadingAnchor).isActive = true
maxOccupancyLabel.topAnchor.constraint(equalTo:self.IsOccupiedLabel.bottomAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureCell(createdAt: String, IsOccupied: Bool, maxOccupancy: String, id : String ) {
createdAtLabel.text = "CreatedAt :\(createdAt)"
IsOccupiedLabel.text = "IsOccupied : \(IsOccupied)"
maxOccupancyLabel.text = "MaxOccupancy : \(maxOccupancy)"
idLabel.text = "Id : \(id)"
}
}
Here is the result .
In setUpUI() method of your RoomViewController class, set tableView's rowHeight to UITableView.automaticDimension and estimatedRowHeight to 100.
import UIKit
class RoomViewController: UIViewController {
private func setUpUI() {
view.backgroundColor = .white
title = "Room List "
view.addSubview(tableView)
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableView.automaticDimension
// create constraints
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
tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
// Creating constrain for Indecator
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(activityIndicator)
activityIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
activityIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
And add UIStackView in the RoomCellTableViewCell class as bellow:
import UIKit
class RoomCellTableViewCell: UITableViewCell {
private var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
stackView.alignment = .fill
stackView.spacing = 8
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(stackView)
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: topAnchor),
stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
stackView.leftAnchor.constraint(equalTo: leftAnchor),
stackView.rightAnchor.constraint(equalTo: rightAnchor)
])
stackView.addArrangedSubview(createdAtLabel)
stackView.addArrangedSubview(isOccupiedLabel)
stackView.addArrangedSubview(maxOccupancyLabel)
// stackView.addArrangedSubview(idLabel)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

fetch data from Firebase using autoID - Xcode

On the top level of my app I have a tableViewController where you can create new jobs that are saved to my firebase database using an autoID.
Here is the JSON
{
"jobInfo" : {
"-L59sEGslWF7HFza26ay" : {
"FPS" : "25",
"director" : "Mike & Jim",
"jobBrand" : "Honda",
"jobName" : "Dreammakers"
},
"-L59sWGEccWMFEeFWyNU" : {
"FPS" : "25",
"director" : "Anthony Test",
"jobBrand" : "WWF",
"jobName" : "Eye"
}
}
}
This is the code that links you to the UITabBarController:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let jobInfo = page_jobTabController()
show(jobInfo, sender: self)
}
You can then click on one of the cells which takes you to a UItabBarViewController that has 4 tabs. In the first tab I have several textFields that lists information about the job.
I am struggling to work out how to send the autoID from the UITableViewController through the UITabBarController to the UIViewController so the textFields can be filled in with the correct information.
Here is a screenshot of the two pages to get an idea of what I want to do.
Sorry to dump all this code below - I am not trying to spam.
This is my dictionary:
class Job: NSObject {
var id: String?
var jobBrand: String?
var jobName : String?
var directorName : String?
var FPSvalue : String?
init(dictionary: [String: AnyObject]) {
self.id = dictionary["id"] as? String
self.jobBrand = dictionary["jobBrand"] as? String
self.jobName = dictionary["jobName"] as? String
self.directorName = dictionary["directorName"] as? String
self.FPSvalue = dictionary["FPSvalue"] as? String
}
}
This is my jobList page (top level)
class page_jobList: UITableViewController {
let cellId = "cellId"
var jobs = [Job]()
override func viewDidLoad() {
super.viewDidLoad()
// NAVIGATION ITEM
navigationItem.title = "Jobs"
navigationController?.navigationBar.prefersLargeTitles = true
tableView.register(JobCell.self, forCellReuseIdentifier: cellId)
// FIREBASE FETCH JOBS
fetchJobs()
}
// FETCH JOBS
func fetchJobs() {
Database.database().reference().child("jobInfo").observe(.childAdded) { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
print (snapshot)
// PROCESSES VALUES RECEIVED FROM SERVER
if ( snapshot.value is NSNull ) {
// DATA WAS NOT FOUND
print("– – – Data was not found – – –")
} else {
let job = Job(dictionary: dictionary)
job.id = snapshot.key
self.jobs.append(job)
//this will crash because of background thread, use dispatch_async to fix
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
}
}
}
// AFTER DATA IS FETCHED AND ADD IT TO TABLE VIEW
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return jobs.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let job = jobs[indexPath.row]
cell.accessoryType = UITableViewCellAccessoryType.disclosureIndicator
cell.textLabel?.text = job.jobBrand
cell.detailTextLabel?.text = job.jobName
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 72
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let jobInfo = page_jobTabController()
jobInfo.job? = jobs[indexPath.row]
show(jobInfo, sender: self)
}
}
class JobCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?){
super .init(style: .subtitle, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implented")
}
}
This is my tabBarController:
class page_jobTabController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
// NAVIGATION ITEM
let jobInfo = page_jobInformation()
jobInfo.job? = job!
let shots = page_shotList()
let attachments = page_attachments()
let notes = page_notesList()
jobInfo.tabBarItem.title = "Information"
jobInfo.tabBarItem.image = UIImage(named: "jobInfo")
shots.tabBarItem.title = "Shots"
shots.tabBarItem.image = UIImage(named: "shots")
attachments.tabBarItem.title = "Attachments"
attachments.tabBarItem.image = UIImage(named: "attachments")
notes.tabBarItem.title = "Notes"
notes.tabBarItem.image = UIImage(named: "notes")
viewControllers = [jobInfo, shots, attachments, notes]
}
#IBAction func HandleEdit(sender : UIButton) {
let transition = CATransition()
transition.duration = 0.5
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromRight
transition.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut)
view.window!.layer.add(transition, forKey: kCATransition)
}
}
This is my jobInfo tab ViewController
class page_jobInformation: UIViewController{
// CONTENT CREATION
// GENERAL
let generalTitle: UILabel = {
let generalTitle = UILabel()
generalTitle.font = UIFont(name: "HelveticaNeue", size: 12.0)
generalTitle.text = "GENERAL"
generalTitle.translatesAutoresizingMaskIntoConstraints = false
return generalTitle
}()
let jobBrand: UITextField = {
let jobBrand = UITextField()
//jobBrand.text = "jobBrand"
jobBrand.isUserInteractionEnabled = false
jobBrand.keyboardType = UIKeyboardType.default
jobBrand.translatesAutoresizingMaskIntoConstraints = false
return jobBrand
}()
let jobName: UITextField = {
let jobName = UITextField()
jobName.keyboardType = UIKeyboardType.default
jobName.text = "jobName"
jobName.isUserInteractionEnabled = false
jobName.translatesAutoresizingMaskIntoConstraints = false
return jobName
}()
let directorName: UITextField = {
let directorName = UITextField()
directorName.text = "directorName"
directorName.keyboardType = UIKeyboardType.default
directorName.isUserInteractionEnabled = false
directorName.translatesAutoresizingMaskIntoConstraints = false
return directorName
}()
let AgencyName: UITextField = {
let AgencyName = UITextField()
AgencyName.text = "Agency"
AgencyName.keyboardType = UIKeyboardType.default
AgencyName.isUserInteractionEnabled = false
AgencyName.translatesAutoresizingMaskIntoConstraints = false
return AgencyName
}()
let prodCoName: UITextField = {
let prodCoName = UITextField()
prodCoName.text = "Production Company"
prodCoName.keyboardType = UIKeyboardType.default
prodCoName.isUserInteractionEnabled = false
prodCoName.translatesAutoresizingMaskIntoConstraints = false
return prodCoName
}()
// TECHNICAL
let technicalTitle: UILabel = {
let technicalTitle = UILabel()
technicalTitle.font = UIFont(name: "HelveticaNeue", size: 12.0)
technicalTitle.text = "TECHNICAL"
technicalTitle.translatesAutoresizingMaskIntoConstraints = false
return technicalTitle
}()
lazy var jobSpecsButton: UIButton = {
let jobSpecsButton = UIButton(type: .system)
jobSpecsButton.setTitle("Job Specifications", for: .normal)
jobSpecsButton.titleLabel?.font = UIFont(name: "HelveticaNeue", size: 14.0)
jobSpecsButton.layer.masksToBounds = true
jobSpecsButton.contentHorizontalAlignment = .left
jobSpecsButton.setTitleColor(UIColor.gray, for: UIControlState.normal)
jobSpecsButton.backgroundColor = UIColor(red:1.00, green:1.00, blue:1.00, alpha:1.0)
jobSpecsButton.setTitleColor(.black, for: .normal)
jobSpecsButton.translatesAutoresizingMaskIntoConstraints = false
jobSpecsButton.addTarget(self, action: #selector(HandleJobSpecs), for: .touchUpInside)
return jobSpecsButton
}()
lazy var cameraButton: UIButton = {
let cameraButton = UIButton(type: .system)
cameraButton.setTitle("Camera & Lenses", for: .normal)
cameraButton.titleLabel?.font = UIFont(name: "HelveticaNeue", size: 14.0)
cameraButton.layer.masksToBounds = true
cameraButton.contentHorizontalAlignment = .left
cameraButton.setTitleColor(UIColor.gray, for: UIControlState.normal)
cameraButton.backgroundColor = UIColor(red:1.00, green:1.00, blue:1.00, alpha:1.0)
cameraButton.setTitleColor(.black, for: .normal)
cameraButton.translatesAutoresizingMaskIntoConstraints = false
cameraButton.addTarget(self, action: #selector(HandleCameraLenses), for: .touchUpInside)
return cameraButton
}()
lazy var SKCButton: UIButton = {
let SKCButton = UIButton(type: .system)
SKCButton.setTitle("Shoot Kit Checklist", for: .normal)
SKCButton.titleLabel?.font = UIFont(name: "HelveticaNeue", size: 14.0)
SKCButton.layer.masksToBounds = true
SKCButton.contentHorizontalAlignment = .left
SKCButton.setTitleColor(UIColor.gray, for: UIControlState.normal)
SKCButton.backgroundColor = UIColor(red:1.00, green:1.00, blue:1.00, alpha:1.0)
SKCButton.setTitleColor(.black, for: .normal)
SKCButton.translatesAutoresizingMaskIntoConstraints = false
SKCButton.addTarget(self, action: #selector(HandleSKC), for: .touchUpInside)
return SKCButton
}()
// STACKED VIEWS
lazy var stack:UIStackView = {
let s = UIStackView(frame: self.view.bounds)
s.axis = .vertical
s.distribution = .equalSpacing
s.alignment = .fill
s.spacing = 10
//s.autoresizingMask = [.flexibleWidth, .flexibleHeight]
s.addArrangedSubview(self.generalTitle)
s.addArrangedSubview(self.jobBrand)
s.addArrangedSubview(self.jobName)
s.addArrangedSubview(self.directorName)
s.addArrangedSubview(self.AgencyName)
s.addArrangedSubview(self.prodCoName)
s.addArrangedSubview(self.technicalTitle)
s.addArrangedSubview(self.jobSpecsButton)
s.addArrangedSubview(self.cameraButton)
s.addArrangedSubview(self.SKCButton)
return s
}()
// SUPER VIEW DID LOAD
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
}
// VIEW WILL APPEAR
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.navigationItem.title = "Job Information"
self.tabBarController?.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(HandleEditJob))
view.addSubview(stack)
jobInfoValues()
setupLayout()
}
// OBSERVE JOB
var job: Job?
// FILL IN TEXT FIELDS FROM FIREBASE
func jobInfoValues(){
jobBrand.text = job?.jobBrand
jobName.text = job?.jobName
directorName.text = job?.directorName
}
// BUTTONS & ACTIONS
#IBAction func HandleJobSpecs(sender : UIButton) {
let jobSpecs = page_jobSpecs()
show(jobSpecs, sender: self)
}
#IBAction func HandleCameraLenses(sender : UIButton) {
let cameras = page_camera_lenses()
show(cameras, sender: self)
}
#IBAction func HandleSKC(sender : UIButton) {
let shootKit = page_SKC()
show(shootKit, sender: self)
}
#IBAction func HandleEditJob(sender : UIButton) {
// CHANGE NAVIGATION BAR ITEM
self.tabBarController?.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(HandleJobEditDone))
// ALLOW TEXT FIELDS TO BE EDITABLE
jobBrand.isUserInteractionEnabled = true
jobName.isUserInteractionEnabled = true
directorName.isUserInteractionEnabled = true
AgencyName.isUserInteractionEnabled = true
prodCoName.isUserInteractionEnabled = true
// ADDING CLEAR BUTTON
jobBrand.clearButtonMode = .always
jobName.clearButtonMode = .always
directorName.clearButtonMode = .always
AgencyName.clearButtonMode = .always
prodCoName.clearButtonMode = .always
}
#IBAction func HandleJobEditDone(sender : UIButton) {
self.tabBarController?.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(HandleEditJob))
jobBrand.isUserInteractionEnabled = false
jobName.isUserInteractionEnabled = false
directorName.isUserInteractionEnabled = false
AgencyName.isUserInteractionEnabled = false
prodCoName.isUserInteractionEnabled = false
// ADDING CLEAR BUTTON
jobBrand.clearButtonMode = .never
jobName.clearButtonMode = .never
directorName.clearButtonMode = .never
AgencyName.clearButtonMode = .never
prodCoName.clearButtonMode = .never
}
// CONSTRAINTS
private func setupLayout(){
// Auto layout constraints for jobInfo
generalTitle.topAnchor.constraint(equalTo: view.topAnchor, constant: 150).isActive = true
generalTitle.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
// Auto layout constraints for jobInfo
jobBrand.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
// Auto layout constraints for cameras
jobName.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
// Auto layout constraints for attachments
directorName.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
// Auto layout constraints for attachments
AgencyName.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
// Auto layout constraints for attachments
prodCoName.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
// Auto layout constraints for attachments
technicalTitle.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
// Auto layout constraints for attachments
jobSpecsButton.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
// Auto layout constraints for attachments
cameraButton.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
// Auto layout constraints for SKC
SKCButton.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
SKCButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -100).isActive = true
}
}
Please check the codes.
https://github.com/HsiaoAi/JobInfoStackoverflow/tree/master/JobInfoStackoverflow
Make sure your Json decoding is correct, because I found you used"director" in Firebase but "directorName" when init Job
I modified the "fetchJobs()" because I dont have the Firebase plist file
I modified 5 stuff and I marked them in code as "###", you can use search and check them, I hope it works for you too.

how to use ViewWithTag in Swift?

I'm new to Swift, however I work in Objective-C before. I'm having issue in checkin if the cell is being reused in UITableView or not.
let cell = tableView.dequeueReusableCellWithIdentifier(strCellId, forIndexPath:indexPath) as! MGSwipeTableCell
cell.backgroundColor = UIColor.clearColor()
let SMSObj = self.arraySMSContent[indexPath.row] as! SMSModel
let lblMessage = UILabel(frame: CGRectMake(15, 10, Constants.SCREEN_WIDTH/1.4, Constants.SCREEN_HEIGHT/11))
lblMessage.text = SMSObj.strSMSContent
lblMessage.textAlignment = NSTextAlignment.Left
lblMessage.numberOfLines = 2
lblMessage.textColor = UIColor.whiteColor()
cell.contentView.addSubview(lblMessage)
I have used MGSwipebleCell. While scrolling lblMessage overlaps. Even we can not check if cell is nil or not. So how to use viewWithTag in this situation? Thnaks
Rather than using viewWithTag, you can register a custom class with the cell reuse identifier. Then you can access the label using a property of that subclass:
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerClass(CustomCell.self, forCellReuseIdentifier: "CustomCell")
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath) as! CustomCell
cell.smsLabel.text = ...
return cell
}
Where:
class CustomCell: MGSwipeTableCell {
var smsLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .Left
label.numberOfLines = 2
label.textColor = .whiteColor()
return label
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = .blueColor()
contentView.addSubview(smsLabel)
NSLayoutConstraint.activateConstraints([
smsLabel.topAnchor.constraintEqualToAnchor(contentView.topAnchor, constant: 5),
smsLabel.bottomAnchor.constraintEqualToAnchor(contentView.bottomAnchor, constant: -5),
smsLabel.leadingAnchor.constraintEqualToAnchor(contentView.leadingAnchor, constant: 5),
smsLabel.trailingAnchor.constraintEqualToAnchor(contentView.trailingAnchor, constant: -5)
])
leftButtons = [MGSwipeButton(title: "Red", backgroundColor: .redColor())]
leftSwipeSettings.transition = .Rotate3D;
rightButtons = [MGSwipeButton(title: "Green", backgroundColor: .greenColor())]
rightSwipeSettings.transition = .Rotate3D;
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("not needed")
}
}
There're many ways to solve your problem. Please try:
if let lblMessage = cell.contentView.viewWithTag(9999) as? UILabel { //Tag
lblMessage.text = SMSObj.strSMSContent
}else{
let lblMessage = UILabel(frame: CGRectMake(15, 10, Constants.SCREEN_WIDTH/1.4, Constants.SCREEN_HEIGHT/11))
lblMessage.text = SMSObj.strSMSContent
lblMessage.textAlignment = NSTextAlignment.Left
lblMessage.numberOfLines = 2
lblMessage.textColor = UIColor.whiteColor()
lblMessage.tag = 9999 //Tag
cell.contentView.addSubview(lblMessage)
}