Why does UITableView show the same view for each cell? - swift

I have been trying to implement a UISegmentedControl in the first cell of a UITableView and it has worked however it shows for each cell following the first one. How do I fix this
import UIKit
class InfoTableViewController: UITableViewController {
convenience init() {
self.init(style: UITableView.Style.grouped)
}
var image: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 5
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath)
if indexPath.row == 0 {
let genderItems = ["Male", "Female"]
let genderControl = UISegmentedControl(items: genderItems)
genderControl.addTarget(self, action: #selector(genderChanged), for: .valueChanged)
cell.contentView.addSubview(genderControl)
genderControl.translatesAutoresizingMaskIntoConstraints = false
genderControl.centerXAnchor.constraint(equalTo: cell.centerXAnchor).isActive = true
genderControl.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true
}
return cell
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0{
return view.frame.height/3
}
return UITableView.automaticDimension
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
let imageView: UIImageView = UIImageView()
imageView.clipsToBounds = true
imageView.contentMode = .scaleAspectFit
imageView.image = self.image
return imageView
}
return nil
}
#objc func genderChanged(){
}
}

Solution #1
Remove this
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 5
}
and return 5 here
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 5
}
Solution #2
Replace
if indexPath.row == 0 {
to
if indexPath.section == 0 {
in both options you should implement else for that if to return the other needed cell
also move this line to viewDidLoad
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")

Related

SWIFT / UIKit Footer text displaying over dynamic tableViewCell

I have a UITableViewcontroller setup with just two cells. The footer text is displaying over the last cell.
Strangely I have other controllers with practically the same setup and code and where the footer is showing as expected.
I have tried tried changing the style group / inset etc.
Any ideas appreciated. Thanks
import UIKit
class LanguagesTableViewController: UITableViewController {
var checked = [Bool]()
var choices = ["English","French"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.allowsMultipleSelection = false
let defaults = UserDefaults.standard
checked = defaults.array(forKey: "Language") as? [Bool] ?? [true, false]
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateSwitchState()
tableView.reloadData()
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "languageChoiceCell", for: indexPath)
cell.textLabel?.text = choices[indexPath.row]
if !checked[indexPath.row] {
cell.accessoryType = .none
} else if checked[indexPath.row] {
cell.accessoryType = .checkmark
}
return cell
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return choices.count
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if checked[indexPath.row] {
tableView.selectRow(at: indexPath, animated: false, scrollPosition: UITableView.ScrollPosition.none)
}
}
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return Constants.languagesFooterText
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// If we are selecting a row that is already checked we do nothing
guard !checked[indexPath.row] else { return }
// Reset all checked state.
checked = [Bool](repeating: false, count: choices.count)
// And set the current row to true.
checked[indexPath.row] = true
if let cell = tableView.cellForRow(at: indexPath) {
if cell.accessoryType == .checkmark {
cell.accessoryType = .none
checked[indexPath.row] = false
} else {
cell.accessoryType = .checkmark
checked[indexPath.row] = true
}
}
updateSwitchState()
}
// did ** DE ** Select
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
if cell.accessoryType == .checkmark {
cell.accessoryType = .none
checked[indexPath.row] = false
} else {
cell.accessoryType = .checkmark
checked[indexPath.row] = true
}
}
updateSwitchState()
}
func updateSwitchState() {
let defaults = UserDefaults.standard
defaults.set(checked, forKey: "Language")
}
}
You should set a height on your footer view. You can do this by calling tableView(:heightForFooterInSection:) and returning UITableView.automaticDimension.
As you are inheriting from UITableViewController you can do it in the following way
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
UITableView.automaticDimension
}
This will give you the following result:

Disable users to scroll up in tableView?

