Rows in table view do not expand the width of it and are stacked Swift - ios

I have spent an hour trying to figure out what I'm doing wrong but no success. below is the code and an image of the result. All the rows appear one on top of the other in the first row of the table. and the row does not expand the width of the table as I have set it in the constraints. What am I doing wrong? Thank you.
The table view class:
class TableViewListType: UITableView {
override init(frame: CGRect, style: UITableView.Style) {
super.init(frame: frame, style: style)
translatesAutoresizingMaskIntoConstraints = false
allowsSelection = true
allowsMultipleSelection = false
allowsSelectionDuringEditing = true
allowsMultipleSelectionDuringEditing = true
dragInteractionEnabled = false
backgroundColor = .clear
separatorColor = .white
separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
indicatorStyle = .white
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The row classes for the table view:
class SuperRowForProfileAttributesTable: UITableViewCell {
//MARK: - Properties.
internal let firstLabel: LabelForRowInList = {
let label = LabelForRowInList(frame: .zero)
return label
}()
internal let secondLabel: LabelForRowInList = {
let label = LabelForRowInList(frame: .zero)
return label
}()
internal let thirdLabel: LabelForRowInList = {
let label = LabelForRowInList(frame: .zero)
return label
}()
internal let fourthLabel: LabelForRowInList = {
let label = LabelForRowInList(frame: .zero)
return label
}()
//MARK: - Init.
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
translatesAutoresizingMaskIntoConstraints = false
clipsToBounds = true
backgroundColor = .clear
setupViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//MARK: - Functions.
internal func setupViews() {
addSubview(firstLabel)
addSubview(secondLabel)
addSubview(thirdLabel)
addSubview(fourthLabel)
let firstConstraints = [
firstLabel.topAnchor.constraint(equalTo: topAnchor),
firstLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
firstLabel.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 1/3),
firstLabel.widthAnchor.constraint(equalTo: widthAnchor)
]
NSLayoutConstraint.activate(firstConstraints)
let secondConstraints = [
secondLabel.topAnchor.constraint(equalTo: firstLabel.bottomAnchor),
secondLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
secondLabel.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 1/3),
secondLabel.widthAnchor.constraint(equalTo: widthAnchor)
]
NSLayoutConstraint.activate(secondConstraints)
let thirdConstraints = [
thirdLabel.topAnchor.constraint(equalTo: secondLabel.bottomAnchor),
thirdLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
thirdLabel.trailingAnchor.constraint(equalTo: centerXAnchor),
thirdLabel.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 1/3),
]
NSLayoutConstraint.activate(thirdConstraints)
let fourthConstraints = [
fourthLabel.topAnchor.constraint(equalTo: secondLabel.bottomAnchor),
fourthLabel.leadingAnchor.constraint(equalTo: centerXAnchor),
fourthLabel.trailingAnchor.constraint(equalTo: trailingAnchor),
fourthLabel.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 1/3)
]
NSLayoutConstraint.activate(fourthConstraints)
}
}
class RowForExperienceInProfileTable: SuperRowForProfileAttributesTable {
//MARK: - Properties.
internal var valueForExperienceRow: ExperienceModelForProfileAttributes! {
didSet {
firstLabel.text = valueForExperienceRow.jobTitle
secondLabel.text = valueForExperienceRow.companyName
thirdLabel.text = valueForExperienceRow.startedWork
fourthLabel.text = valueForExperienceRow.finishedWork
}
}
}
class RowForSkillInProfileTable: SuperRowForProfileAttributesTable {
//MARK: - Properties.
internal var valueForSkillRow: SkillsModelForProfileAttributes! {
didSet {
firstLabel.text = valueForSkillRow.skill
}
}
//MARK: - Init.
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setupViews() {
addSubview(firstLabel)
let firstConstraints = [
firstLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
firstLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
firstLabel.widthAnchor.constraint(equalTo: widthAnchor, constant: 0),
firstLabel.heightAnchor.constraint(equalTo: heightAnchor, constant: 0)
]
NSLayoutConstraint.activate(firstConstraints)
}
}
class RowForEducationInProfileTable: SuperRowForProfileAttributesTable {
//MARK: - Properties.
internal var valueForEducationRow: EducationModelForProfileAttributes! {
didSet {
firstLabel.text = valueForEducationRow.institutionName
secondLabel.text = valueForEducationRow.degreeName
thirdLabel.text = valueForEducationRow.startedStudy
fourthLabel.text = valueForEducationRow.finishedStudy
}
}
}
The VC:
fileprivate var experienceForProfile = [ExperienceModelForProfileAttributes(jobTitle: "Tester", companyName: "Testing Company", startedWork: "May 2019", finishedWork: "October 2019"), ExperienceModelForProfileAttributes(jobTitle: "Welder", companyName: "Welding Company", startedWork: "January 2018", finishedWork: "May 2020")]
fileprivate var skillsForProfile = [SkillsModelForProfileAttributes]()
fileprivate var educationForProfile = [EducationModelForProfileAttributes]()
fileprivate lazy var tableForAttributes: TableViewListType = {
let table = TableViewListType(frame: .zero, style: .plain)
table.delegate = self
table.dataSource = self
return table
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.isHidden = true
tableForAttributes.register(RowForExperienceInProfileTable.self, forCellReuseIdentifier: firstIDForTable)
tableForAttributes.register(RowForSkillInProfileTable.self, forCellReuseIdentifier: secondIDForTable)
tableForAttributes.register(RowForEducationInProfileTable.self, forCellReuseIdentifier: thirdIDForTable)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var numberOfRows = 0
switch selectedMimicIndex {
case 0: numberOfRows = experienceForProfile.count
case 1: numberOfRows = skillsForProfile.count
case 2: numberOfRows = educationForProfile.count
default: print("nu such rows for attributes table in own profile")
}
return numberOfRows
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch selectedMimicIndex {
case 0: let cell = tableView.dequeueReusableCell(withIdentifier: firstIDForTable, for: indexPath) as! RowForExperienceInProfileTable
cell.valueForExperienceRow = experienceForProfile[indexPath.row]
return cell
case 1: let cell = tableView.dequeueReusableCell(withIdentifier: secondIDForTable, for: indexPath) as! RowForSkillInProfileTable
cell.valueForSkillRow = skillsForProfile[indexPath.row]
return cell
case 2: let cell = tableView.dequeueReusableCell(withIdentifier: thirdIDForTable, for: indexPath) as! RowForEducationInProfileTable
cell.valueForEducationRow = educationForProfile[indexPath.row]
return cell
default: return UITableViewCell()
}
}
The selectedMimicIndex value is changed the didSelectItem() function of the collection view that I have; a cv that controls what cells the table displays. reloadData() is called here after the Int value is changed when the user changes the selected cv cell.
Also notice that the second row is much taller that it should be; ignores the height that I have specified.

