How do I create two different sections on table? - swift

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 }
}

Related

how to back data from viewcontroller to tableview use delegate

extension SecondViewController: UITableViewDelegate, UITableViewDataSource, EditContactDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contacts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ContactTableView
cell.contact = contacts[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let newVC = NewViewController()
newVC.nameLabel.text = contacts[indexPath.row].name
newVC.delegate = self
newVC.numberPhoneLabel.text = contacts[indexPath.row].numberPhone
newVC.avatarView.image = contacts[indexPath.row].avatar
self.navigationController?.pushViewController(newVC, animated: true)
}
func editContact(name: String, numberPhone: String) {
var contact:Contact?
let nameEdit = name
let numberEdit = numberPhone
contact?.name = nameEdit
contact?.numberPhone = numberEdit
}
}
My data is not updating at tableview even though delegate worksenter image description here
Make variable selectedIndex and set value on did select method
var selectedIndex = 0
func editContact(name: String, numberPhone: String) {
guard selectedIndex > 0 else { return }
contacts[selectedIndex].name = name
contacts[selectedIndex].numberPhone = numberPhone
tableView.reloadData()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let newVC = NewViewController()
newVC.nameLabel.text = contacts[indexPath.row].name
newVC.delegate = self
newVC.numberPhoneLabel.text = contacts[indexPath.row].numberPhone
newVC.avatarView.image = contacts[indexPath.row].avatar
self.navigationController?.pushViewController(newVC, animated: true)
selectedIndex = indexPath.row
}

Issue displaying search results

Essentially I have tableview with JSON data and a search controller for the user to quickly find values from the response. While the tableview data loads initially when attempting to search I get the error index out of range. The error occurs inside the cellForRowAt function. The following is what I am currently doing:
var sections = [customerListSection]()
var structure = [customerList]()
var searchsections = [customerListSection]()
var searchstruct = [customerList]()
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let section = sections[section]
let searchsection = searchsections[section]
if isFiltering() {
return searchsection.count
}
return section.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let item = sections[indexPath.section].items[indexPath.row]
let customerList: customerList
if isFiltering() {
customerList = searchstruct[indexPath.row]
} else {
customerList = item
}
return cell
}
struct customerListSection {
let customerType : String
var items : [customerList]
}
struct customerList: Decodable {
let customerid: Int
let customer: String
let type: String
}
From your code, it seems that there are multiple sections, so you need to implement numberOfSections(in:)method.
If you do not implement this method, the table configures the table with one section.)
https://developer.apple.com/documentation/uikit/uitableviewdatasource/1614860-numberofsections
And since the number of rows is the number of items, not the number of sections, the tableView(_:numberOfRowsInSection:) method should return items.count.
The following is the modified code:
override func numberOfSections(in tableView: UITableView) -> Int {
if isFiltering() {
return searchsections.count
}
return sections.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering() {
return searchsections[section].items.count
}
return sections[section].items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let customerList: customerList
if isFiltering() {
customerList = searchsections[indexPath.section].items[indexPath.row]
} else {
customerList = sections[indexPath.section].items[indexPath.row]
}
cell.textLabel?.textAlignment = .left
cell.selectionStyle = .none
cell.textLabel?.font = .boldSystemFont(ofSize: 20)
cell.textLabel?.numberOfLines = 0
cell.textLabel?.lineBreakMode = .byWordWrapping
cell.textLabel!.text = customerList.customer
return cell
}

Why does UITableView show the same view for each cell?

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")

Add section after 7 cell created in TableView - Swift

