What is this extra row in the UITabBar (Swift) - swift

I programmatically add a TableView in a ViewController, then appeared an extra row. I would like to be able to manage it, but first I would have to know what it is. This line seems to come from the TabBar rather than the TableView. Do you know which object it is?
The code:
import UIKit
final class SearchViewController2: UIViewController, UITableViewDataSource, UITableViewDelegate {
private let ingredients = [
"flour",
"sugar",
"eggs",
"butter",
"baking powder",
"vanilla extract",
"milk",
"salt",
"cocoa powder",
"yeast",
"olive oil",
"cheese",
"breadcrumbs",
"onions",
"garlic",
"tomatoes",
"basil",
"oregano",
"thyme",
"parsley",
"pepper"
]
private let tableView: UITableView = {
let tableView = UITableView()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
return tableView
}()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let ingredient = ingredients[indexPath.row]
var cellConfig = cell.defaultContentConfiguration()
cellConfig.text = ingredient
cell.contentConfiguration = cellConfig
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
ingredients.count
}
override func viewDidLoad() {
super.viewDidLoad()
let header = StretchyTableHeaderView(
frame: CGRect(
x: 0,
y: 0,
width: view.frame.size.width,
height: view.frame.size.width / 2
)
)
view.addSubview(tableView)
header.imageView.image = UIImage(named: "header")
tableView.dataSource = self
tableView.delegate = self
tableView.frame = view.bounds
tableView.tableHeaderView = header
}
}
extension SearchViewController2: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard let header = tableView.tableHeaderView as? StretchyTableHeaderView else { return }
header.scrollViewDidScroll(scrollView: tableView)
}
}
I tried to add/remove a footer in the TableView... No changes.

Related

How I can pin the tableHeaderView?

Tell me, please, how I can pin the tableHeaderView on the ViewController's screen through code? I use this code, but the tableViewHeader disappears on scrolling:
import UIKit
class TestViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
lazy var tableViewTest = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
createTable()
}
private func createTable() {
self.tableViewTest = UITableView(frame: view.bounds, style: .grouped)
tableViewTest.register(TestTableViewCell.self, forCellReuseIdentifier: "Test")
self.tableViewTest.delegate = self
self.tableViewTest.dataSource = self
tableViewTest.autoresizingMask = [.flexibleWidth, .flexibleHeight]
tableViewTest.separatorInset.left = 10
tableViewTest.separatorInset.right = 10
tableViewTest.tableHeaderView = "Test Header"
view.addSubview(tableViewTest)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Test", for: indexPath) as! TestTableViewCell
cell.testLabel.text = "test label"
return cell
}
}
This is my second class:
import UIKit
class TestTableViewCell: UITableViewCell {
let testLabel = UILabel()
override func layoutSubviews() {
super.layoutSubviews()
testLabel.frame = CGRect(x: 60, y: 5, width: UIScreen.main.bounds.width - 80, height: 50)
testLabel.numberOfLines = 0
testLabel.sizeToFit()
addSubview(testLabel)
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Try to use the implicit construct of UITableView
lazy var tableViewTest = UITableView(frame: .zero, style: .plain)
But also you need to change your table header to section header which you can define in UITableViewDelegate method
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let identifier = YourSectionHeaderView.reuseIdentifier
let headerView =
tableView.dequeueReusableHeaderFooterView(withIdentifier: identifier)
return headerView
}
By the way, I also recommend you to use constraint instead of autoresizing mask
Thank you.
This work for me:
import UIKit
class TestViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
lazy var tableViewTest = UITableView()
var segmentedControl = UISegmentedControl(items: ["Test1", "Test2", "Test3"])
override func viewDidLoad() {
super.viewDidLoad()
createTable()
configureSegmentedControl()
}
private func createTable() {
self.tableViewTest = UITableView(frame: view.bounds, style: .plain)
tableViewTest.register(TestTableViewCell.self, forCellReuseIdentifier: "Test")
self.tableViewTest.delegate = self
self.tableViewTest.dataSource = self
tableViewTest.autoresizingMask = [.flexibleWidth, .flexibleHeight]
tableViewTest.separatorInset.left = 10
tableViewTest.separatorInset.right = 10
tableViewTest.tableFooterView = UIView()
view.addSubview(tableViewTest)
}
private func configureSegmentedControl() {
segmentedControl.selectedSegmentIndex = 0
segmentedControl.frame = CGRect(x: 10, y: 150, width: UIScreen.main.bounds.width - 20.0, height: 40)
segmentedControl.layer.cornerRadius = 5.0
segmentedControl.backgroundColor = .blue
segmentedControl.selectedSegmentTintColor = .red
segmentedControl.tintColor = .white
segmentedControl.addTarget(self, action: #selector(changeSegment), for: .valueChanged)
}
#objc func changeSegment(sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
print("Test1")
case 1:
print("Test2")
default:
print("Test3")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Test", for: indexPath) as! TestTableViewCell
cell.testLabel.text = "test label"
return cell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return segmentedControl
}
}

How to use a UISwitch to add and remove data from an Array of Data

