How to Render a Pie Chart on childviews in iOS? - ios

How to render pie charts on child views? In my story board having 4 child views. I am trying to show 4 different pie charts based on the selection ( I am using segmented control which has 4 segments ). I tried with sample data but not able to render pie chart ( as it showing no data available ). How to add pie chart data to child views?
[ Note:- I am using danielgindib library and swift 3 ]
Screenshot of my storyboard
Storyboard screenshot 1
Storyboard screenshot 2
SecondViewController.swift
import UIKit
import Charts
class SecondViewController: UIViewController, ChartViewDelegate{
var controllerName:String?
#IBOutlet weak var pChartViewA: PieChartView!
#IBOutlet weak var pChartViewB: PieChartView!
#IBOutlet weak var pChartViewC: PieChartView!
#IBOutlet weak var pChartViewD: PieChartView!
override func viewDidLoad() {
super.viewDidLoad()
self.pChartViewA.delegate = self
// self.pChartViewB.delegate = self
//self.pChartViewC.delegate = self
// self.pChartViewD.delegate = self
self.pChartViewA.alpha = 1
//self.pChartView.delegate = self
// Pie Chart
pChartViewA.noDataText = "You need to provide data for the chart."
let pys1 = Array(1..<10).map { x in return sin(Double(x) / 2.0 / 3.141 * 1.5) * 100.0 }
let pyse1 = pys1.enumerated().map { x, y in return PieChartDataEntry(value: y, label: String(x)) }
let pdata = PieChartData()
let pds1 = PieChartDataSet(values: pyse1, label: "Hello")
pds1.colors = ChartColorTemplates.vordiplom()
pdata.addDataSet(pds1)
let paragraphStyle: NSMutableParagraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
paragraphStyle.lineBreakMode = .byTruncatingTail
paragraphStyle.alignment = .center
let centerText: NSMutableAttributedString = NSMutableAttributedString(string: "Sample Screening Pie")
self.pChartViewA.centerAttributedText = centerText
self.pChartViewA.data = pdata
self.pChartViewA.chartDescription?.text = "Piechart Demo"
}
#IBAction func ChangeComponent(_ sender: AnyObject) {
if sender.selectedSegmentIndex == 0 {
UIView.animate(withDuration: 0.5, animations: {
self.pChartViewA.alpha = 1
self.pChartViewB.alpha = 0
self.pChartViewC.alpha = 0
self.pChartViewD.alpha = 0
// Pie Chart
self.pChartViewA.noDataText = "You need to provide data for the chart."
let pys1 = Array(1..<10).map { x in return sin(Double(x) / 2.0 / 3.141 * 1.5) * 100.0 }
let pyse1 = pys1.enumerated().map { x, y in return PieChartDataEntry(value: y, label: String(x)) }
let pdata = PieChartData()
let pds1 = PieChartDataSet(values: pyse1, label: "Hello")
pds1.colors = ChartColorTemplates.vordiplom()
pdata.addDataSet(pds1)
let paragraphStyle: NSMutableParagraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
paragraphStyle.lineBreakMode = .byTruncatingTail
paragraphStyle.alignment = .center
let centerText: NSMutableAttributedString = NSMutableAttributedString(string: "Sample Screening Pie")
self.pChartViewA.centerAttributedText = centerText
self.pChartViewA.data = pdata
self.pChartViewA.chartDescription?.text = "Piechart Demo"
})
} else if sender.selectedSegmentIndex == 1 {
UIView.animate(withDuration: 0.5, animations: {
self.pChartViewA.alpha = 0
self.pChartViewB.alpha = 1
self.pChartViewC.alpha = 0
self.pChartViewD.alpha = 0
})
}
else if sender.selectedSegmentIndex == 2 {
UIView.animate(withDuration: 0.5, animations: {
self.pChartViewA.alpha = 0
self.pChartViewB.alpha = 0
self.pChartViewC.alpha = 1
self.pChartViewD.alpha = 0
})
}
else if sender.selectedSegmentIndex == 3 {
UIView.animate(withDuration: 0.5, animations: {
self.pChartViewA.alpha = 0
self.pChartViewB.alpha = 0
self.pChartViewC.alpha = 0
self.pChartViewD.alpha = 1
})
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Thank you in advance.

Related

iOS Charts(Daniel Gindi) - Two LineChartDataSet with different color failed

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

How to reload data for line chart in swift?

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

Change position of data labels to bottom of the line in ios charts

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

Smiley Rating Bar swift

If we had to do this Smiley Rating Bar on iOS...how we can do?
In the link-example, use a gif, but let's avoid this
If I had to do it ... I would use 5 images of faces for the background with their respective descriptions.
For the face that moves in its position X would use UIPanGestureRecognizer:
class ViewController: UIViewController , UIGestureRecognizerDelegate , UITextFieldDelegate{
#IBOutlet weak var image1: UIImageView!
var panGesture = UIPanGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
panGesture = UIPanGestureRecognizer(target: self, action: #selector(ViewController.draggedView(_:)))
image1.isUserInteractionEnabled = true
image1.addGestureRecognizer(panGesture)
}
func draggedView(_ sender:UIPanGestureRecognizer){
self.view.bringSubview(toFront: image1)
let translation = sender.translation(in: self.view)
image1.center = CGPoint(x: image1.center.x + translation.x, y: image1.center.y)
sender.setTranslation(CGPoint.zero, in: self.view)
}
}
The question I have is how do I move the image1 to detect what is going on "above" the images below. Like this:
So...any help I will appreciate
I have done the same thing, Please use
https://github.com/gali8/G8SliderStep/tree/master/G8SliderStep Library .Please replace the Draw labels and Draw Images method with following in G8SliderStep.swift File.
#objc internal func drawLabels() {
guard let ti = tickTitles else {
return
}
if _stepTickLabels == nil {
_stepTickLabels = []
}
if let sl = _stepTickLabels {
for l in sl {
l.removeFromSuperview()
}
_stepTickLabels?.removeAll()
for index in 0..<ti.count {
let title = ti[index]
let lbl = UILabel()
lbl.font = unselectedFont
lbl.text = title
lbl.textAlignment = .center
lbl.sizeToFit()
var offset: CGFloat = 0
if index+1 < (steps%2 == 0 ? steps/2+1 : steps/2) {
offset = trackLeftOffset/2
}
else if index+1 > (steps%2 == 0 ? steps/2+1 : steps/2) {
offset = -(trackRightOffset/2)
}
if index == 0 {
offset = trackLeftOffset
}
if index == steps {
offset = -trackRightOffset
}
let x = offset + CGFloat(Double(index) * stepWidth) - (lbl.frame.size.width / 2)
var rect = lbl.frame
rect.origin.x = x
rect.origin.y = bounds.midY - (bounds.size.height / 2) - rect.size.height + 80
lbl.frame = rect
self.addSubview(lbl)
_stepTickLabels?.append(lbl)
}
}
}
#objc internal func drawImages() {
guard let ti = tickImages else {
return
}
if _stepTickImages == nil {
_stepTickImages = []
}
if let sl = _stepTickImages {
for l in sl {
l.removeFromSuperview()
}
_stepTickImages?.removeAll()
for index in 0..<ti.count {
let img = ti[index]
let imv = UIImageView(image: img)
imv.contentMode = .scaleAspectFit
imv.sizeToFit()
var offset: CGFloat = 0
if index+1 < (steps%2 == 0 ? steps/2+1 : steps/2) {
offset = trackLeftOffset/2
}
else if index+1 > (steps%2 == 0 ? steps/2+1 : steps/2) {
offset = -(trackLeftOffset/2)
}
if index == 0 {
offset = trackLeftOffset
}
if index == steps {
offset = -trackRightOffset
}
let x = offset + CGFloat(Double(index) * stepWidth) - (imv.frame.size.width / 2)
var rect = imv.frame
rect.origin.x = x
rect.origin.y = bounds.midY - (bounds.size.height / 2)
imv.frame = rect
self.insertSubview(imv, at: 2) //index 2 => draw images below the thumb/above the line
_stepTickImages?.append(imv)
}
}
}
Please get selected and unselected emoticons image from your personal resource, and pass those image in array as done in given example.In your view controller in viewDidLoad please write this code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
sliderStepBar.stepImages = [UIImage(named:"terrible")!, UIImage(named:"bad")!, UIImage(named:"okay")!, UIImage(named:"good")!,UIImage(named:"great")!, ]
sliderStepBar.tickTitles = ["Terrible", "Bad", "Okay", "Good", "Great"]
sliderStepBar.tickImages = [#imageLiteral(resourceName: "unselectterrible"), #imageLiteral(resourceName: "unselectbad"), #imageLiteral(resourceName: "unselectokay"),#imageLiteral(resourceName: "unselectgood"),#imageLiteral(resourceName: "unselectgreat")]
sliderStepBar.minimumValue = 4
sliderStepBar.maximumValue = Float(sliderStepBar.stepImages!.count) + sliderStepBar.minimumValue - 1.0
sliderStepBar.stepTickColor = UIColor.clear
sliderStepBar.stepTickWidth = 40
sliderStepBar.stepTickHeight = 40
sliderStepBar.trackHeight = 5
sliderStepBar.value = 5
}
Enjoy the Smiley Rating.
Happy Coding.
I have created the same, Check the below image
Overview
Easy customization(Font, Colors, Images, Ticks, Height, Width, Rounded)
#IBInspectable
Tappable
Draggable
Swift 5.0 above
Xcode 11 above
Orientation support
Manual drag & drop the class
Find the GIT URL for code
SmileyRating

