Does somebody have a good Tutorial for a Search Bar with Sections? I did not found a good one yet. Or maybe you can help me straight with my project!?
The section Title is static but i integrated some customized subtitles to show the regions and the total time i spend to make all this Locations(Liegenschaften) in this section. I create this values in the function createWinterdienstDetail. This values should not change when i search for one Location.
I get the Values for my tableview from a different ViewController as
var AllWinterdienstTourInfos: [Int:[Int:[String]]] = [:] // Section - Locationnumber - Locationinformations
Here is my ViewController File:
import UIKit
class WinterdienstTourVC: UIViewController {
var sections = ["Tour 1","Tour 2","Tour 3","Tour 4"]
var LiegenschaftDetail: [String] = []
// VARIABLEN WINTERDIENST
var WinterdienstregionTour1: [String] = []
...
var WinterdienstregionTour15: [String] = []
var WinterdienstaufwandTour1String: [String] = []
...
var WinterdienstaufwandTour15String: [String] = []
var WinterdienstaufwandTour1: [Double] = []
...
var WinterdienstaufwandTour15: [Double] = []
var Totaltouraufwand1 = Double()
...
var Totaltouraufwand15 = Double()
var AllWinterdienstTourInfos: [Int:[Int:[String]]] = [:]
// Initialisierung
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
createWinterdienstDetail()
tableView.delegate = self
tableView.dataSource = self
tableView.tableFooterView = UIView(frame: CGRect.zero)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "WinterdienstDetailSegue1") {
let DetailviewController = segue.destination as! WinterdienstLiegenschaftDetailVC
DetailviewController.LiegenschaftDetail = LiegenschaftDetail
}
}
func createWinterdienstDetail() {
if let liegenschaftenTour1 = AllWinterdienstTourInfos[0],
let liegenschaftenTour2 = AllWinterdienstTourInfos[1],
let liegenschaftenTour3 = AllWinterdienstTourInfos[2],
let liegenschaftenTour4 = AllWinterdienstTourInfos[3]{
self.myGroup.enter()
// DETAILS TOUR 1
for liegenschaften in liegenschaftenTour1.keys {
WinterdienstregionTour1.append(AllWinterdienstTourInfos[0]![liegenschaften]![6])
WinterdienstaufwandTour1String.append(AllWinterdienstTourInfos[0]![liegenschaften]![15])
for i in 0 ..< WinterdienstregionTour1.count-1 {
var j = WinterdienstregionTour1.count - 1
while(j > i) {
if WinterdienstregionTour1[i] == WinterdienstregionTour1[j] {
WinterdienstregionTour1.remove(at: j)
}
j -= 1
}
}
}
for WinterdienstaufwandTour1Array in WinterdienstaufwandTour1String {
WinterdienstaufwandTour1.append((WinterdienstaufwandTour1Array as NSString).doubleValue)
}
Totaltouraufwand1 = WinterdienstaufwandTour1.reduce(0,+)
// DETAILS TOUR 2
// DETAILS TOUR 3
// DETAILS TOUR 4
self.myGroup.leave()
self.myGroup.notify(queue: .main) {}
DispatchQueue.main.async {}
} // ENDE Function Create Winterdienst
} // ENDE CLASS WinterdienstTourVC
// DataSource-Methoden
extension WinterdienstTourVC: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int
{
return sections.count
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView()
// SECTION HEADER ALLGEMEIN
view.backgroundColor = UIColor.lightGray
// SECTION HEADER TOUR - NR
let tournr = UILabel()
tournr.text = sections[section]
tournr.font = UIFont.boldSystemFont(ofSize: 20.0)
tournr.frame = CGRect(x: 15,y:0, width: 100, height: 30)
view.addSubview(tournr)
// SECTION HEADER - TOURREGION / TOURAUFWAND
let tourregion = UILabel()
WinterdienstregionTour1.sort()
WinterdienstregionTour1.sort()
WinterdienstregionTour3.sort()
WinterdienstregionTour4.sort()
switch section{
case 0:
let tourregion1 = WinterdienstregionTour1.joined(separator: ", ")
tourregion.text = "\(tourregion1)"
case 1:
let tourregion2 = WinterdienstregionTour2.joined(separator: ", ")
tourregion.text = "\(tourregion2)"
.....
default:
tourregion.text = "Keine Angaben"
}
tourregion.font = UIFont.systemFont(ofSize: 16)
tourregion.frame = CGRect(x: 15,y:22, width: 200, height: 30)
view.addSubview(tourregion)
let touraufwand = UILabel()
switch section{
case 0:
touraufwand.text = "\(Totaltouraufwand1) h"
case 1:
touraufwand.text = "\(Totaltouraufwand2) h"
....
default:
touraufwand.text = "Keine Angaben"
}
touraufwand.frame = CGRect(x: -5,y:12, width: 370, height: 30)
touraufwand.font = UIFont.boldSystemFont(ofSize: 22.0)
touraufwand.textAlignment = .right
view.addSubview(touraufwand)
return view
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int
{
switch section{
case 0:
return self.AllWinterdienstTourInfos[0]!.count
// self.WinterdienstadressTourAll[0]!.count
case 1:
return self.AllWinterdienstTourInfos[1]!.count
//self.WinterdienstAlltourinfosAll[1]![section]!.count
....
default:
return 1
}
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath)
-> UITableViewCell
{
// versuchen, eine Zelle wiederzuverwenden
var cell = tableView.dequeueReusableCell(
withIdentifier: "cell")
if cell == nil {
// nicht möglich, daher neue Zelle erzeugen
cell = UITableViewCell(
style: .default, reuseIdentifier: "cell")
}
// Eigenschaften der Zelle einstellen
cell!.textLabel!.text = AllWinterdienstTourInfos[indexPath.section]![indexPath.row]?[4]
cell!.textLabel!.adjustsFontSizeToFitWidth = true
cell!.textLabel!.textAlignment = .left
// Zelle zurückgeben
return cell!
}
}
// TableView-Delegates
extension WinterdienstTourVC: UITableViewDelegate {
func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath)
{
LiegenschaftDetail = AllWinterdienstTourInfos[indexPath.section]![indexPath.row]!
performSegue(withIdentifier: "WinterdienstDetailSegue1", sender: self)
}
}
Related
I'm setting up a collapsable tableView, but something strange happens on the collapsable item. When you look at the video keep an eye on the "Where are you located" line.. (I'm using a .plist for the question and answer items)
Where do I go wrong, is it somewhere in my code? I don't want to let that line stick on the top :(
Here is the code I'm using but I can't find anything strange...
class FAQViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var questionsArray = [String]()
var answersDict = Dictionary<String, [String]>() // multiple answers for a question
var collapsedArray = [Bool]()
#IBOutlet weak var tableView: UITableView!
override func viewWillAppear(_ animated: Bool) {
// Hide the navigation bar on the this view controller
self.navigationController?.setNavigationBarHidden(true, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
addTableStyles()
readQAFile()
tableView.delegate = self
tableView.dataSource = self
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
func addTableStyles(){
navigationController?.isNavigationBarHidden = false
self.tableView?.backgroundView = {
let view = UIView(frame: self.tableView.bounds)
return view
}()
tableView.estimatedRowHeight = 43.0;
tableView.rowHeight = UITableView.automaticDimension
tableView.separatorStyle = UITableViewCell.SeparatorStyle.singleLine
}
func readQAFile(){
guard let url = Bundle.main.url(forResource: "QA", withExtension: "plist")
else { print("no QAFile found")
return
}
let QAFileData = try! Data(contentsOf: url)
let dict = try! PropertyListSerialization.propertyList(from: QAFileData, format: nil) as! Dictionary<String, Any>
// Read the questions and answers from the plist
questionsArray = dict["Questions"] as! [String]
answersDict = dict["Answers"] as! Dictionary<String, [String]>
// Initially collapse every question
for _ in 0..<questionsArray.count {
collapsedArray.append(false)
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return questionsArray.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if collapsedArray[section] {
let ansCount = answersDict[String(section)]!
return ansCount.count
}
return 0
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
// Set it to any number
return 70
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if collapsedArray[indexPath.section] {
return UITableView.automaticDimension
}
return 2
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView(frame: CGRect(x:0, y:0, width:tableView.frame.size.width, height:40))
headerView.tag = section
let headerString = UILabel(frame: CGRect(x: 10, y: 10, width: tableView.frame.size.width, height: 50)) as UILabel
headerString.text = "\(questionsArray[section])"
headerView .addSubview(headerString)
let headerTapped = UITapGestureRecognizer (target: self, action:#selector(sectionHeaderTapped(_:)))
headerView.addGestureRecognizer(headerTapped)
return headerView
}
#objc func sectionHeaderTapped(_ recognizer: UITapGestureRecognizer) {
let indexPath : IndexPath = IndexPath(row: 0, section:recognizer.view!.tag)
if (indexPath.row == 0) {
let collapsed = collapsedArray[indexPath.section]
collapsedArray[indexPath.section] = !collapsed
//reload specific section animated
let range = Range(NSRange(location: indexPath.section, length: 1))!
let sectionToReload = IndexSet(integersIn: range)
self.tableView.reloadSections(sectionToReload as IndexSet, with:UITableView.RowAnimation.fade)
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cellIdentifier = "Cell"
let cell: UITableViewCell! = self.tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
cell.textLabel?.adjustsFontSizeToFitWidth = true
cell.textLabel?.numberOfLines = 0
let manyCells : Bool = collapsedArray[indexPath.section]
if (manyCells) {
let content = answersDict[String(indexPath.section)]
cell.textLabel?.text = content![indexPath.row]
}
return cell
}
override var prefersStatusBarHidden: Bool {
return true
}
}
You need to change the style of the tableView to grouped, when you initialize it:
let tableView = UITableView(frame: someFrame, style: .grouped)
or from Storyboard:
After that you will have this issue, which I solved by setting a tableHeaderView to the tableView that has CGFloat.leastNormalMagnitude as its height:
override func viewDidLoad() {
super.viewDidLoad()
var frame = CGRect.zero
frame.size.height = .leastNormalMagnitude
tableView.tableHeaderView = UIView(frame: frame)
}
Just remove your headerView from view hierarchy here
#objc func sectionHeaderTapped(_ recognizer: UITapGestureRecognizer) {
headerView.removeFromSuperview()
...
}
By the way, yes creating a openable tableview menu with using plist is one of the methods but it could be more simple. In my opinion you should refactor your code.
I added an arrow to fold the section when clicked, when I fold the last section (e.g. setting the row height to 0 in cellForRow, all the rows in the last section get mixed up, as can be seen in the image below:
Can anyone suggest any reason why this should happen if I'm setting the height of the row to 0?
Here's the relevant code:
viewForHeaderInSection:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
let profileCompletion = clientFields?.profileCompletion ?? 0
headerView.profileComplete.text = "Profile".localized() + " \(profileCompletion)% " + "Complete".localized()
headerView.profileProgress.progress = Float(profileCompletion) * 0.01
headerView.profileProgress.progressTintColor = Constants.Client.getProfileCompletenessColor(profileCompletion)
headerView.delegate = self
headerView.section = section
headerView.isOpen = self.sectionsStatuses[section].shouldDisplaySection
return headerView
}
else {
let height = (section == 0) ? FIRST_HEADER_HEIGHT : HEADER_HEIGHT
let nib = UINib(nibName: "ClientDetailsSectionHeaderCell", bundle: nil)
guard let returnedView = nib.instantiate(withOwner: self, options: nil)[0] as? ClientDetailsSectionHeaderCell else {
return UIView()
}
returnedView.delegate = self
returnedView.section = section
returnedView.isOpen = self.sectionsStatuses[section].shouldDisplaySection
returnedView.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: height)
returnedView.heightConstraint.constant = height
returnedView.mainView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
returnedView.mainView.backgroundColor = Constants.ParentForm.headerBgColor
// Draw separators
if DRAW_SECTION_SEPARATORS || DRAW_SECTION_TOP_SEPARATOR {
returnedView.createTopSeparator(color: Constants.ParentForm.separatorColor)
}
if DRAW_SECTION_SEPARATORS || DRAW_SECTION_BOTTOM_SEPARATOR {
returnedView.createBottomSeparator(color: Constants.ParentForm.separatorColor)
}
if isSectionTitleHidden == false {
let xOffset = HEADER_X_OFFSET
let yOffset = section == 0 ? FIRST_HEADER_Y_OFFSET : HEADER_Y_OFFSET
returnedView.title.frame = CGRect(x: xOffset, y: yOffset, width: view.frame.size.width - xOffset, height: height - yOffset)
returnedView.title.text = self.sectionsArray[section].uppercased().localized()
returnedView.title.font = Constants.ParentForm.headerFont
returnedView.title.textColor = Constants.ParentForm.headerTextColor
returnedView.title.sizeToFit()
}
return returnedView
}
}
heightForRow
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if sectionsStatuses[indexPath.section].shouldDisplaySection {
return super.tableView(tableView, heightForRowAt: indexPath)
}
return CGFloat(0)
}
cellForRow
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if self.sectionsArray[indexPath.section] != "ADDRESSES" {
if let clientFields = self.clientFields {
self.dataMap = Utils.convertObjectToMap(item: clientFields)
let birthdayDate = clientFields.birthdayDateString
self.dataMap["birthdayDate"] = birthdayDate
let createdDate = clientFields.createdDateString
self.dataMap["createdDate"] = createdDate
}
}
else {
if let clientAddresses = self.clientAddresses, clientAddresses.count > 0 {
self.dataMap = Utils.convertObjectToMap(item: clientAddresses[Int(floor(Double(indexPath.row) / 8.0))])
}
}
return super.tableView(tableView, cellForRowAt: indexPath)
}
protocol function
func openCloseSection(openSection: Bool, section: Int) {
self.sectionsStatuses[section].shouldDisplaySection = openSection
self.tableView.reloadData()
}
ClientDetailsSectionHeaderCell
import UIKit
protocol SectionViewerProtocol {
func openCloseSection(openSection: Bool, section: Int)
}
class ClientDetailsSectionHeaderCell: UITableViewCell {
#IBOutlet weak var mainView: UIView!
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
#IBOutlet weak var accessoryButton: UIButton!
#IBOutlet weak var title: UILabel!
var section = 0
var delegate: SectionViewerProtocol?
var isOpen: Bool? {
didSet {
if let isOpen = self.isOpen {
accessoryButton.setImage(UIImage(named: isOpen ? "fill588": "fill589"), for: .normal)
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func openCloseSection(_ sender: UIButton) {
if let isOpen = self.isOpen {
self.isOpen = !isOpen
delegate?.openCloseSection(openSection: !isOpen, section: section)
}
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Try setting clipsToBounds of your cell or any enclosing UIView to true.
I'm using iOS Charts in my project, and I've been able to embed my charts in a tableview (every cell will contain a chart, up to maximum number of possible entries to keep under control the performance).
I would like to use the 'touchMatrix' feature in order zoom the x axis on one chart (in a cell) and have all of the other visible charts to follow along with the zoom scale (the x axis), possibly with a vertical axis showing the Y value on every visible chart for that x coordinate, but I'm kind of stuck, since even the delegate methods such as 'chartValueNothingSelected' are not working (I probably got it wrong when I tried to associate the delegate to the chartview, since the chart it's embedded in the cell, and it's not the tipical case of associating the delegate to a view controller).
Assigning the delegate to the cell just doesn't seem to be working.
Any tips?
Thanks in advance.
Here is the code:
import UIKit
import Charts
class CustomChartCell: UITableViewCell, ChartViewDelegate {
#IBOutlet weak var lineChartView: LineChartView!
var yValues = [Double]()
var xValues = [Double]()
var chartColor = UIColor()
var channelName = ""
var yMin = 0.0
var yMax = 0.0
var yAvg = 0.0
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.lineChartView.noDataFont = UIFont.systemFont(ofSize: 28.0)
self.lineChartView.noDataText = NSLocalizedString("No chart data available", comment: "No Chart Data Available Title")
self.lineChartView.xAxis.labelPosition = .bottom
self.lineChartView.highlightPerTapEnabled = true
self.lineChartView.autoScaleMinMaxEnabled = true
self.lineChartView.chartDescription?.enabled = true
self.lineChartView.dragEnabled = true
self.lineChartView.setScaleEnabled(true)
self.lineChartView.pinchZoomEnabled = false
self.lineChartView.xAxis.gridLineDashLengths = [10, 10]
self.lineChartView.xAxis.gridLineDashPhase = 0
self.lineChartView.leftAxis.gridLineDashLengths = [5, 5]
self.lineChartView.leftAxis.drawLimitLinesBehindDataEnabled = true
self.lineChartView.rightAxis.enabled = false
let marker = BalloonMarker(color: UIColor(white: 180/255, alpha: 1),
font: .systemFont(ofSize: 12),
textColor: .white,
insets: UIEdgeInsets(top: 8, left: 8, bottom: 20, right: 8))
marker.chartView = self.lineChartView
marker.minimumSize = CGSize(width: 80, height: 40)
self.lineChartView.marker = marker
self.lineChartView.legend.form = .line
self.lineChartView.delegate = self
}
func setChart(_ xValues: [Double], _ yValues: [Double]) {
var dataEntries: [ChartDataEntry] = []
for i in 0..<xValues.count {
let dataEntry = ChartDataEntry(x: xValues[i], y: yValues[i])
dataEntries.append(dataEntry)
}
let chartDataSet = LineChartDataSet(values: dataEntries, label: self.channelName)
let chartData = LineChartData(dataSets: [chartDataSet])
chartDataSet.setColor(self.chartColor.withAlphaComponent(0.5))
chartDataSet.drawCirclesEnabled = false
chartDataSet.lineWidth = 2
chartDataSet.drawValuesEnabled = false
chartDataSet.highlightEnabled = true
chartDataSet.drawFilledEnabled = false
lineChartView.data = chartData
self.lineChartView!.leftAxis.axisMinimum = self.yValues.min()! - self.yValues.max()!*0.2
self.lineChartView!.leftAxis.axisMaximum = self.yValues.max()! + self.yValues.max()!*0.2
self.lineChartView!.chartDescription?.font = UIFont.systemFont(ofSize: 11.0)
self.lineChartView!.chartDescription?.text = "y_min: \(self.yMin), y_max: \(self.yMax), y_avg: \(self.yAvg)"
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
class ChartTableViewController: UITableViewController, ChartViewDelegate {
var dataSet : [LineChartData] = []
let data = LineChartData()
var channelNames : [String] = []
let channelColors = [UIColor.blue, UIColor.green, UIColor.red,
UIColor.orange, UIColor.purple, UIColor.darkGray]
var averageValues : [Double] = []
var minValues : [Double] = []
var maxValues : [Double] = []
var xValues : [[Double]] = []
var yValues : [[Double]] = []
#IBAction func chartZoomOut(_ sender: Any) {
tableView?.visibleCells.forEach { cell in
if let cell = cell as? CustomChartCell {
cell.lineChartView.zoomOut()
}
}
}
#IBAction func chartZoom100(_ sender: Any) {
tableView?.visibleCells.forEach { cell in
if let cell = cell as? CustomChartCell {
cell.lineChartView.zoomToCenter(scaleX: 0, scaleY: 0)
}
}
}
#IBAction func chartZoomIn(_ sender: Any) {
tableView?.visibleCells.forEach { cell in
if let cell = cell as? CustomChartCell {
cell.lineChartView.zoomIn()
}
func chartValueNothingSelected(_ chartView: ChartViewBase) {
chartView.chartDescription?.text = ""
}
func chartScaled(chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) {
print(scaleX)
print(scaleY)
}
func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
chartView.chartDescription?.text = "x: \(entry.x), y: \(entry.y), y_min: \(minValues[0]), y_max: \(maxValues[0]), y_avg: \(averageValues[0])"
chartView.reloadInputViews()
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.isEditing = false
self.editButtonItem.title = NSLocalizedString("Edit", comment: "Edit Title")
self.tableView.separatorColor = UIColor.clear
self.navigationItem.rightBarButtonItem = self.editButtonItem
self.loadFile()
self.tableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tableView.isEditing = false
self.editButtonItem.title = NSLocalizedString("Edit", comment: "Edit Title")
}
// MARK: Load Chart File
func loadFile() {
// Try to load the file content
do {
// get the documents folder url
let documentDirectoryURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
// create the destination url for the text file to be saved
let fileDestinationUrl = documentDirectoryURL.appendingPathComponent(Constants.CHART_FILE_NAME)
// reading from disk
do {
let file = try String(contentsOf: fileDestinationUrl)
let newFile = file.replacingOccurrences(of: "\r", with: "") // Get rid of additional new lines
let cleanChannels = self.generateCleanChannels(newFile) // // a function used to retrieve the channels from the file (since the file it's not a plain CSV file but something different)
channelNames.removeAll()
dataSet.removeAll()
channelNames = self.getChannelNames(newFile) // a function used to retrieve the channel names from the file (since the file it's not a plain CSV file but something different)
var lineChartEntry = [ChartDataEntry]()
data.clearValues()
for (index,channel) in cleanChannels.enumerated() {
if (index <= Constants.MAX_CHART_CHANNELS) {
let csvImage = CSV(string: channel, delimiter: ";")
var y_max = 0.0
var firstXValueSampled : Bool = false
var firstXValue : Double = 0.0
var average = 0.0
var min = 0.0
var max = 0.0
var count = 0
var new_xValues : [Double] = []
var new_yValues : [Double] = []
csvImage.enumerateAsDict { dict in
print(dict["X_Value"]!)
new_xValues.append(Double(dict["X_Value"]!)!)
new_yValues.append(Double(dict["Y_Value"]!)!)
if !firstXValueSampled {
firstXValueSampled = true
firstXValue = Double(dict["X_Value"]!)!
}
average = average + Double(dict["Y_Value"]!)!
count = count + 1
let value = ChartDataEntry(x: (Double(dict["X_Value"]!)! - firstXValue), y: Double(dict["Y_Value"]!)! )
lineChartEntry.append(value)
if Double(dict["Y_Value"]!)! > max {
max = Double(dict["Y_Value"]!)!
}
if Double(dict["Y_Value"]!)! < min {
min = Double(dict["Y_Value"]!)!
}
if Double(dict["Y_Value"]!)! > y_max {
y_max = Double(dict["Y_Value"]!)!
}
}
xValues.append(new_xValues)
yValues.append(new_yValues)
average = average / Double(count)
averageValues.append(average)
minValues.append(min)
maxValues.append(max)
let line = LineChartDataSet(values: lineChartEntry, label: channelNames[index])
line.axisDependency = .left
line.colors = [channelColors[index]] // Set the color
line.setColor(channelColors[index].withAlphaComponent(0.5))
line.setCircleColor(channelColors[index])
line.lineWidth = 2.0
line.circleRadius = 3.0
line.fillAlpha = 65 / 255.0
line.fillColor = channelColors[index]
line.highlightColor = UIColor(red: 244/255, green: 117/255, blue: 117/255, alpha: 1)
line.drawCircleHoleEnabled = false
line.highlightLineWidth = 2.0
line.drawHorizontalHighlightIndicatorEnabled = true
data.setValueFont(.systemFont(ofSize: 9))
data.addDataSet(line) // Add the line to the dataSet
let newData = LineChartData()
newData.setValueFont(.systemFont(ofSize: 9))
newData.addDataSet(line)
dataSet.append(newData)
}
}
} catch let error as NSError {
print("error loading contentsOf url \(fileDestinationUrl)")
print(error.localizedDescription)
}
} catch let error as NSError {
print("error getting documentDirectoryURL")
print(error.localizedDescription)
}
}
// MARK: - Table view data source
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 dataSet.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "CustomChartCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier,
for: indexPath) as! CustomChartCell
cell.lineChartView.delegate = self
if dataSet.count > 0 {
cell.xValues = xValues[indexPath.row]
cell.yValues = yValues[indexPath.row]
cell.chartColor = channelColors[indexPath.row]
cell.channelName = channelNames[indexPath.row]
cell.yMin = minValues[indexPath.row]
cell.yMax = maxValues[indexPath.row]
cell.yAvg = averageValues[indexPath.row]
cell.lineChartView.leftAxis.axisMaximum = cell.yValues.max()! + 1
cell.lineChartView.leftAxis.axisMinimum = cell.yValues.min()! - 1
cell.setChart(cell.xValues, cell.yValues)
} else {
cell.lineChartView.clearValues()
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//tableView.deselectRow(at: indexPath, animated: true)
let selectedCell = tableView.cellForRow(at: indexPath) as! CustomChartCell
selectedCell.contentView.backgroundColor = UIColor.lightGray
//let currentMatrix = selectedCell.lineChartView.viewPortHandler.touchMatrix
//let selectedCell:UITableViewCell = tableView.cellForRow(at: indexPath)! as! CustomChartCell
//selectedCell.contentView.backgroundColor = UIColor.lightGray
//let currentMatrix = selectedCell.lineChartView.viewPortHandler.touchMatrix
/*
tableView.visibleCells.forEach { cell in
if let cell = cell as? CustomChartCell {
if cell.channelName != selectedCell.channelName {
cell.lineChartView.viewPortHandler.refresh(newMatrix: currentMatrix, chart: cell.lineChartView, invalidate: true)
}
}
}
*/
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 250
}
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
return .none
}
override func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
return false
}
override func setEditing (_ editing:Bool, animated:Bool)
{
super.setEditing(editing,animated:animated)
if (self.isEditing) {
//self.editButtonItem.title = "Editing"
self.editButtonItem.title = NSLocalizedString("Done", comment: "Done Title")
}
else {
self.editButtonItem.title = NSLocalizedString("Edit", comment: "Edit Title")
}
}
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let movedObject = self.dataSet[sourceIndexPath.row]
dataSet.remove(at: sourceIndexPath.row)
dataSet.insert(movedObject, at: destinationIndexPath.row)
}
}
i have a textfield, when the user tap on it, a table1 appear with a list of countries currency, then the user choose a currency and add it to table2, i can add one currency , when i add another one to table2 the app crash with the following error: Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'request for rect at invalid index path ( {length = 2, path = 0 - 1})'
class CurrencySecondStep: UIViewController ,UITableViewDataSource, UITableViewDelegate,UITextFieldDelegate {
#IBOutlet weak var tableViewAddedByUser: UITableView!
#IBOutlet weak var amountToBeExchanged: UILabel!
#IBOutlet weak var tableViewFor: UITableView!
#IBOutlet weak var textFieldFor: UITextField!
//cell identifier
let cellReuseIdentifier = "cell"
//struct
struct Currency {
var country = String()
var currencyCode = String()
var currencyFlag = UIImage()
}
var addedCurrencyByUserArray = [Currency]()
var currencyArr = [
Currency(country: "United Arab Emirates Dirham", currencyCode: "AED",currencyFlag: UIImage(named:"United-Arab-Emirates")!),
Currency(country: "US Dollar", currencyCode: "USD",currencyFlag: UIImage(named:"United-States")!),
Currency(country: "Afghan Afghani (1927–2002)", currencyCode: "AFA",currencyFlag: UIImage(named:"Afghanistan")!),
Currency(country: "Albania Lek", currencyCode: "ALL",currencyFlag: UIImage(named:"Albania")!),
Currency(country: "Algerian Dinar", currencyCode: "DZD",currencyFlag: UIImage(named:"Algeria")!),
Currency(country: "Angolan Kwanza", currencyCode: "AOA",currencyFlag: UIImage(named:"Angola")!),
Currency(country: "Argentine Peso", currencyCode: "ARS",currencyFlag: UIImage(named:"Argentina")!),
Currency(country: "Armenian Dram", currencyCode: "AMD",currencyFlag: UIImage(named:"Armenia")!)
]
//searched results
var filteredCurrenciesOffer = [Currency]()
//currency code to be sent to google finance
var currencyCodeOffer = ""
var currencyPassed = ""
var countryChoice = ""
var countryCodeChoice = ""
var countryflagChoice = UIImage()
#IBAction func textFieldChanged(_ sender: AnyObject) {
tableViewFor.isHidden = true
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableViewFor.delegate = self
tableViewFor.dataSource = self
tableViewFor.isHidden = true
textFieldFor.delegate = self
textFieldFor.addTarget(self, action: #selector(textFieldActive), for: UIControlEvents.touchDown)
textFieldFor.addTarget(self, action:#selector(textFieldDidChange) , for: UIControlEvents.editingChanged)
//hide or show keyboard
textFieldFor.addTarget(self, action: #selector(textFieldShouldReturn), for: UIControlEvents.touchDown)
amountToBeExchanged.text = currencyPassed
//table added By User
tableViewAddedByUser.delegate = self
tableViewAddedByUser.dataSource = self
tableViewAddedByUser.isHidden = true
}
when the user press add Button to insert the second element the app crash
#IBAction func addPressed(_ sender: Any) {
if(textFieldFor.text == ""){
displayError(title: "Currency Type",message:"Please Pick Your Currency")
}else{
tableViewAddedByUser.isHidden = false
addedCurrencyByUserArray.append(Currency(country: countryChoice, currencyCode: countryCodeChoice,currencyFlag: countryflagChoice))
tableViewAddedByUser.beginUpdates()
let insert = IndexPath(row: addedCurrencyByUserArray.count - 1, section: 0)
tableViewAddedByUser.insertRows(at: [insert], with: .automatic)
tableViewAddedByUser.endUpdates()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: TextField Functions
func textFieldDidEndEditing(_ textField: UITextField) {
filteredCurrenciesOffer = currencyArr.filter { currencyGiven in
return currencyGiven.country.lowercased().contains(textFieldFor.text!.lowercased())
}
//reload table based on the user pick
tableViewFor.reloadData()
}
func textFieldDidChange(textField: UITextField) {
//update search based on user search
filteredCurrenciesOffer = currencyArr.filter { currencyGiven in
return currencyGiven.country.lowercased().contains(textFieldFor.text!.lowercased())
}
tableViewFor.reloadData()
}
// Toggle the tableView visibility when click on textField
func textFieldActive(textField: UITextField) {
tableViewFor.isHidden = !tableViewFor.isHidden
}
//hide keyboard
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
// MARK: TableView Functions
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count:Int?
if (tableView == self.tableViewFor && textFieldFor.text != ""){
count = filteredCurrenciesOffer.count
}else if(tableView == self.tableViewFor && textFieldFor.text == ""){
count = currencyArr.count
}
if(tableView == self.tableViewAddedByUser){
count = addedCurrencyByUserArray.count
print("number of rows")
print(count!)
}
return count!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableViewFor.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomCell
var currency: Currency
if (tableView == self.tableViewFor){
if ( textFieldFor.text != "") {
currency = filteredCurrenciesOffer[indexPath.row]
} else {
currency = currencyArr[indexPath.row]
}
cell.flag.image = currency.currencyFlag
cell.country.text = currency.country
cell.currencyCode.text = currency.currencyCode
}
if (tableView == self.tableViewAddedByUser){
currency = addedCurrencyByUserArray[indexPath.row]
cell.flag.image = currency.currencyFlag
cell.country.text = currency.country
cell.currencyCode.text = currency.currencyCode
}
return cell
}
// MARK: UITableViewDelegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var currency: Currency
if(tableView == tableViewFor){
if textFieldFor.text != "" {
currency = filteredCurrenciesOffer[indexPath.row]
} else {
currency = currencyArr[indexPath.row]
}
//add image to currency text field
let leftImageView = UIImageView()
leftImageView.image = currency.currencyFlag
let leftView = UIView()
leftView.addSubview(leftImageView)
leftView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
leftImageView.frame = CGRect(x: 10, y: 10, width: 20, height: 20)
textFieldFor.leftViewMode = .always
textFieldFor.leftView = leftView
//add country to currency text field
textFieldFor.text = currency.country
countryChoice = currency.country
countryCodeChoice = currency.currencyCode
countryflagChoice = currency.currencyFlag
currencyCodeOffer = currency.currencyCode
tableViewFor.isHidden = true
textFieldFor.endEditing(true)
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0.0
}
//hide the table and keyboard
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
guard let touch:UITouch = touches.first else
{
return;
}
if touch.view != tableViewFor
{
textFieldFor.endEditing(true)
tableViewFor.isHidden = true
}
}
func displayError(title:String,message:String){
// Create the alert controller
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
// Create the actions
let retryButton = UIAlertAction(title: "Ok", style: UIAlertActionStyle.cancel) {
UIAlertAction in
NSLog("Cancel Pressed")
}
// Add the actions
alertController.addAction(retryButton)
}
}
I think the problem happens in your
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
On the first line you always dequeue a cell from the tableViewFor even when you need a cell for tableViewAddedByUser.
Try replacing the first line with this which will dequeue a cell from the tableView sent in the parameters instead:
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomCell
If that doesn't work then try without specifying the indexPath like so:
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CustomCell
I have an expandable table view and fill it with static arrays and that works fine, But if I want to fill this table with NSDictionary or NSarrays, then what should I do !?
This is my first time with expandable table view so please guide me.
Here is my entire code:
import UIKit
class MenuViewController: UIViewController, UITableViewDataSource{
#IBOutlet weak var expandTableView: UITableView!
var expandedSections : NSMutableSet = []
var sectionData : [String] = ["Vegs ", "Fruits ", "Birds ", "Reptiles "]
var row1 = ["Tomato", "Onion", "Potato", "Pumpkin", "Babycorn", "Cucumber", "Carrot", "Beans", "Cabbage", "Corn", "EggPlant", "Pea", "Sweet Potato"]
var row2 = ["Mango", "Orange", "Watermelon", "Apple", "Apricot", "Banana", "Papaya", "Pineapple", "Melon", "Avocado", "Cherry", "Date", "Fig", "Grape", "Guava", "Kiwi", "Olive", "Pear"]
var row3 = ["Parrot", "Peacock", "Woodpecker", "Kingfisher", "Owl", "Eagle", "Pigeon", "Vulture", "Bats", "Nightingale", "Crow"]
var row4 = ["Lizard", "Crocodile", "Snake", "Turtle", "Dinosaur"]
func sectionTapped(_ sender: UIButton) {
let section = sender.tag
let shouldExpand = !expandedSections.contains(section)
if (shouldExpand) {
expandedSections.removeAllObjects()
expandedSections.add(section)
} else {
expandedSections.removeAllObjects()
}
self.expandTableView.reloadData()
}
} // end of class
extension MenuViewController: UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return sectionData.count
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView.init(frame: CGRect(x: 0, y: 0, width: 300, height: 28))
var imageView = UIImageView()
let headerTitle = UILabel.init(frame: CGRect(x: 38, y: 4, width: 250, height: 28))
headerTitle.text = sectionData[section]
headerTitle.textAlignment = .right
let tappedSection = UIButton.init(frame: CGRect(x: 0, y: 0, width: headerView.frame.size.width, height: headerView.frame.size.height))
tappedSection.addTarget(self, action: #selector(sectionTapped), for: .touchUpInside)
tappedSection.tag = section
headerView.addSubview(imageView)
headerView.addSubview(headerTitle)
headerView.addSubview(tappedSection)
headerView.tintColor = UIColor.white
return headerView
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(expandedSections.contains(section)) {
switch section {
case 0:
return row1.count
case 1:
return row2.count
case 2:
return row3.count
default:
return row4.count
}
} else {
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "cell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
cell?.textLabel?.textAlignment = .right
switch indexPath.section {
case 0:
cell?.textLabel?.text = row1[indexPath.row]
case 1:
cell?.textLabel?.text = row2[indexPath.row]
case 2:
cell?.textLabel?.text = row3[indexPath.row]
default:
cell?.textLabel?.text = row4[indexPath.row]
}
return cell!;
}
}
Here are my new values:
var expandedSections : NSMutableSet = []
var categoryLevel1 = [Category]()
var categoryLevel2 = [[Category]]()