I have a tableview with a UISwitch in each of the cells. What I am trying to do is that whenever the UISwitch is Toggled On, it adds that cell into an array and when the switch is Toggled Off it removes it. Right now it only adds and doesn't remove.
Once this is complete I need the CollectionView that is also within this ViewController to update and visually show the newStringArray Cells based on the number in that array and that also is able to appear and disappear based on the cells that have their UISwitch toggled on.
import UIKit
class NewMoveViewController: UIViewController {
private var stringSource = ["String 1,", "String 2", "String 3"]
var newStringArray = Array<String>()
private let tableView: UITableView = {
let tableView = UITableView()
tableView.rowHeight = 100
return tableView
}()
private var collectionView: UICollectionView?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = CGSize(width: 50, height: 50)
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView?.register(NewMoveCollectionViewCell.self, forCellWithReuseIdentifier: NewMoveCollectionViewCell.identifier)
collectionView?.showsHorizontalScrollIndicator = false
title = "Add to Group"
tableView.register(NewMoveTableViewCell.self, forCellReuseIdentifier: NewMoveTableViewCell.identifier)
tableView.delegate = self
tableView.dataSource = self
collectionView?.backgroundColor = .systemBlue
collectionView?.dataSource = self
collectionView?.delegate = self
guard let myCollection = collectionView else {
return
}
view.addSubview(myCollection)
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView?.frame = CGRect(x: 0, y: 100, width: view.frame.size.width, height: 50)
tableView.frame = CGRect(x: 0, y: 200, width: view.frame.size.width, height: view.frame.size.height)
}
}
extension NewMoveViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: NewMoveTableViewCell.identifier, for: indexPath) as! NewMoveTableViewCell
let switchView = UISwitch(frame: .zero)
switchView.setOn(false, animated: true)
switchView.tag = indexPath.row
switchView.addTarget(self, action: #selector(self.switchDidChange(_:)), for: .valueChanged)
cell.accessoryView = switchView
cell.configure(with: "", label: "test")
return cell
}
#objc func switchDidChange(_ sender: UISwitch) {
newStringArray.append(stringSource[sender.tag])
// newStringArray.remove(stringSource.remove(at: [s]))
print(newStringArray)
}
func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
return false
}
}
extension NewMoveViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return newStringArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NewMoveCollectionViewCell.identifier, for: indexPath) as! NewMoveCollectionViewCell
return cell
}
}
the hardest part is to remove an object from an array, my approach on these situations is to transform my array in a NSMutableArray because it have a function to remove an specific object, then make a delegate in the cell that informs the viewController to remove the object from the list and reload the tableView.
the delegate wil be something like this:
protocol RemoveObjectFromCell {
func removeObjectIncell(object: MyObject)
}
class myCell: UITableViewCell {
//your outlets and variables
var object: MyObject?
var delegate: removeObjectIncell?
func setupCell(object: myObject) {
//configure your cell with the specific object
}
}
make sure of calling the delegate on the switch action inside you cell class like this:
#IBAction func switchPressed(sender: UISwitch) {
if !sender.isOn {
self.delegate?.removeObjectIncell(object: self.object)
}
in the view controller implement your protocol and use the required function like this:
class myViewController: UIViewController, RemoveObjectFromCell {
//everything that goes in your class
func removeObjectIncell(object: MyObject) {
self.myArray.remove(object)
DispatchQueue.main.async {
self.myTableView.reloadData()
}
}
}
In order to get changes you want you have to set property which is gonna indicate whether switch is on or off
Something like: var switchIsActive = false
and simply change it in function and if it is turned on you perform one action when it is off you perform another one. Also after function you have to reload your tableView tableView.reloadData()
You can remove elements in your array by their tag by calling Array.remove(at: Int). It can be done by the cells [indexPath.row]

add together all of the labels in tableview cells

i want my swift code to add together all of the tableview cells together and convert it from a string to a int. The code should be called from the view did load func. The total should be 3 when all of the labels are added together. Look at my comment in view did load for more info. The code does not use storyboards.
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var numberOfRows = 3
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { numberOfRows }
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 118 }
var tableView = UITableView()
var selectedIndexPath = IndexPath(row: 0, section: 0)
override func viewDidLoad() {
super.viewDidLoad()
setTableVIew()
//print the sum of the labels added togther on the tableview cells
}
func setTableVIew(){
let VCframe = view.frame
let height = VCframe.height * 0.8
let widthx = VCframe.width
tableView.frame = CGRect(x: 10, y: 0, width: widthx - 20, height: height)
tableView.delegate = self
tableView.dataSource = self
view.addSubview(tableView)
tableView.register(customtv.self, forCellReuseIdentifier: "cell")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! customtv
cell.lbl.text = "\(indexPath.row)"
return cell
}
}
class customtv: UITableViewCell {
lazy var backView : UIView = {
let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width , height: 110))
view.backgroundColor = .green
print(self.frame.width)
return view
}()
override func layoutSubviews() {
backView.clipsToBounds = true
backView.frame = CGRect(x: 0, y: 6, width: bounds.maxX , height: 110)
}
lazy var lbl : UILabel = {
let press = UILabel(frame: CGRect(x: 0, y: 3, width: 120 , height: 50))
press.backgroundColor = .yellow
press.text = String("1")
return press
}()
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(animated, animated: true)
addSubview(backView)
backView.addSubview(lbl)
}
}
Here's how you do this:
override func viewDidLoad() {
super.viewDidLoad()
setTableView()
calculateSum()
}
func calculateSum() {
var sum = 0
for row in 0..<numberOfRows {
let indexPath = IndexPath(row: row, section: 0)
let cell = tableView.cellForRow(at: indexPath) as! CustomTableViewCell
sum += Int(cell.label.text ?? "") ?? 0
}
print(sum)
}
Note: Make the class and object names consistent eg: lbl -> label and customtv -> CustomTableViewCell as I've given above. Even the method names setTableVIew -> setTableView. It would be best if you use SwiftLint.

