I'm coding a GraphViewController
class that houses an array of graphs (of type LineChartView
). However, when I attempt to display these graphs in the format of cells of a collection view (using the called class GraphCell
), the LineChartView
objects don't seem to load any data, even though these functions are called inside the GraphViewController
class. Here are the relevant bits of my code so far:
class GraphViewController: UIViewController {
//lazy var only calculated when called
lazy var lineChartView: LineChartView = {
let chartView = LineChartView()
chartView.backgroundColor = .systemBlue
chartView.rightAxis.enabled = false //right axis contributes nothing
let yAxis = chartView.leftAxis
yAxis.labelFont = .boldSystemFont(ofSize: 12)
yAxis.setLabelCount(6, force: false)
yAxis.labelTextColor = .white
yAxis.axisLineColor = .white
yAxis.labelPosition = .outsideChart
let xAxis = chartView.xAxis
xAxis.labelPosition = .bottom
xAxis.labelFont = .boldSystemFont(ofSize: 12)
xAxis.setLabelCount(6, force: false)
xAxis.labelTextColor = .white
xAxis.axisLineColor = .systemBlue
chartView.animate(xAxisDuration: 1)
return chartView
}()
var graphColl: UICollectionView!
var graphs: [LineChartView] = []
var graphReuseID = "graph"
var homeViewController: ViewController = ViewController()
let dataPts = 50
var yValues: [ChartDataEntry] = []
var allWordsNoPins: [Word] = []
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setUpViews();
setUpConstraints()
}
func setUpViews(){
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = padding/3.2
layout.scrollDirection = .vertical
graphColl = UICollectionView(frame: .zero, collectionViewLayout: layout)
graphColl.translatesAutoresizingMaskIntoConstraints = false
graphColl.dataSource = self
graphColl.delegate = self
//must register GraphCell class before calling dequeueReusableCell
graphColl.register(GraphCell.self, forCellWithReuseIdentifier: graphReuseID)
graphColl.backgroundColor = .white
view.addSubview(graphColl)
print("stuff assigned")
assignData()
setData()
graphs.append(lineChartView)
}
One can assume setUpConstraints()
is working correctly, as the graph collection does show up. Here are all the functions that have to deal with the collection view I'm using:
//INSIDE GraphViewController
extension GraphViewController: UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
//collectionView.frame.height -
let size = 25*padding
let sizeWidth = collectionView.frame.width - padding/3
return CGSize(width: sizeWidth, height: size)
}
}
extension GraphViewController: UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return graphs.count //total number of entries
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let graphColl = collectionView.dequeueReusableCell(withReuseIdentifier: graphReuseID, for: indexPath) as! GraphCell
graphColl.configure(graph: graphs[indexPath.item])
return graphColl
}
}
and:
//configure function INSIDE GraphCell
func configure(graph: LineChartView){
chartView = graph
}
Here are the assignData()
and setData()
functions:
func setData(){
let set1 = LineChartDataSet(entries: yValues, label: "Word Frequency")
let data = LineChartData(dataSet: set1)
set1.drawCirclesEnabled = true
set1.circleRadius = 3
set1.mode = .cubicBezier //smoothes out curve
set1.setColor(.white)
set1.lineWidth = 3
set1.drawHorizontalHighlightIndicatorEnabled = false //ugly yellow line
data.setDrawValues(false)
lineChartView.data = data
}
func assignData(){
setUpTempWords()
let dataValues = allWordsNoPins
print(allWordsNoPins.count)
for i in 0...dataPts-1{
yValues.append(ChartDataEntry(x: Double(i), y: Double(dataValues[i].count)))
}
}
One can also assume the setUpTempWords()
function is working correctly, because of this screenshot below:
Here, I have plotted the lineChartView object of type LineChartView
directly on top of the GraphColl UICollectionView
variable inside my GraphViewController
class. The data is displayed. However, when I try to plot the same graph in my GraphCell class, I get
"No chart data available." I have traced the calls in my GraphViewController class, and based on the way the viewDidLoad()
function is set up, I can conclude that the lineChartView setup methods (assignData(), etc) are being called. For reference, here is my GraphCell class code:
import UIKit
import Charts
class GraphCell: UICollectionViewCell {
var chartView = LineChartView()
var graphCellBox: UIView!
override init(frame: CGRect){
super.init(frame: frame)
contentView.backgroundColor = .blue
chartView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(chartView)
graphCellBox = UIView()
graphCellBox.translatesAutoresizingMaskIntoConstraints = false
//graphCellBox.backgroundColor = cellorange
graphCellBox.layer.cornerRadius = 15.0
contentView.addSubview(graphCellBox)
setUpConstraints()
}
func setUpConstraints(){
NSLayoutConstraint.activate([
graphCellBox.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5),
graphCellBox.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
graphCellBox.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
graphCellBox.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
chartView.topAnchor.constraint(equalTo: graphCellBox.topAnchor),
chartView.bottomAnchor.constraint(equalTo: graphCellBox.bottomAnchor),
chartView.leadingAnchor.constraint(equalTo: graphCellBox.leadingAnchor),
chartView.trailingAnchor.constraint(equalTo: graphCellBox.trailingAnchor),
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(graph: LineChartView){
chartView = graph
}
}
As a side note, changing the lineChartView to a non-lazy (normal) variable does not fix this problem. I suspected the lazy declaration was the problem, since the graph would only be initialize if called, but this was not the case. Thank you for reading this post, and I'd greatly appreciate any direction or guidance!
Tough to test this without a reproducible example, but...
Assigning chartView = graph
looks problematic.
Try using your graphCellBoxgraphCellBox
as a "container" for the LineChartView
you're passing in with configure(...)
:
class GraphCell: UICollectionViewCell {
var graphCellBox: UIView!
override init(frame: CGRect){
super.init(frame: frame)
contentView.backgroundColor = .blue
graphCellBox = UIView()
graphCellBox.translatesAutoresizingMaskIntoConstraints = false
//graphCellBox.backgroundColor = cellorange
graphCellBox.layer.cornerRadius = 15.0
contentView.addSubview(graphCellBox)
setUpConstraints()
}
func setUpConstraints(){
NSLayoutConstraint.activate([
graphCellBox.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5),
graphCellBox.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
graphCellBox.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
graphCellBox.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(graph: LineChartView){
graph.translatesAutoresizingMaskIntoConstraints = false
graphCellBox.addSubview(graph)
NSLayoutConstraint.activate([
graph.topAnchor.constraint(equalTo: graphCellBox.topAnchor),
graph.bottomAnchor.constraint(equalTo: graphCellBox.bottomAnchor),
graph.leadingAnchor.constraint(equalTo: graphCellBox.leadingAnchor),
graph.trailingAnchor.constraint(equalTo: graphCellBox.trailingAnchor),
])
}
}