Reputation: 142
Working on a project where one Storyboard view requires two classes, a normal UIViewController class and an IAxisValueFormatter class that works with the iOS-Charts library to label one axis with string values. These classes in relevant part and in their original (before troubleshooting) form are shown:
import Foundation
import Charts
@objc(BarChartFormatter)
public class BarChartFormatter: NSObject, IAxisValueFormatter{
var dates: [String]!
public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return dates[Int(value)]
}
}
class SessionRevew: UIViewController {
@IBOutlet weak var HBCController: HorizontalBarChartView!
var dates: [String]!
The problem is that I have been unable to pass any values into the IAxisValueFormatter class. Passing values from the previous view controller to the SessionRevew class is painless and continues to work.
The following code from another view controller successfully passes the dates array to the UIViewController class SessionRevew:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "ReviewData") {
let datespass = segue.destination as! SessionRevew
datespass.dates = dates
}
}
However as soon as code is added to pass the same variable to the other class:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "ReviewData") {
let datespass1 = segue.destination as! BarChartFormatter
let datespass = segue.destination as! SessionRevew
datespass1.dates = dates
datespass.dates = dates
}
}
The code becomes in operable, as the activation of the segue triggers this error: EXC_BAD_INSTRUCTION error
Which occurs at the line: let datespass1 = segue.destination as! BarChartFormatter If anyone can help me understand why this is failing and how to pass the array to that class, this is the ideal answer. That said, the following represents some efforts to sidestep this error by sharing the variable from the SessionRevew class to the other class. We first created a secondary variable in the other class, which resulted in this:
@objc(BarChartFormatter)
public class BarChartFormatter: NSObject, IAxisValueFormatter{
var BCFdates = SessionRevew.dates
public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return BCFdates[Int(value)]
}
}
That code will not compile because of the error "Instance member 'dates' cannot be used on type SessionRevew", which occurs on the line:
var BCFdates = SessionRevew.dates
We tried nesting the above class inside of the SessionRevew class, which yielded the exact same error. In trying to resolve this error, we found the solution shown here: https://stackoverflow.com/a/32693285/6889264, and attempted to implement it like so:
import Foundation
import Charts
class SessionRevew: UIViewController {
@IBOutlet weak var HBCController: HorizontalBarChartView!
var dates: [String]!
@objc(BarChartFormatter)
public class BarChartFormatter: NSObject, IAxisValueFormatter{
var BCFdates: [String] {
get {
return {
SessionRevew.dates
}
}
}
public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return BCFdates[Int(value)]
}
}
}
Unfortunately, the error remains exactly the same ("Instance member 'dates' cannot be used on type SessionRevew") and the code does not compile. Un-nesting the class gives:
import Foundation
import Charts
@objc(BarChartFormatter)
public class BarChartFormatter: NSObject, IAxisValueFormatter{
var BCFdates: [String] {
get {
return {
SessionRevew.dates
}
}
}
public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return BCFdates[Int(value)]
}
}
class SessionRevew: UIViewController {
@IBOutlet weak var HBCController: HorizontalBarChartView!
var dates: [String]!
}
This code also generates the error code: "Instance member 'dates' cannot be used on type SessionRevew". Then tried to define the BCFdates variable as a read only computed property in accordance with this solution to a similar problem: https://stackoverflow.com/a/38651304/6889264
var BCFdates: [String] {
return SessionRevew{dates}
}
But the compile error remained. Any solutions or explanations that get this code working would be greatly appreciated. As mentioned before, it would be ideal if we could just pass the array right to the BarChartFormatter class and forget the sharing between class issue.
EDIT
The BarChartFormatter is used in the SessionRevew class by the function setChart in the following way:
func setChart(_ dataPoints: [String], values: [Double]) {
let formato:BarChartFormatter = BarChartFormatter()
for i in 0..<dataPoints.count {
let dataEntry = BarChartDataEntry(x: Double(i), yValues: [values[i]])
dataEntries.append(dataEntry)
formato.stringForValue(Double(i), axis: xaxis)
}
xaxis.valueFormatter = formato
HBCController.xAxis.valueFormatter = xaxis.valueFormatter
}
It's not referenced anywhere else in the code.
Upvotes: 1
Views: 240
Reputation: 142
As Phillip suggested, the solution is to forgo the attempt to pass the variable to the BarChartFormatter during the segue, and instead forward the variable in the setChart function.
Only one new line of code is required:
formato.dates = self.dates
This is added to the setChart function immediately after the line:
let formato:BarChartFormatter = BarChartFormatter()
The original BarChartFormatter code (shown below) then works perfectly with the dates array.
@objc(BarChartFormatter)
public class BarChartFormatter: NSObject, IAxisValueFormatter{
var dates: [String]!
public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return dates[Int(value)]
}
}
Also as Phillip indicated, it is impossible to pass the dates array to the BarChartFormatter class using the segue.destination code, so that must not be attempted. Thus this version of that code is the correct one:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "ReviewData") {
let datespass = segue.destination as! SessionRevew
datespass.dates = dates
}
}
Upvotes: 0