iOS CorePlot point conversion

I am experimenting with Core Plot. I am trying to add a custom "goal" label over the graph at x: 2.0, y: 50.0 - basically label over the y == 50, its in a separate view, which means I need to convert my point from Core Plot to my UIView bounds. I have not found a combination of points conversions from layer/views that works across all iPhones screen sizes. In my pictures below iPhone 6s is the closest.
iPhone 6s+:
iPhone 6s:
iPhone 5s:
My view layout:
Here is my class:
class BarChartViewController: UIViewController, CPTBarPlotDataSource
{
private var barGraph : CPTXYGraph? = nil
#IBOutlet weak var textBox: UILabel!
#IBOutlet weak var graphHostingView: CPTGraphHostingView!
#IBOutlet weak var textBoxView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(false, animated: false)
self.title = "My results"
let newGraph = CPTXYGraph(frame: CGRectZero)
let theme = CPTTheme(named: kCPTPlainWhiteTheme)
newGraph.applyTheme(theme)
let hostingView = graphHostingView
hostingView.hostedGraph = newGraph
if let frameLayer = newGraph.plotAreaFrame
{
// Border
frameLayer.borderLineStyle = nil
frameLayer.cornerRadius = 0.0
frameLayer.masksToBorder = false
// Paddings
newGraph.paddingLeft = 0.0
newGraph.paddingRight = 0.0
newGraph.paddingTop = 0.0
newGraph.paddingBottom = 0.0
frameLayer.paddingLeft = 70.0
frameLayer.paddingTop = 20.0
frameLayer.paddingRight = 20.0
frameLayer.paddingBottom = 80.0
}
// Plot space
let plotSpace = newGraph.defaultPlotSpace as? CPTXYPlotSpace
plotSpace?.yRange = CPTPlotRange(location: 0.0, length: 100.0)
plotSpace?.xRange = CPTPlotRange(location: 0.0, length: 4.0)
let axisSet = newGraph.axisSet as? CPTXYAxisSet
if let x = axisSet?.xAxis {
x.axisLineStyle = nil
x.majorTickLineStyle = nil
x.minorTickLineStyle = nil
x.majorIntervalLength = 5.0
x.orthogonalPosition = 0.0
x.title = "X Axis"
x.titleLocation = 7.5
x.titleOffset = 55.0
// Custom labels
x.labelRotation = CGFloat(M_PI_4)
x.labelingPolicy = .None
let customTickLocations = [0.5, 1.5, 2.5]
let xAxisLabels = ["Label A", "Label B", "Label C"]
var labelLocation = 0
var customLabels = Set<CPTAxisLabel>()
for tickLocation in customTickLocations
{
let newLabel = CPTAxisLabel(text: xAxisLabels[labelLocation], textStyle: x.labelTextStyle)
labelLocation += 1
newLabel.tickLocation = tickLocation
newLabel.offset = x.labelOffset + x.majorTickLength
newLabel.rotation = 0 //CGFloat(M_PI_4)
customLabels.insert(newLabel)
}
x.axisLabels = customLabels
}
if let y = axisSet?.yAxis
{
y.axisLineStyle = nil
y.majorGridLineStyle = CPTMutableLineStyle()
var style = y.majorTickLineStyle?.mutableCopy() as? CPTMutableLineStyle
//style!.lineColor = CPTColor(CGColor: UIColor.blackColor()) //UIColor.blackColor())
style!.lineWidth = 10.0
y.minorGridLineStyle = CPTMutableLineStyle()
style = y.minorTickLineStyle?.mutableCopy() as? CPTMutableLineStyle
//style.lineColor = UIColor.redColor()
style!.lineWidth = 10.0
style!.lineCap = .Round
y.majorTickLength = 10.0
y.majorIntervalLength = 50.0
y.orthogonalPosition = 0.0
y.title = "Y Axis"
y.titleOffset = 45.0
y.titleLocation = 150.0
y.labelRotation = 0
y.labelingPolicy = .None
let customTickLocations = [50, 100]
let yAxisLabels = ["50", "100"]
var labelLocation = 0
var customLabels = Set<CPTAxisLabel>()
for tickLocation in customTickLocations
{
let newLabel = CPTAxisLabel(text: yAxisLabels[labelLocation], textStyle: y.labelTextStyle)
labelLocation += 1
newLabel.tickLocation = tickLocation
newLabel.offset = y.labelOffset + y.majorTickLength
newLabel.rotation = 0 //CGFloat(M_PI_4)
customLabels.insert(newLabel)
}
y.axisLabels = customLabels
var nums = Set<NSNumber>()
nums.insert(NSNumber(double: 50.0))
y.majorTickLocations = nums
}
// First bar plot
let barPlot1 = CPTBarPlot.tubularBarPlotWithColor(CPTColor.yellowColor(), horizontalBars: false)
barPlot1.baseValue = 0.0
barPlot1.dataSource = self
//barPlot1.barOffset = 0.5
barPlot1.identifier = "Bar Plot 1"
let textStyle = CPTMutableTextStyle()
textStyle.color = CPTColor.redColor()
textStyle.fontSize = 10.0
barPlot1.labelTextStyle = textStyle
newGraph.addPlot(barPlot1, toPlotSpace: plotSpace)
self.barGraph = newGraph
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.view.layoutIfNeeded()
let point: [Double] = [2.0, 50.0]
let plotPoint = UnsafeMutablePointer<Double>(point)
var dataPoint = self.barGraph?.defaultPlotSpace?.plotAreaViewPointForDoublePrecisionPlotPoint(plotPoint, numberOfCoordinates: 2)
//dataPoint = graphHostingView.layer.convertPoint(dataPoint!, fromLayer: self.barGraph!.plotAreaFrame!.plotArea)
dataPoint = graphHostingView.layer.convertPoint(dataPoint!, toLayer: graphHostingView.layer.superlayer)
//dataPoint = barGraph?.convertPoint(dataPoint!, fromLayer: graphHostingView.layer)
dataPoint = self.textBoxView.convertPoint(dataPoint!, fromView: graphHostingView)
print(dataPoint!)
for item in (self.textBoxView?.constraints)!
{
if let id = item.identifier
{
if id == "goalX"
{
print(item.constant)
item.constant = CGFloat((dataPoint?.x)!) - item.constant
}
else if id == "goalY"
{
print(item.constant)
item.constant = CGFloat((dataPoint?.y)!) - item.constant
}
}
}
barGraph?.titleDisplacement = CGPoint(x: dataPoint!.x * -1, y: dataPoint!.y * -1)
barGraph?.titlePlotAreaFrameAnchor = .TopLeft
self.textBoxView?.layoutIfNeeded()
}
}
Based on some other questions I have seen about CorePlot I know I need to convert it from CorePlot layer to my new view. I left some of my experiments in viewWillAppear() to see what I have tried. None of the solutions on SO seemed to work and I am new to iOS so probably missing something. Any ideas of what conversions I need to do to get my label to show up properly across all screen sizes?
You're on the right track with the point conversion. This is how I would do it:
// Update layer layout if needed
self.view.layoutIfNeeded()
graphHostingView.layer.layoutIfNeeded()
// Point in data coordinates
let point: [Double] = [2.0, 50.0]
// Data point in plot area layer coordinates
var dataPoint = self.barGraph?.defaultPlotSpace?.plotAreaViewPointForPlotPoint(point)
// Convert data point to graph hosting view coordinates
dataPoint = graphHostingView.layer.convertPoint(dataPoint!, fromLayer: self.barGraph!.plotAreaFrame!.plotArea)
Use this to update the constraints on the text box relative to the graph hosting view.

Resources