Similar to tinder, if a user goes down, I don't want them to be able to go back upwards. How do I do this?
I tried the code below, but it keeps running an error: Index out of range.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath) as! FeedTableViewCell
cell.collectionView.delegate = self
cell.collectionView.dataSource = self
//reloading the tableview
let artist: UserModel
artist = self.userList[indexPath.row]
cell.nameLabel.text = artist.name
cell.passionLabel.text = artist.passion
cell.collectionView.reloadData()
if indexPath.row == 1 {
userList.removeFirst()
}
return cell
}
Refer below code,
You need to store last scroll Content Offset of tableview, when user scroll down, refer scrollViewDidScroll method.
#IBOutlet weak var tableView: UITableView!
var lastContentOffsetY:CGFloat = 0.0
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.bounces = false
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "reuse")
self.tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 50
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuse")
return cell!
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if(scrollView.contentOffset.y < self.lastContentOffsetY){
self.tableView.setContentOffset(CGPoint(x:scrollView.contentOffset.x, y: self.lastContentOffsetY), animated: false)
}else{
self.lastContentOffsetY = scrollView.contentOffset.y
}
}

dropdown tableview - Swift

I draw data from MSSQL and load this data into tableView. No problem with that. I want to make a drop down menu, but I don't know how to do it. Now all the menus are angled. How do I make tables open and close? How can I do this? How do I make a folding tableview?
class CollapsibleTableViewController: UITableViewController {
var sectionNames: [String] = []
var sectionDetay: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
makaleDetay()
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableView.automaticDimension
}
func makaleDetay(){
...
client.execute("SELECT soru_icerik FROM uzaktanptomg_db.omg_user.ie_soru", completion: { (_ results: ([Any]?)) in
... self.sectionDetay.append(String(result))
...
}
extension CollapsibleTableViewController {
override func numberOfSections(in tableView: UITableView) -> Int {
return sectionDetay.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: CollapsibleTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as? CollapsibleTableViewCell ??
CollapsibleTableViewCell(style: .default, reuseIdentifier: "cell")
cell.nameLabel.text = "SSS"
cell.detailLabel.text = sectionDetay[indexPath.section]
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "header") as? CollapsibleTableViewHeader ?? CollapsibleTableViewHeader(reuseIdentifier: "header")
header.titleLabel.text = sectionNames[section]
header.arrowLabel.text = ">"
header.setCollapsed(false)
header.section = section
header.delegate = self
return header
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44.0
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 1.0
}
}
extension CollapsibleTableViewController: CollapsibleTableViewHeaderDelegate {
func toggleSection(_ header: CollapsibleTableViewHeader, section: Int) {
....
}
}

How do I create two different sections on table?

I am trying to separate these data into two different sections :
–one section with movies of runtime more than 120 mins, another for the rest.
The problem is I am pulling data from the same array. How can I apply conditional statement/logic and insert into each section depending on the value? I am able to pull data from the different arrays and insert into the different sections however. Please guide me. Thank you
import UIKit
class ViewController: UIViewController,
UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section:
Int) -> Int {
return movieList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : MovieCell = tableView.dequeueReusableCell(withIdentifier: "MovieCell", for: indexPath) as! MovieCell
let p = movieList[indexPath.row]
cell.nameLabel.text = p.movieName
cell.runTimeLabel.text = "\(p.runtime) mins"
cell.movieImageView.image = UIImage(named: p.imageName)
return cell
}
var movieList : [Movie] = []
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
movieList.append(Movie(movieName:"Oklahoma Crude",
movieDesc:"",
runtime:115,
imageName:"movie_oklahoma"))
movieList.append(Movie(movieName:"Bootleggers",
movieDesc:"",
runtime:140,
imageName:"movie_bootleggers"))
movieList.append(Movie(movieName:"Superdad",
movieDesc:"",
runtime:112,
imageName:"movie_superdad"))
// Do any additional setup after loading the view, typically from a nib.
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete{
movieList.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
#IBAction func editButton(_ sender: Any) {
if !self.tableView.isEditing
{
(sender as AnyObject).setTitle("Done", for: .normal)
tableView.setEditing(true, animated: true)
}
else
{
(sender as AnyObject).setTitle("Edit", for: .normal)
tableView.setEditing(false, animated: true)
} }
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let m = movieList[sourceIndexPath.row]
movieList.remove(at:sourceIndexPath.row)
movieList.insert(m, at:destinationIndexPath.row)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
let headerTitles = ["More than 120 minutes", "Others"]
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section < headerTitles.count {
return headerTitles[section]
}
return nil
}
}
Suppose with 120 will be section = 0
func tableView(_ tableView: UITableView, numberOfRowsInSection section:
Int) -> Int {
let arr120 = movieList.filter { $0.runtime >= 120 }
let arrLess120 = movieList.filter { $0.runtime < 120 }
return section == 0 ? arr120.count : arrLess120.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let arr120 = movieList.filter { $0.runtime >= 120 }
///..
}
else {
let arrLess120 = movieList.filter { $0.runtime < 120 }
////....
}
Howver this not the perfest solution , you can organize your data source to
struct AllMovies {
let title:String // ex. above 120
let arr:[Movie]
}
It is better to have 2 arrays - one containing the movies shorter than 120mins and one containing the movies longer than 120mins. You should create those two arrays from the movieList array:
var lessThan120: [Movie]!
var moreThan120: [Movie]!
override func viewDidLoad() {
super.viewDidLoad()
movieList.append(Movie(movieName:"Oklahoma Crude",
movieDesc:"",
runtime:115,
imageName:"movie_oklahoma"))
movieList.append(Movie(movieName:"Bootleggers",
movieDesc:"",
runtime:140,
imageName:"movie_bootleggers"))
movieList.append(Movie(movieName:"Superdad",
movieDesc:"",
runtime:112,
imageName:"movie_superdad"))
let count = movieList.partition(by: {$0.runtime > 120})
lessThan120 = Array(movieList[0..<count])
moreThan120 = Array(movieList[count...])
}
Then implementing the data source methods would be very simple:
func tableView(_ tableView: UITableView, numberOfRowsInSection section:
Int) -> Int {
if section == 0 {
return moreThan120.count
} else if section == 1 {
return lessThan120.count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : MovieCell = tableView.dequeueReusableCell(withIdentifier: "MovieCell", for: indexPath) as! MovieCell
let p = indexPath.section == 0 ? moreThan120[indexPath.row] : lessThan120[indexPath.row]
cell.nameLabel.text = p.movieName
cell.runTimeLabel.text = "\(p.runtime) mins"
cell.movieImageView.image = UIImage(named: p.imageName)
return cell
}
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
var movieList = [String]()
var movieListGreaterThen120 = [String]()
var movieListSmallerThen120 = [String]()
for item in movieList {
if item.runtime > 120 {
movieListGreaterThen120.append(item)
}else {
movieListSmallerThen120.append(item)
}
}
//MARK: - tableView DataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section:
Int) -> Int {
if section == 0 {
return movieListGreaterThen120.count
}else {
return movieListSmallerThen120.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : MovieCell = tableView.dequeueReusableCell(withIdentifier: "MovieCell", for: indexPath) as! MovieCell
if indexPath.section == 0 {
//Load your movieListGreaterThen120 Data
}else {
//Load your movieListSmallerThen120 Data
}
return cell
}
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
"movieList" array can be filtered out depending on the runtime value.
if indexPath.section == 0 {
movieList.filter { $0.runtime >= 120 }
}
else {
movieList.filter { $0.runtime < 120 }
}

UITableView only displays one section

I have a UITableView that is supposed to have items in sections but only the first section (Science) in the array is appearing and I'm not sure what's wrong with it. It should be displaying both sections.
My code:
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var tableView: UITableView!
var checked = [Bool]()
let section = ["Science", "Math"]
let items = [["Physics", "Biology", "Chemistry"], ["Algebra I", "Algebra II", "Statistics"]]
override func viewDidLoad(){
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.tableView.allowsMultipleSelection = true
}
override func didReceiveMemoryWarning(){
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return self.items[section].count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int{
// #warning Incomplete implementation, return the number of sections
return self.section.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?{
return self.section[section]
}
//All before.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = self.items[indexPath.section][indexPath.row]
//MARK: -Checkmark and save support.
cell.accessoryType = cell.isSelected ? .checkmark : .none
cell.selectionStyle = .none // to prevent cells from being "highlighted"
return cell
}
//MARK: - Checkmark and save functions.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = .none
}
//MARK: - Sections
}
You made a mistake, wrong method. Let change numberOfSectionsInTableView(tableView:) to:
func numberOfSections(in tableView: UITableView) -> Int {
return self.section.count
}
In ViewDidLoad :
tableView.delegate = self
tableView.dataSource = self