For every label you need to set and add it to contentView
firstLabel.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(firstLabel)
also the most bottom label to bottom of cell
fourthLabel.bottomAnchor.constraint(equalTo:self.contentView.bottomAnchor, constant:-20)

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

How can implement two vertical button in swipe to delete in swift 5

I am trying to implement swipe to delete feature with two options, one is to delete and another one is to edit. The things I want is these options should be vertical rather than horizontal.
Thanks in advance for support.
You can easily achieve this swipe to reveal option feature using Custom TablViewCell
Design a view with two buttons and add a swipe gesture to the view to reveal the vertically aligned buttons
Anyway, I think you would rather use default method editActionsForRowAt for similar cases. If not I hope this code will help you.
class TableViewController: UIViewController {
let tableView: UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.register(CustomCell.self,
forCellReuseIdentifier: CustomCell.identifier)
return tableView
}()
var selectedCell: CustomCell?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(tableView)
setupConstraints()
tableView.dataSource = self
tableView.delegate = self
tableView.reloadData()
}
func setupConstraints() {
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
}
extension TableViewController: UITableViewDelegate & UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: CustomCell.identifier, for: indexPath) as? CustomCell else {
return UITableViewCell()
}
cell.delegate = self
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
44
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
selectedCell?.setInitialState()
}
}
extension TableViewController: CustomCellDelegate {
func didUpdateState(customCell: CustomCell?) {
if customCell != selectedCell {
selectedCell?.setInitialState()
}
selectedCell = customCell
}
}
protocol CustomCellDelegate: class {
func didUpdateState(customCell: CustomCell?)
}
class CustomCell: UITableViewCell {
weak var delegate: CustomCellDelegate?
static let identifier = "Cell"
private let customViewWidth: CGFloat = 100
private let customView: CustomView = {
let view = CustomView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .yellow
return view
}()
private let fakeContentView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
enum CustomCellState {
case hiddenCustomView
case showedCustomView
}
private var trailingConstraint = NSLayoutConstraint()
private var cellState: CustomCellState = .hiddenCustomView
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = .yellow
let panGesture = UIPanGestureRecognizer(target: self, action:(#selector(handleGesture(_:))))
fakeContentView.addGestureRecognizer(panGesture)
contentView.addSubview(fakeContentView)
fakeContentView.addSubview(customView)
setConstraints()
updateCellState()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setInitialState() {
self.trailingConstraint.constant = 0
UIView.animate(withDuration: 0.5, animations: {
self.contentView.layoutIfNeeded()
})
}
#objc private func handleGesture(_ recognizer: UIPanGestureRecognizer) {
let location = recognizer.location(in: fakeContentView)
let dx = frame.width - location.x
updateFrame(deltaX: dx)
if recognizer.state == .ended {
cellState = dx > customViewWidth / 2 ? .showedCustomView : .hiddenCustomView
updateCellState()
delegate?.didUpdateState(customCell: self)
}
}
private func updateFrame(deltaX: CGFloat) {
guard abs(deltaX) <= customViewWidth else {
return
}
trailingConstraint.constant = -deltaX
contentView.layoutIfNeeded()
}
private func updateCellState() {
let dx: CGFloat = cellState == .hiddenCustomView ? 0 : customViewWidth
self.trailingConstraint.constant = -dx
UIView.animate(withDuration: 0.5, animations: {
self.contentView.layoutIfNeeded()
}, completion: nil)
}
private func setConstraints() {
trailingConstraint = fakeContentView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
trailingConstraint.isActive = true
NSLayoutConstraint.activate([
fakeContentView.topAnchor.constraint(equalTo: contentView.topAnchor),
fakeContentView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
fakeContentView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
customView.topAnchor.constraint(equalTo: fakeContentView.topAnchor),
customView.bottomAnchor.constraint(equalTo: fakeContentView.bottomAnchor),
customView.leadingAnchor.constraint(equalTo: fakeContentView.trailingAnchor),
customView.widthAnchor.constraint(equalToConstant: customViewWidth)
])
}
}
class CustomView: UIView {
private let button1: UIButton = {
let button = UIButton(type: .custom)
button.backgroundColor = .blue
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
private let button2: UIButton = {
let button = UIButton(type: .custom)
button.backgroundColor = .black
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .gray
addSubview(button1)
addSubview(button2)
setConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setConstraints() {
NSLayoutConstraint.activate([
button1.topAnchor.constraint(equalTo: topAnchor),
button1.leadingAnchor.constraint(equalTo: leadingAnchor),
button1.trailingAnchor.constraint(equalTo: trailingAnchor),
button1.bottomAnchor.constraint(equalTo: centerYAnchor),
button2.bottomAnchor.constraint(equalTo: bottomAnchor),
button2.leadingAnchor.constraint(equalTo: leadingAnchor),
button2.trailingAnchor.constraint(equalTo: trailingAnchor),
button2.topAnchor.constraint(equalTo: button1.bottomAnchor)
])
}
}

Creating a custom view when a tableview cell is selected

How would I create a new view that displays unique information for the selection of a specific tableview cell? For example, if row 1 is selected then the new image will have unique information to that cell which would be different from the view if row 2 was selected.
Here is the code for my tableview.
import UIKit
struct Data {
var sectionTitle = String()
var rowTitles = [String]()
}
class ViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
var dataArray = [Data(sectionTitle: "section 1", rowTitles: ["row 1", "row 2", "row 3"]),
Data(sectionTitle: "section 2", rowTitles: ["row 1"]),
Data(sectionTitle: "section 3", rowTitles: ["row 1", "row 2"])
]
var searchArray = [Data]()
var searching = false
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return searchArray[section].rowTitles.count
} else {
return dataArray[section].rowTitles.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "searchCell")
cell?.textLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
cell?.textLabel?.numberOfLines = 3
if searching {
cell?.textLabel?.text = self.searchArray[indexPath.section].rowTitles[indexPath.row]
} else {
cell?.textLabel?.text = self.dataArray[indexPath.section].rowTitles[indexPath.row]
}
return cell!
}
func numberOfSections(in tableView: UITableView) -> Int {
if searching {
return searchArray.count
} else {
return dataArray.count
}
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return dataArray[section].sectionTitle
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let index = indexPath.row
let data = dataArray[index]
let destVC = SecondViewController(data: data)
navigationController?.pushViewController(destVC, animated: true)
}
}
extension ViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == "" {
searching = false
view.endEditing(true)
tableView.reloadData()
} else {
searching = true
searchArray = dataArray.filter({$0.sectionTitle.lowercased().contains(searchBar.text!.lowercased()) || $0.rowTitles.map{$0.lowercased()}.contains(searchBar.text!.lowercased())})
tableView.reloadData()
}
}
}
Here is my second view controller which shows my generic stack view that I would like to change to display different images and labels depending on the cell that is selected in the tableview.
import UIKit
class SecondViewController: UIViewController {
var data: Data!
var dataView: DataView { return self.view as! DataView}
init(data: Data) {
self.data = data
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func loadView() {
self.view = DataView(frame: UIScreen.main.bounds)
}
}
class DataView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .white
setupViews()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
self.addSubview(stack)
stack.distribution = .fillProportionally
stack.spacing = 10
}
func setupConstraints() {
stack.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
stack.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
stack.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
stack.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
dataImage.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
dataImage.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 60).isActive = true
dataImage.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -60).isActive = true
}
let dataNameLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 20, weight: .bold)
label.textAlignment = .center
label.numberOfLines = 2
label.text = "Organization"
return label
} ()
let dataDescriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 18, weight: .bold)
label.textAlignment = .center
label.numberOfLines = 8
label.text = "Description"
return label
} ()
let dataImage: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "")?.withRenderingMode(.alwaysOriginal)
imageView.backgroundColor = .gray
imageView.layer.cornerRadius = 8
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.heightAnchor.constraint(equalToConstant: 260).isActive = true
return imageView
} ()
let visitButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Visit", for: .normal)
return button
} ()
lazy var stack: UIStackView = {
let stack = UIStackView(arrangedSubviews: [dataImage, dataNameLabel, dataDescriptionLabel, visitButton])
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
return stack
} ()
}
The objective is to have a user select a tableview cell, and for the stack view to display specific information that corresponds to that cell. How would I create this?
I'm not sure if you're asking how to pass information to a new view controller when an item in your TableViewController has been selected.
But I'm interpreting your question, as you know how to pass information, but you want to pass different information, maybe an image to your second view controller, based on which TableViewCell was selected.
The way that I would do this, is to have an array with images in your SecondViewController. When an item is selected in your TableViewController, also pass the indexPath.row to the SecondViewController. From there, you can load the image from your imagesArray at the indexPath.row that you have passed to it. You can do this by having an integer value in your SecondViewController, that is populated right before you segue.