Adding two UITableViews in one UIVIewController programmatically and want to change the View of Controller with UISegmentedControl

I want to change the View of my ViewController based on 2 tableVIews with segmentedControl. I have reached it with programmatic approach. I created two TableViews and One SegmentedControl. But When I change the segmentedControl it stays on the same Index but changing the View. I have to tap twice on each Segment to reach it. Why it is happening?
Here is my code:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
configureFirstTableView()
}
var firstTableView = UITableView()
var secondTableView = UITableView()
func configureFirstTableView() {
firstTableView.frame = self.view.frame
firstTableView.delegate = self
firstTableView.dataSource = self
firstTableView.register(UITableViewCell.self, forCellReuseIdentifier: "FirstCell")
view.addSubview(firstTableView)
}
func configureSecondTableView() {
secondTableView.frame = self.view.frame
secondTableView.delegate = self
secondTableView.dataSource = self
secondTableView.register(UITableViewCell.self, forCellReuseIdentifier: "SecondCell")
view.addSubview(secondTableView)
}
func configureSegmentedControl() -> UISegmentedControl {
let segmentedControl = UISegmentedControl(frame: CGRect(x: 10, y: 5, width: view.frame.width - 10, height: 30))
segmentedControl.insertSegment(withTitle: "First", at: 0, animated: false)
segmentedControl.insertSegment(withTitle: "Second", at: 1, animated: false)
segmentedControl.selectedSegmentIndex = 0
segmentedControl.addTarget(self, action: #selector(changeSegmentedControlValue(_:)), for: .valueChanged)
return segmentedControl
}
#objc func changeSegmentedControlValue(_ sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
print("1")
configureFirstTableView()
case 1:
print("2")
configureSecondTableView()
default:
break
}
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count: Int?
if tableView == firstTableView {
count = 3
return count!
} else if tableView == secondTableView {
count = 4
return count!
}
return count!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == firstTableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "FirstCell", for: indexPath)
cell.textLabel?.text = "First"
return cell
} else if tableView == secondTableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "SecondCell", for: indexPath)
cell.textLabel?.text = "Second"
return cell
}
return UITableViewCell()
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let v = UIView()
v.backgroundColor = .white
v.addSubview(configureSegmentedControl())
return v
}
}

Put a UIButton in tableView

I made a code for added a array of UIView in my TableView, but when I add them, it doesn't show them row by row while I made a loop for it to add each item in my TableView but it adds them one above the other, another solution?
my view controller :
class mainViewController: UIViewController {
private var myArray: [UIView] = []
private var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = self.view.frame.height
myTableView = UITableView(frame: CGRect(x: 50, y: barHeight, width: displayWidth, height: displayHeight - barHeight))
myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
myTableView.dataSource = self
myTableView.delegate = self
self.view.addSubview(myTableView)
let DoneBut: UIButton = UIButton(frame: CGRect(x: 50, y: 0, width: 150, height: 50))
DoneBut.setTitle("Done", for: .normal)
DoneBut.backgroundColor = UIColor.blue
let DoneBut2: UIButton = UIButton(frame: CGRect(x: 50, y: 0, width: 50, height: 50))
DoneBut2.setTitle("Done2", for: .normal)
DoneBut2.backgroundColor = UIColor.blue
let view1 = UIView()
view1.addSubview(DoneBut)
myArray.append(view1)
let view2 = UIView()
view2.addSubview(DoneBut2)
myArray.append(view2)
}
}
extension mainViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:
"MyCell", for: indexPath as IndexPath)
for array in myArray {
cell.contentView.addSubview(array)
}
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return myArray.count
}
}
extension mainViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath:
IndexPath) {
print("Num: \(indexPath.row)")
print("Value: \(myArray[indexPath.row])")
}
}
Your issue is that each time the cellForRowAt function is asking for a cell, you are looping through ALL the views and adding them to each cell. Instead you should be indexing in it using the indexPath. See the following:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath as IndexPath)
// check if there is a view at the indexPath
if indexPath.row < myArray.count {
// there is a view, add it to the cells contentView
cell.contentView.addSubview(myArray[indexPath.row])
} else {
print("no view at index")
}
return cell
}