I am using Charts library for rendering line chart but I am unable to reload the data as I am trying to fetch the data from the API, there is a method given notifyDataSetChanged() but not working. I am using the chart within tableView cell. If anybody has some idea please help me out..............................................
import UIKit
import Charts
class ChartViewCell: UITableViewCell, ChartViewDelegate {
#IBOutlet weak var lineChartViewContainer: UIStackView?
var yVlaues = [ChartDataEntry]()
lazy var lineChartView: LineChartView = {
let chartView = LineChartView()
return chartView
}()
override func awakeFromNib() {
super.awakeFromNib()
setLineChart()
loadData()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func setLineChart() {
lineChartView.delegate = self
lineChartView.noDataText = "No Data Found"
lineChartView.rightAxis.enabled = false
lineChartView.xAxis.drawGridLinesEnabled = false
lineChartView.leftAxis.drawGridLinesEnabled = false
lineChartView.legend.enabled = false
//lineChartView.leftAxis.labelFont = .boldSystemFont(ofSize: 14)
lineChartView.leftAxis.labelCount = 6
lineChartView.leftAxis.labelTextColor = .black
lineChartView.xAxis.labelPosition = .bottom
let screenSize = UIScreen.main.bounds
let screenWidth = screenSize.width
lineChartView.frame = CGRect(x: 10, y: 70, width: screenWidth - 20, height: 200)
setData()
self.addSubview(lineChartView)
}
func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
print("Abhay x \(entry.x) y \(entry.y)")
}
func setData() {
let set1 = LineChartDataSet(entries: yVlaues)
set1.lineWidth = 1
set1.colors = [UIColor.colorFromHex(hexString: "#80005661")]
set1.drawVerticalHighlightIndicatorEnabled = true
set1.drawHorizontalHighlightIndicatorEnabled = false
set1.highlightColor = UIColor.colorFromHex(hexString: "#80005661")
set1.highlightLineWidth = 1.0
set1.drawValuesEnabled = false
set1.circleHoleColor = .white
set1.circleColors = [UIColor.colorFromHex(hexString: "#80005661")]
set1.circleRadius = 5
let data = LineChartData(dataSet: set1)
lineChartView.data = data
let startColor = UIColor.colorFromHex(hexString: "#80005661").cgColor
let endColor = UIColor.white.cgColor
let gradientColors = [startColor, endColor] as CFArray // Colors of the gradient
let colorLocations:[CGFloat] = [1.0, 0.0] // Positioning of the gradient
let gradient = CGGradient.init(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: gradientColors, locations: colorLocations) // Gradient Object
if let gradient = gradient {
set1.fill = Fill.fillWithLinearGradient(gradient, angle: 90.0) // Set the Gradient
}
set1.drawFilledEnabled = true // Draw the Gradient
}
func loadData() {
var params = [String: Any]()
guard let userId = Reusable.getUserInfo()?.id else { return }
params = ["user_id": userId]
WebServiceHelper.postWebServiceCall(Constants.baseURL + "performedWorkoutsGraph", params: params, isShowLoader: false, success: { (responceObj) in
let statusMsg = StatusBool(json: responceObj)
if statusMsg.status {
self.yVlaues.removeAll()
let responseJsonArr = responceObj["data"].arrayValue
for item in responseJsonArr {
let graphData = GraphDataModel(json: item)
let chartDataEntry = ChartDataEntry(x: Double(graphData.interval), y: Double(graphData.workouts))
self.yVlaues.append(chartDataEntry)
}
self.lineChartView.data?.notifyDataChanged()
self.lineChartView.notifyDataSetChanged()
} else {
CommonUtils.showToastMessage(message: statusMsg.message)
}
}
, failure: { (failure) in
print(failure)
})
}
}
I just found the solution but not the reloading tricks I m calling setData() inside my loadData() and it's working
Related
I am trying to build a graph with 2 LineChartDataSet. At the first time, I build with one and then on every selected value, I want to do some different color to the right data set yet it seems like the last set sort of run over the settings and do it the opposite:
class GraphTableViewCell: UITableViewCell {
#IBOutlet weak var yieldLabel: UILabel!
#IBOutlet weak var yieldPercentLabel: UILabel!
#IBOutlet weak var lineChart: LineChartView!
#IBOutlet weak var graphButtonView: AssetGraphButtonView!
#IBOutlet weak var endDateLabel: UILabel!
#IBOutlet weak var startDateLabel: UILabel!
var selectionView: AssetGraphSelectionView!
var viewModel: GraphViewModelType!
var set: LineChartDataSet!
var set1: LineChartDataSet!
var marker = BalloonMarker(color: .red,
font: UIFont.systemFont(ofSize: 15),
textColor: .white,
insets: UIEdgeInsets(top: 5, left: 5, bottom: 10, right: 3))
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override func layoutSubviews() {
super.layoutSubviews()
}
func config(with viewModel: GraphViewModelType) {
self.viewModel = viewModel
yieldLabel.attributedText = viewModel.titleAttributeText
yieldPercentLabel.attributedText = viewModel.yielAttributeText
startDateLabel.attributedText = viewModel.startDateAttributeText
endDateLabel.attributedText = viewModel.endDateAttributeText
graphButtonView.confgiureCell(with: self.viewModel.btnData)
setUpChart()
}
func setUpChart() {
lineChart.delegate = self
lineChart.noDataText = "No Data Available"
lineChart.rightAxis.enabled = false
lineChart.leftAxis.enabled = false
lineChart.xAxis.enabled = false
lineChart.legend.enabled = false
lineChart.xAxis.drawGridLinesEnabled = false
lineChart.drawMarkers = true
lineChart.doubleTapToZoomEnabled = false
lineChart.pinchZoomEnabled = false
lineChart.scaleXEnabled = false
lineChart.scaleYEnabled = false
marker.chartView = lineChart
marker.minimumSize = CGSize(width: 28, height: 20)
lineChart.marker = marker
let dataSets = viewModel.getLineChartDataSet()
let data = LineChartData(dataSets: dataSets)
data.setValueFont(.systemFont(ofSize: 7, weight: .light))
lineChart.data = data
}
extension GraphTableViewCell: ChartViewDelegate {
func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
let transform = lineChart.getTransformer(forAxis: .left)
let point = transform.pixelForValues(x: highlight.x, y: highlight.y)
print("point : x = \(point.x) y = \(point.y)")
let color = self.lineChart.colorOfPoint(point: point)
marker.color = color
let dataSets = viewModel.chartValueSelected(entry: entry)
lineChart.data = dataSets
}
}
This is the viewModel:
final class GraphTableViewCellViewModel: GraphViewModelType {
var startDateAttributeText = NSMutableAttributedString()
var endDateAttributeText = NSMutableAttributedString()
var titleAttributeText = NSMutableAttributedString(string: "", attributes: [NSAttributedString.Key.font : UIFont(name: "Orion-Bold", size: 14)!, NSAttributedString.Key.foregroundColor : UIColor.black])
var yielAttributeText = NSMutableAttributedString(string: "\(String(format: "%.2f%%", abs(14)))", attributes: [NSAttributedString.Key.font : UIFont(name: "Orion-Bold", size: 14)!, NSAttributedString.Key.foregroundColor : UIColor.green])
let disposeBag = DisposeBag()
var btnData: [AssetGraphButtonViewViewModel]
var data: TwrGraph
var dataSet = [LineChartDataSet]()
var set: LineChartDataSet!
var set1: LineChartDataSet!
init(with data: TwrGraph) {
btnData = AssetGraphViewModel(security: nil).assetGraphData
self.data = data
startDateAttributeText = NSMutableAttributedString(string: data.startDate, attributes: [NSAttributedString.Key.font : UIFont(name: "Orion-Regular", size: 12)!, NSAttributedString.Key.foregroundColor : ColorName.warmGreyTwo])
endDateAttributeText = NSMutableAttributedString(string: data.endDate, attributes: [NSAttributedString.Key.font : UIFont(name: "Orion-Regular", size: 12)!, NSAttributedString.Key.foregroundColor : ColorName.warmGreyTwo])
}
func getChartDataPoints(dataPoints: [String], values: [Double]) -> [ChartDataEntry] {
var dataEntries: [ChartDataEntry] = []
for count in (0..<dataPoints.count) {
dataEntries.append(ChartDataEntry.init(x: Double(count), y: values[count]))
}
return dataEntries
}
func getLineChartDataSet() -> [LineChartDataSet] {
let dataWeeklyDate = self.data.weeklyGraph.map { $0.date }
let dataWeeklyYield = self.data.weeklyGraph.map { $0.yield }
let dataPoints = getChartDataPoints(dataPoints: dataWeeklyDate, values: dataWeeklyYield)
set = LineChartDataSet(entries: dataPoints, label:"")
setup(set)
return [set]
}
func setup(_ dataSet: LineChartDataSet) {
dataSet.drawHorizontalHighlightIndicatorEnabled = false
dataSet.drawVerticalHighlightIndicatorEnabled = true
dataSet.isDrawLineWithGradientEnabled = true
dataSet.fillAlpha = 0.15
dataSet.lineWidth = 2
dataSet.circleRadius = 0
dataSet.drawCircleHoleEnabled = false
dataSet.drawCirclesEnabled = false
dataSet.drawValuesEnabled = false
dataSet.highlightColor = .blue
let rightColor = [ChartColorTemplates.colorFromString("#FA3A7A"), ChartColorTemplates.colorFromString("#C257B1"),
ChartColorTemplates.colorFromString("#8B73E8")]
dataSet.colors = rightColor
dataSet.gradientPositions = [0, 40, 100]
let gradientColors = [ChartColorTemplates.colorFromString("#FC4684").cgColor,
ChartColorTemplates.colorFromString("#D8D8D8").cgColor]
let colorLocations:[CGFloat] = [1.0, 0.0]
if let gradient = CGGradient(colorsSpace: nil, colors: gradientColors as CFArray, locations: colorLocations) {
dataSet.fill = LinearGradientFill(gradient: gradient, angle: 90.0)
}
dataSet.drawFilledEnabled = true
}
func updateSetAfterTouch(_ dataSet: LineChartDataSet) {
dataSet.drawHorizontalHighlightIndicatorEnabled = false
dataSet.drawVerticalHighlightIndicatorEnabled = true
// dataSet.isDrawLineWithGradientEnabled = true
dataSet.fillAlpha = 0.15
dataSet.lineWidth = 2
dataSet.circleRadius = 0
dataSet.drawCircleHoleEnabled = false
dataSet.drawCirclesEnabled = false
dataSet.drawValuesEnabled = false
// dataSet.highlightColor = .blue
dataSet.colors = [.red]
}
func chartValueSelected(entry: ChartDataEntry) -> LineChartData {
return updateSet(with: entry)
}
func updateSet(with entry: ChartDataEntry) -> LineChartData {
var dataEntries: [ChartDataEntry] = []
var dataEntries1: [ChartDataEntry] = []
for count in (0..<self.data.weeklyGraph.count) {
if count < self.data.weeklyGraph.count && count < Int(entry.x) {
dataEntries.append(ChartDataEntry.init(x: Double(count), y: self.data.weeklyGraph[count].yield))
} else {
dataEntries1.append(ChartDataEntry.init(x: Double(count), y: self.data.weeklyGraph[count].yield))
}
}
set = LineChartDataSet(entries: dataEntries, label:"")
set1 = LineChartDataSet(entries: dataEntries1, label:"")
setup(set)
updateSetAfterTouch(set1)
let data = LineChartData(dataSets: [set1, set])
return data
}
}
As you can see when value is selected and I am using it to create two ChartDataEntry and the two LineChartDataSet that one continue the other (in the x axis ). This is the image when we first entering(looks fine):
This is the image when selecting:
After creating two LineChartDataSet add a different colour for each set.
func updateGraph(){
//create 2 ChartDataEntry arrays for both sets
var dataEntries : [ChartDataEntry] = []
var dataEntries1 : [ChartDataEntry] = []
//use ur condition here to devide the data in to two groups
for i in 0..<numbers.count {
if i < 3{
let value = ChartDataEntry(x: Double(i), y: numbers[i]) // here we set the X and Y status in a data chart entry
dataEntries.append(value)
}else{
let value = ChartDataEntry(x: Double(i), y: numbers[i]) // here we set the X and Y status in a data chart entry
dataEntries1.append(value)
}
}
//I add the last element of the first array to the begining of the first array to stop discontinue
dataEntries1.insert(dataEntries.last!, at: 0)
//create data set 1
let set1 = LineChartDataSet(entries: dataEntries, label: "Number")
set1.colors = [UIColor.blue]
//create data set 2
let set2 = LineChartDataSet(entries: dataEntries1, label: "Number")
set2.colors = [UIColor.red]
//This is the object that will be added to the chart
let data = LineChartData(dataSets: [set1, set2])
chtChart.data = data //finally - it adds the chart data to the chart and causes an update
}
My Output looks like follows.
Full ViewController Code
class ViewController: UIViewController {
#IBOutlet weak var txtTextBox: UITextField!
#IBOutlet weak var chtChart: LineChartView!
var numbers : [Double] = [] //This is where we are going to store all the numbers. This can be a set of numbers that come from a Realm database, Core data, External API's or where ever else
override func viewDidLoad() {
super.viewDidLoad()
var dataEntries : [ChartDataEntry] = []
numbers = [2,4,7,3,4,5,8,9,1,2,9]
for i in 0..<numbers.count{
let value = ChartDataEntry(x: Double(i), y: numbers[i]) // here we set the X and Y status in a data chart entry
dataEntries.append(value)
}
let dataSet = LineChartDataSet(entries: dataEntries, label: "Number")
dataSet.drawHorizontalHighlightIndicatorEnabled = false
dataSet.drawVerticalHighlightIndicatorEnabled = true
dataSet.drawHorizontalHighlightIndicatorEnabled = true
dataSet.fillAlpha = 0.15
dataSet.lineWidth = 2
dataSet.circleRadius = 0
dataSet.drawCircleHoleEnabled = false
dataSet.drawCirclesEnabled = false
dataSet.drawValuesEnabled = false
dataSet.highlightColor = .blue
dataSet.colors = [UIColor.green]
let data = LineChartData(dataSets: [dataSet])
chtChart.data = data
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func btnbutton(_ sender: Any) {
// let input = Double(txtTextBox.text!) //gets input from the textbox - expects input as double/int
// numbers.append(input!) //here we add the data to the array.
updateGraph()
txtTextBox.text = ""
}
func updateGraph(){
//create 2 ChartDataEntry arrays for both sets
var dataEntries : [ChartDataEntry] = []
var dataEntries1 : [ChartDataEntry] = []
//use ur condition here to devide the data in to two groups
for i in 0..<numbers.count {
if i < 3{
let value = ChartDataEntry(x: Double(i), y: numbers[i]) // here we set the X and Y status in a data chart entry
dataEntries.append(value)
}else{
let value = ChartDataEntry(x: Double(i), y: numbers[i]) // here we set the X and Y status in a data chart entry
dataEntries1.append(value)
}
}
//I add the last element of the first array to the begining of the first array to stop discontinue
dataEntries1.insert(dataEntries.last!, at: 0)
//create data set 1
let set1 = LineChartDataSet(entries: dataEntries, label: "Number")
set1.colors = [UIColor.blue]
set1.drawHorizontalHighlightIndicatorEnabled = false
set1.drawVerticalHighlightIndicatorEnabled = true
set1.drawHorizontalHighlightIndicatorEnabled = true
set1.fillAlpha = 0.15
set1.lineWidth = 2
set1.circleRadius = 0
set1.drawCircleHoleEnabled = false
set1.drawCirclesEnabled = false
set1.drawValuesEnabled = false
set1.highlightColor = .blue
//create data set 2
let set2 = LineChartDataSet(entries: dataEntries1, label: "Number")
set2.colors = [UIColor.red]
set2.drawHorizontalHighlightIndicatorEnabled = false
set2.drawVerticalHighlightIndicatorEnabled = true
set2.drawHorizontalHighlightIndicatorEnabled = true
set2.fillAlpha = 0.15
set2.lineWidth = 2
set2.circleRadius = 0
set2.drawCircleHoleEnabled = false
set2.drawCirclesEnabled = false
set2.drawValuesEnabled = false
set2.highlightColor = .blue
//This is the object that will be added to the chart
let data = LineChartData(dataSets: [set1, set2])
chtChart.data = data //finally - it adds the chart data to the chart and causes an update
}
}
So i have a graph with data.
I am trying to get the color of the selected value in order to show the same color on my Marker, yet i cant figure it out how to get color on the lineChart:
class GraphTableViewCell: UITableViewCell {
#IBOutlet weak var yieldLabel: UILabel!
#IBOutlet weak var yieldPercentLabel: UILabel!
#IBOutlet weak var lineChart: LineChartView!
#IBOutlet weak var graphButtonView: AssetGraphButtonView!
#IBOutlet weak var endDateLabel: UILabel!
#IBOutlet weak var startDateLabel: UILabel!
var selectionView: AssetGraphSelectionView!
var viewModel: GraphViewModelType!
var set: LineChartDataSet!
var marker = BalloonMarker(color: .red,
font: UIFont.systemFont(ofSize: 15),
textColor: .white,
insets: UIEdgeInsets(top: 5, left: 5, bottom: 10, right: 3))
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override func layoutSubviews() {
super.layoutSubviews()
}
func config(with viewModel: GraphViewModelType) {
self.viewModel = viewModel
yieldLabel.attributedText = viewModel.titleAttributeText
yieldPercentLabel.attributedText = viewModel.yielAttributeText
startDateLabel.attributedText = viewModel.startDateAttributeText
endDateLabel.attributedText = viewModel.endDateAttributeText
graphButtonView.confgiureCell(with: self.viewModel.btnData)
setUpChart()
}
func setUpChart() {
lineChart.delegate = self
lineChart.noDataText = "No Data Available"
lineChart.rightAxis.enabled = false
lineChart.leftAxis.enabled = false
lineChart.xAxis.enabled = false
lineChart.legend.enabled = false
lineChart.xAxis.drawGridLinesEnabled = false
lineChart.drawMarkers = true
lineChart.doubleTapToZoomEnabled = false
lineChart.pinchZoomEnabled = false
lineChart.scaleXEnabled = false
lineChart.scaleYEnabled = false
marker.chartView = lineChart
marker.minimumSize = CGSize(width: 28, height: 20)
lineChart.marker = marker
let dataSets = [getLineChartDataSet()]
let data = LineChartData(dataSets: dataSets)
data.setValueFont(.systemFont(ofSize: 7, weight: .light))
lineChart.data = data
}
func getChartDataPoints(sessions: [Int], accuracy: [Double]) -> [ChartDataEntry] {
var dataPoints: [ChartDataEntry] = []
for count in (0..<sessions.count) {
dataPoints.append(ChartDataEntry.init(x: Double(sessions[count]), y: accuracy[count]))
}
return dataPoints
}
func getLineChartDataSet() -> LineChartDataSet {
let dataPoints = getChartDataPoints(sessions: [0,1,2,3,4,5,6,7,8,9,10], accuracy: [100, 20.0, 30.0, 50.0, 105.3, 100, 43.8, 100, 82, 57, 122])
set = LineChartDataSet(entries: dataPoints, label:"")
setup(set)
return set
}
func setup(_ dataSet: LineChartDataSet) {
dataSet.drawHorizontalHighlightIndicatorEnabled = false
dataSet.drawVerticalHighlightIndicatorEnabled = true
dataSet.isDrawLineWithGradientEnabled = true
dataSet.fillAlpha = 0.15
dataSet.lineWidth = 2
dataSet.circleRadius = 0
dataSet.drawCircleHoleEnabled = false
dataSet.drawCirclesEnabled = false
dataSet.drawValuesEnabled = false
dataSet.highlightColor = .blue
dataSet.setColors(ChartColorTemplates.colorFromString("#FA3A7A"), ChartColorTemplates.colorFromString("#C257B1"),
ChartColorTemplates.colorFromString("#8B73E8"))
dataSet.gradientPositions = [0, 40, 100]
let gradientColors = [ChartColorTemplates.colorFromString("#FC4684").cgColor,
ChartColorTemplates.colorFromString("#D8D8D8").cgColor]
let colorLocations:[CGFloat] = [1.0, 0.0]
if let gradient = CGGradient(colorsSpace: nil, colors: gradientColors as CFArray, locations: colorLocations) {
dataSet.fill = LinearGradientFill(gradient: gradient, angle: 90.0)
}
dataSet.drawFilledEnabled = true
}
}
extension GraphTableViewCell: ChartViewDelegate {
func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
print("chartValueSelected : x = \(highlight.x) y = \(highlight.y)")
}
}
In the method chartValueSelected you need to get the point of the selectedValue by:
let transform = chartView.getTransformer(forAxis: .left)
let point = transform.pixelForValues(x: highlight.x, y: highlight.y)
With the CGPoint of the selected value, go to the pixel and pick the color using the method at https://stackoverflow.com/a/56723477/14087244
Call the method on your lineChart like this
let color = lineChart.colorOfPoint(point: point)
Complete code:
func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
let transform = chartView.getTransformer(forAxis: .left)
let point = transform.pixelForValues(x: highlight.x, y: highlight.y)
let color = lineChart.colorOfPoint(point: point)
}
I have a StackContainerView inside my main view controller called TodayPicksViewController. I am trying to programmatically set the StackContainerView to fill up the whole view controller side to side, with around 50 from top and bottom (just like a Tinder card).
However, as I try to implement constraints relative to safe area as follows(as other answers on StackOverflow suggest), turned out the StackContainerView doesn't show up at all. I don't know where the problem is.
Please advice.
Code of my main view controller, TodayPicksViewController:
class TodayPicksViewController: UIViewController {
//MARK: - Properties
var viewModelData = [CardsDataModel(bgColor: UIColor(red:0.96, green:0.81, blue:0.46, alpha:1.0), text: "Hamburger", image: "hamburger"),
CardsDataModel(bgColor: UIColor(red:0.29, green:0.64, blue:0.96, alpha:1.0), text: "Puppy", image: "puppy"),
CardsDataModel(bgColor: UIColor(red:0.29, green:0.63, blue:0.49, alpha:1.0), text: "Poop", image: "poop"),
CardsDataModel(bgColor: UIColor(red:0.69, green:0.52, blue:0.38, alpha:1.0), text: "Panda", image: "panda"),
CardsDataModel(bgColor: UIColor(red:0.90, green:0.99, blue:0.97, alpha:1.0), text: "Subway", image: "subway"),
CardsDataModel(bgColor: UIColor(red:0.83, green:0.82, blue:0.69, alpha:1.0), text: "Robot", image: "robot")]
var stackContainer : StackContainerView!
private let spinner = JGProgressHUD(style: .dark)
private var users = [[String: String]]()
private var results = [SearchResult]()
private var hasFetched = false
var divisor: CGFloat!
private let noResultsLabel: UILabel = {
let label = UILabel()
label.isHidden = true
label.text = "No Results"
label.textAlignment = .center
label.textColor = .green
label.font = .systemFont(ofSize: 21, weight: .medium)
return label
}()
override func loadView() {
view = UIView()
stackContainer = StackContainerView()
view.addSubview(stackContainer)
stackContainer.translatesAutoresizingMaskIntoConstraints = false
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(noResultsLabel)
configureStackContainer()
stackContainer.dataSource = self
}
#IBAction func panMatch(_ sender: UIPanGestureRecognizer) {
let match = sender.view!
let point = sender.translation(in: view)
let xFromCenter = match.center.x - view.center.x
print(xFromCenter)
match.center = CGPoint(x: view.center.x + point.x, y: view.center.y + point.y)
match.transform = CGAffineTransform(rotationAngle: xFromCenter/divisor)
if sender.state == UIGestureRecognizer.State.ended {
if match.center.x < 75 {
// Move off to the left side
UIView.animate(withDuration: 0.3, animations: {
match.center = CGPoint(x: match.center.x - 200, y: match.center.y + 75)
match.alpha = 0
})
return
} else if match.center.x > (view.frame.width - 75) {
// Move off to the right side
UIView.animate(withDuration: 0.3, animations: {
match.center = CGPoint(x: match.center.x + 200, y: match.center.y + 75)
match.alpha = 0
})
return
}
// resetCard()
}
}
private var loginObserver: NSObjectProtocol?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
validateAuth()
}
private func validateAuth() {
if FirebaseAuth.Auth.auth().currentUser == nil {
let vc = SignInViewController()
let nav = UINavigationController(rootViewController: vc)
nav.modalPresentationStyle = .fullScreen
present(nav, animated: false)
}
}
#objc private func pageControlDidChange(_ sender: UIPageControl) {
let current = sender.currentPage
// scrollView.setContentOffset(CGPoint(x: CGFloat(current) * view.frame.size.width,
// y: 70), animated: true)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
//MARK: - Configurations
func configureStackContainer() {
stackContainer.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
stackContainer.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: -60).isActive = true
// stackContainer.widthAnchor.constraint(equalToConstant: 300).isActive = true
// stackContainer.heightAnchor.constraint(equalToConstant: 400).isActive = true
stackContainer.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
stackContainer.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor).isActive = true
stackContainer.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor).isActive = true
stackContainer.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
}
func updateUI() {
if results.isEmpty {
noResultsLabel.isHidden = false
}
else {
noResultsLabel.isHidden = true
}
}
func calcAge(birthday: Date) -> Int {
let dateFormater = DateFormatter()
dateFormater.dateFormat = "MM/dd/yyyy"
// let birthdayDate = dateFormater.date(from: birthday)
let calendar: NSCalendar! = NSCalendar(calendarIdentifier: .gregorian)
let now = Date()
let calcAge = calendar.components(.year, from: birthday, to: now, options: [])
let age = calcAge.year
return age!
}
extension TodayPicksViewController : SwipeCardsDataSource {
func numberOfCardsToShow() -> Int {
return viewModelData.count
}
func card(at index: Int) -> SwipeCardView {
let card = SwipeCardView()
card.dataSource = viewModelData[index]
return card
}
func emptyView() -> UIView? {
return nil
}
}
Probably doesn't matter, but here is my code for the StackContainerView:
class StackContainerView: UIView, SwipeCardsDelegate {
//MARK: - Properties
var numberOfCardsToShow: Int = 0
var cardsToBeVisible: Int = 3
var cardViews : [SwipeCardView] = []
var remainingcards: Int = 0
let horizontalInset: CGFloat = 10.0
let verticalInset: CGFloat = 10.0
var visibleCards: [SwipeCardView] {
return subviews as? [SwipeCardView] ?? []
}
var dataSource: SwipeCardsDataSource? {
didSet {
reloadData()
}
}
//MARK: - Init
override init(frame: CGRect) {
super.init(frame: .zero)
backgroundColor = .clear
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func reloadData() {
removeAllCardViews()
guard let datasource = dataSource else { return }
setNeedsLayout()
layoutIfNeeded()
numberOfCardsToShow = datasource.numberOfCardsToShow()
remainingcards = numberOfCardsToShow
for i in 0..<min(numberOfCardsToShow,cardsToBeVisible) {
addCardView(cardView: datasource.card(at: i), atIndex: i )
}
}
//MARK: - Configurations
private func addCardView(cardView: SwipeCardView, atIndex index: Int) {
cardView.delegate = self
addCardFrame(index: index, cardView: cardView)
cardViews.append(cardView)
insertSubview(cardView, at: 0)
remainingcards -= 1
}
func addCardFrame(index: Int, cardView: SwipeCardView) {
var cardViewFrame = bounds
let horizontalInset = (CGFloat(index) * self.horizontalInset)
let verticalInset = CGFloat(index) * self.verticalInset
cardViewFrame.size.width -= 2 * horizontalInset
cardViewFrame.origin.x += horizontalInset
cardViewFrame.origin.y += verticalInset
cardView.frame = cardViewFrame
}
private func removeAllCardViews() {
for cardView in visibleCards {
cardView.removeFromSuperview()
}
cardViews = []
}
func swipeDidEnd(on view: SwipeCardView) {
guard let datasource = dataSource else { return }
view.removeFromSuperview()
if remainingcards > 0 {
let newIndex = datasource.numberOfCardsToShow() - remainingcards
addCardView(cardView: datasource.card(at: newIndex), atIndex: 2)
for (cardIndex, cardView) in visibleCards.reversed().enumerated() {
UIView.animate(withDuration: 0.2, animations: {
cardView.center = self.center
self.addCardFrame(index: cardIndex, cardView: cardView)
self.layoutIfNeeded()
})
}
}else {
for (cardIndex, cardView) in visibleCards.reversed().enumerated() {
UIView.animate(withDuration: 0.2, animations: {
cardView.center = self.center
self.addCardFrame(index: cardIndex, cardView: cardView)
self.layoutIfNeeded()
})
}
}
}
}
According to the apple developer doc for loadView(), they said "The view controller calls this method when its view property is requested but is currently nil. This method loads or creates a view and assigns it to the view property." This might be the cause of the problem. I would recommend you to perform the view set up operations in viewDidLoad or other proper lifecycle methods. Based on my understanding, this line view = UIView() isn't necessary. In your configureStackContainer() func, you set the centerX and centerY anchor and then set the top, leading, trailing, bottom anchor again. This may also raise the constraint conflicts. I think you don't need to specify centerX and centerY anchor if you want to constraint with top, leading, trailing and bottom and vice versa. I hope this will be helpful.
I have used this library on swift called: iOS Charts https://github.com/danielgindi/ios-charts
I have two datasets and I want to set the position of data labels in one of the dataset to the bottom of the line, so the numbers can be visible and no overlapping happens. How can I do this?
I setup the chart as follows:
private func configureChart() {
lineChartView = LineChartView(frame: CGRect(x: 0, y: 60, width: self.view.frame.width, height: 200))
lineChartView?.delegate = self
lineChartView?.chartDescription?.enabled = false
lineChartView?.dragEnabled = true
lineChartView?.setScaleEnabled(false)
lineChartView?.pinchZoomEnabled = false
lineChartView?.rightAxis.enabled = false
lineChartView?.xAxis.valueFormatter = self
lineChartView?.xAxis.granularity = 1
lineChartView?.legend.form = .line
lineChartView?.animate(yAxisDuration: 0.3)
if let lineChartView = lineChartView {
dashboardHeaderView?.subviews.filter({ $0 is LineChartView }).forEach {
$0.removeFromSuperview()
}
dashboardHeaderView?.addSubview(lineChartView)
}
setupLineChartData()
}
func setupLineChartData() {
monthData = ReportModel.monthlyOveralInfo()
let costSet = self.provideLineData(type: .totalCost)
let incomeSet = self.provideLineData(type: .totalIncome)
let lineChartData = LineChartData(dataSets: [incomeSet, costSet])
lineChartView?.data = lineChartData
lineChartView?.setVisibleXRangeMaximum(5)
lineChartView?.moveViewToX(lineChartView?.chartXMax ?? 0)
}
private func provideLineData(type: SWMonthlyOverallType) -> LineChartDataSet {
var mainColor: UIColor = .black
var gradientFirstColor: UIColor = .clear
var gradientSecondColor: UIColor = .black
if type == .totalIncome {
mainColor = .myAppGreen
gradientFirstColor = .clear
gradientSecondColor = .myAppGreen
}
let totalCosts = monthData.compactMap({
$0.items.first(where: {$0.type == type})
})
var index: Double = -1
let values: [ChartDataEntry] = totalCosts.compactMap({
index += 1
return ChartDataEntry(x: index, y: $0.value)
})
let chartDataSet = LineChartDataSet(values: values, label: type.rawValue)
chartDataSet.resetColors()
chartDataSet.drawIconsEnabled = false
chartDataSet.setColor(mainColor)
chartDataSet.setCircleColor(mainColor)
chartDataSet.lineWidth = 1
chartDataSet.circleRadius = 3
chartDataSet.drawCircleHoleEnabled = true
chartDataSet.valueFont = .systemFont(ofSize: 9)
let gradientColors = [gradientFirstColor.cgColor,
gradientSecondColor.cgColor]
let gradient = CGGradient(colorsSpace: nil, colors: gradientColors as CFArray, locations: nil)
chartDataSet.fillAlpha = 0.5
if let gradient = gradient {
chartDataSet.fill = Fill(linearGradient: gradient, angle: 90)
}
chartDataSet.drawFilledEnabled = true
return chartDataSet
}
Just posting in case someone finds it to be useful:
chart.xAxis.labelPosition = .bottom
I have an array of "hotspot views" which are just UIImage's and an array of stackviews which contain two labels in each. Im trying to get the color of the hotspot view image and one of the labels to change to red when the hotspot view is tapped. I cant seem to find out how after googling most of the day. Any insight would be great.
Here is my code below:
I have commented out in the tap gesture function what i was hoping to achieve but i have no idea how to access the nested labels in the stackview or if im using the tap gesture recognizer correctly.
import UIKit
import OAStackView
protocol StandMapHotspotLayerViewDataSource {
func numberOfHotspots(standMapHotspotLayerView: StandMapHotspotLayerView) -> Int
func hotspotViewForIndex(index: Int, inStandMapHotspotLayerView: StandMapHotspotLayerView) -> (UIView, OAStackView)
}
struct HotspotDataSource {
var stackView: [OAStackView] = []
var hotspotView: [UIView] = []
}
class StandMapHotspotLayerView: UIView {
var dataSource: StandMapHotspotLayerViewDataSource?
var hotspotDataSource = HotspotDataSource()
override func layoutSubviews() {
super.layoutSubviews()
let hotspotCount = self.dataSource?.numberOfHotspots(self) ?? 0
(0..<hotspotCount).map({ index in
return self.dataSource!.hotspotViewForIndex(index, inStandMapHotspotLayerView: self)
}).forEach({ hotspotView, stackView in
hotspotDataSource.hotspotView.append(hotspotView)
hotspotDataSource.stackView.append(stackView)
hotspotView.userInteractionEnabled = true
let gesture = UITapGestureRecognizer(target: hotspotView, action: #selector(self.hotspotWasPressed(_:)))
self.addGestureRecognizer(gesture)
self.addSubview(hotspotView)
self.addSubview(stackView)
})
addLine()
}
func hotspotWasPressed(sender: UITapGestureRecognizer) {
//
// sender.numberOfTouchesRequired = 1
//
// let hotspotView = hotspotDataSource.hotspotView[index]
// let stackView = hotspotDataSource.stackView[index]
//
// hotspotView.tintColor = UIColor(red: 157, green: 27, blue: 50, alpha: 1)
// stackView
}
func addLine() {
let path = UIBezierPath()
let shapeLayer = CAShapeLayer()
for index in 0..<self.dataSource!.numberOfHotspots(self) {
let stackView = hotspotDataSource.stackView[index]
let hotspotView = hotspotDataSource.hotspotView[index]
if stackView.frame.origin.y < 100 {
let stackViewPoint = CGPointMake(stackView.frame.origin.x + stackView.frame.size.width / 2, stackView.frame.origin.y + stackView.frame.size.height)
let imageViewPoint = CGPointMake((hotspotView.frame.origin.x + hotspotView.frame.size.width / 2), hotspotView.frame.origin.y)
path.moveToPoint(stackViewPoint)
path.addLineToPoint(imageViewPoint)
} else {
let stackViewPoint = CGPointMake(stackView.frame.origin.x + stackView.frame.size.width / 2, stackView.frame.origin.y)
let imageViewPoint = CGPointMake((hotspotView.frame.origin.x + hotspotView.frame.size.width / 2), hotspotView.frame.origin.y + hotspotView.bounds.size.height)
path.moveToPoint(stackViewPoint)
path.addLineToPoint(imageViewPoint)
}
shapeLayer.path = path.CGPath
shapeLayer.strokeColor = UIColor.whiteColor().CGColor
shapeLayer.lineWidth = 0.2
shapeLayer.fillColor = UIColor.whiteColor().CGColor
self.layer.addSublayer(shapeLayer)
}
}
func reloadData() {
self.setNeedsLayout()
}
}
Thanks for any help in advance.
Figured it out. See code below:
import UIKit
import OAStackView
protocol StandMapHotspotLayerViewDataSource {
func numberOfHotspots(standMapHotspotLayerView: StandMapHotspotLayerView) -> Int
func hotspotViewForIndex(index: Int, inStandMapHotspotLayerView: StandMapHotspotLayerView) -> (UIImageView, OAStackView)
}
struct HotspotViews {
var stackView: [OAStackView] = []
var hotspotView: [UIImageView] = []
}
class StandMapHotspotLayerView: UIView {
var dataSource: StandMapHotspotLayerViewDataSource?
var hotspotViews = HotspotViews()
override func layoutSubviews() {
super.layoutSubviews()
let hotspotCount = self.dataSource?.numberOfHotspots(self) ?? 0
var i: Int = 0
(0..<hotspotCount).map({ index in
return self.dataSource!.hotspotViewForIndex(index, inStandMapHotspotLayerView: self)
}).forEach({ hotspotView, stackView in
hotspotViews.hotspotView.append(hotspotView)
hotspotViews.stackView.append(stackView)
hotspotView.userInteractionEnabled = true
hotspotView.tag = i
let gesture = UITapGestureRecognizer(target: self, action: #selector(self.hotspotWasPressed(_:)))
hotspotView.addGestureRecognizer(gesture)
self.addSubview(hotspotView)
self.addSubview(stackView)
i += 1
})
addLine()
}
func hotspotWasPressed(sender: UITapGestureRecognizer) {
let index = sender.view!.tag
let hotspot = hotspotViews.hotspotView[index]
let textLabel = hotspotViews.stackView[index].arrangedSubviews.first as? UILabel
hotspot.image = UIImage(named: "RedHotspotImage")
textLabel?.textColor = UIColor(red: 158/255, green: 27/255, blue: 50/255, alpha: 1)
//go to hotspot url: StandMapView.hotspots[index].url
}
func addLine() {
let path = UIBezierPath()
let shapeLayer = CAShapeLayer()
for index in 0..<self.dataSource!.numberOfHotspots(self) {
let stackView = hotspotViews.stackView[index]
let hotspotView = hotspotViews.hotspotView[index]
if stackView.frame.origin.y < 100 {
let stackViewPoint = CGPointMake(stackView.frame.origin.x + stackView.frame.size.width / 2, stackView.frame.origin.y + stackView.frame.size.height)
let imageViewPoint = CGPointMake((hotspotView.frame.origin.x + hotspotView.frame.size.width / 2), hotspotView.frame.origin.y)
path.moveToPoint(stackViewPoint)
path.addLineToPoint(imageViewPoint)
} else {
let stackViewPoint = CGPointMake(stackView.frame.origin.x + stackView.frame.size.width / 2, stackView.frame.origin.y)
let imageViewPoint = CGPointMake((hotspotView.frame.origin.x + hotspotView.frame.size.width / 2), hotspotView.frame.origin.y + hotspotView.bounds.size.height)
path.moveToPoint(stackViewPoint)
path.addLineToPoint(imageViewPoint)
}
shapeLayer.path = path.CGPath
shapeLayer.strokeColor = UIColor.whiteColor().CGColor
shapeLayer.lineWidth = 0.2
shapeLayer.fillColor = UIColor.whiteColor().CGColor
self.layer.addSublayer(shapeLayer)
}
}
func reloadData() {
self.setNeedsLayout()
}
}
Important parts were to pass a .tag through on the hotspot view and then to access the label within the stackview by
stackView[index].arrangedSubviews.first as? UILabel
thanks