How to create custom cells 100% programmatically in Swift?

I am trying to build a TableView programmatically, but I cannot get a basic standard label to display; all I see is basic empty cells. Here's my code:
TableView Cell:
class TableCell: UITableViewCell {
let cellView: UIView = {
let view = UIView()
view.backgroundColor = .systemRed
return view
}()
let labelView: UILabel = {
let label = UILabel()
label.text = "Cell 1"
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setup()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
addSubview(cellView)
NSLayoutConstraint.activate([
cellView.topAnchor.constraint(equalTo: topAnchor),
cellView.bottomAnchor.constraint(equalTo: bottomAnchor),
cellView.leadingAnchor.constraint(equalTo: leadingAnchor),
cellView.trailingAnchor.constraint(equalTo: trailingAnchor)])
cellView.addSubview(labelView)
}
}
Data Source:
class TableDataSource: NSObject, UITableViewDataSource {
let cellID = "cell"
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! TableCell
return cell
}
}
And this is the VC:
class TableViewController: UITableViewController {
let dataSource = TableDataSource()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(TableCell.self, forCellReuseIdentifier: dataSource.cellID)
tableView.dataSource = dataSource
}
}
I am trying to keep the code as basic as possible for future references. I've set various breakpoints to see what could go wrong, but they all check out. Could it be the constraints that are wrong?
Any help is appreciated.
I see several errors in your cell.
Add subviews to contentView, not directly to cell:
contentView.addSubview(cellView)
cellView.addSubview(labelView)
The same is necessary for constraints:
NSLayoutConstraint.activate([
cellView.topAnchor.constraint(equalTo: contentView.topAnchor),
cellView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
cellView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
cellView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
])
Views created in code need to set translatesAutoresizingMaskIntoConstraints = false,
let cellView: UIView = {
let view = UIView()
view.backgroundColor = .systemRed
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let labelView: UILabel = {
let label = UILabel()
label.text = "Cell 1"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
There are no constraints for your label.
Your constraints don't work, because you need to change translatesAutoresizingMaskIntoConstraints for cellView in your setup():
func setup() {
addSubview(cellView)
cellView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
cellView.topAnchor.constraint(equalTo: topAnchor),
cellView.bottomAnchor.constraint(equalTo: bottomAnchor),
cellView.leadingAnchor.constraint(equalTo: leadingAnchor),
cellView.trailingAnchor.constraint(equalTo: trailingAnchor)])
cellView.addSubview(labelView)
}

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