I have 2 arrays, one that is containing the sections and another one that is containing the elements for populate the cells of my TableView.
The question is: is it possible to create multiple sections with title after 7 cells?
For example, the array contains 14 elements and sections array 2 elements. I wish that at the beginning will appear "Section 1" then first 7 elements, then "Section 2" then the rest of the elements.
Thank you!
import UIKit
class ChallengesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var titleArray = [""]
var weekSection = ["1","2"]
override func viewDidLoad() {
super.viewDidLoad()
> let url = URL(string:"https://website.com/file.txt")!
> URLCache.shared.removeAllCachedResponses()
> let task = URLSession.shared.dataTask(with:url) { (data, response, error) in
> if error != nil {
> print(error!)
> }
> else {
> if let textFile = String(data: data!, encoding: .utf8) {
> DispatchQueue.main.async {
> self.titleArray = textFile.components(separatedBy: "\n")
> self.tableView.reloadData()
> print(self.titleArray)
> }
> }
> }
> }
> task.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titleArray.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return weekSection.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "challengeCell", for: indexPath) as! ChallengeTableViewCell
cell.labelTitle.text = titleArray[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch (section) {
case 0:
return "Case 0"
case 1:
return "Case 1"
default:
return "Default"
}
}
Update numberOfRowsInSection and cellForRowAt methods like this
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Case \(section)"
}
func numberOfSections(in tableView: UITableView) -> Int {
let lastSection = titleArray.count % 7 == 0 ? 0 : 1
return (titleArray.count/7) + lastSection
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let count = titleArray.count - (7*section)
return min(7,count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "challengeCell", for: indexPath) as! ChallengeTableViewCell
let rowIndex = (indexPath.section*7)+indexPath.row
cell.labelTitle.text = titleArray[rowIndex]
return cell
}
after the edit of the question, this is one of the best answers I think
extension Array {
func chunked(into size: Int) -> [[Element]] {
return stride(from: 0, to: count, by: size).map {
Array(self[$0 ..< Swift.min($0 + size, count)])
}
}
}
usage:
#IBOutlet weak var tableView: UITableView!
var titleArray: [[String]] = [[]]
var sectionsCount: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string:"https://website.com/file.txt")!
URLCache.shared.removeAllCachedResponses()
let task = URLSession.shared.dataTask(with:url) { (data, response, error) in
if error != nil {
print(error!)
}
else {
if let textFile = String(data: data!, encoding: .utf8) {
DispatchQueue.main.async {
let myArray = textFile.components(separatedBy: "\n")
self.sectionsCount = myArray.chunked(into: 7).count
self.tableView.reloadData()
print(self.titleArray)
}
}
}
}
task.resume()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sectionsCount
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titleArray[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "challengeCell", for: indexPath) as! ChallengeTableViewCell
let sectionArray = titleArray[indexPath.section]
cell.labelTitle.text = sectionArray[indexPath.row]
return cell
}

Multiple Sections In UITableView Swift 4

Well, I am trying to make a registration form. My problem is that in the section cell doesn't show the correct info into the sections, it shows all the info in the sections... ermm I think something I missed on the cell for row.
So, my code would look something like this.
UITableViewDataSource
extension IEKRegistrationPage3Controller: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "IEKRegistrationPage3GraduateCell", for: indexPath) as! IEKRegistrationPage3GraduateCell
// Graduate
if let graduate = Graduate(rawValue: indexPath.row) {
cell.updateLayout(withTitle: graduate.description, isSelected: [graduate] == iekRegistrationFormData.graduate)
}
if let student = Student.init(rawValue: indexPath.row) {
cell.updateLayout(withTitle: student.description, isSelected: [student] == iekRegistrationFormData.student)
}
return cell
}
}
Also here is the UITableViewDelegate
extension IEKRegistrationPage3Controller: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Graduate
tableView.deselectRow(at: indexPath, animated: true)
if indexPath.section == 0 {
if let graduate = Graduate(rawValue: indexPath.row) {
iekRegistrationFormData.graduate = [graduate]
}
}
// Student
tableView.deselectRow(at: indexPath, animated: true)
if indexPath.section == 1 {
if let student = Student(rawValue: indexPath.row) {
iekRegistrationFormData.student = [student]
}
}
// Engilsh
tableView.deselectRow(at: indexPath, animated: true)
if indexPath.section == 2 {
// TODO
}
tableView.reloadData()
}
}
and finally the titleForHeaderInSection
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
// Graduate
if section == 0 {
return "ΑΠΟΦΟΙΤΟΣ"
}
// Student
if section == 1 {
return "ΦΟΙΤΗΤΗΣ"
}
// Engilsh
if section == 2 {
return "ΑΓΓΛΙΚΑ"
}
return nil
}