I am trying to draw line chart in my project using SwiftChart pod. I am getting response from API as ["activity_count":0 , "date": "2020-10-31"] but graph works on double values so I don't know How I draw the chart.
import UIKit
import SwiftChart
class AnalyticsViewController: UIViewController {
#IBOutlet weak var graphContainer: UIView!
var graphData = [(x:0, y: 0.0)]
var graphpResponseData = [[String: Any]]()
override func viewDidLoad() {
super.viewDidLoad()
graphpResponseData.append(["activity_count":0 , "date": "2020-10-31"])
graphpResponseData.append(["activity_count":2 , "date": "2020-10-30"])
graphpResponseData.append(["activity_count":1 , "date": "2020-10-29"])
graphpResponseData.append(["activity_count":29 , "date": "2020-10-28"])
graphpResponseData.append(["activity_count":1 , "date": "2020-10-27"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-26"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-25"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-24"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-23"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-22"])
???? unable to proceed
//let chart = Chart(frame: CGRect(x: 0, y: 0, width: 300, height: 100))
//let series = ChartSeries(data: graphData)
//chart.add(series)
//chart.xLabels = [1, 2, 3, 4]
//chart.xLabelsFormatter = { String(Int(round($1))) + "Oct" }
//graphContainer.addSubview(chart)
}
}
desired output
I just had to solve this issue. I was using the day of the month of each date in memory for the x-axis, but it turned out that this would cause the graph to act unexpectedly and show randomly plotted points when the month changed.
I also tried using timestamps for x (of type Double, which would be accepted by the ChartSeries's data initializer, but this failed too.
The solution I found was to use an increment of one for each point on the x-axis, and using an array of [Date] whose indices corresponded with the order of the datapoints in your chart's data property (for the sake of example, call this correspondingDates). Inside of chart.xLabelsFormatter, I'd access this array using correspondingDates[value]. From there, I used two dateFormatter extensions to access the values.
Here's the code:
chart.xLabelsFormatter = { (index,value) in
//Format for label
let date = correspondingDates[Int(value)]
let dayAbbr = DateFormatter.getDay(date: date)
let monthAbbr = DateFormatter.getMonth(date: date)
return monthAbbr + "-" + dayAbbr
Extensions for DateFormatter:
extension DateFormatter{
/// Used specifically to get the Month Abrreviation (ie Feb), but can be used for formatting in other ways.
/// - Parameter format: Can be 'LLL' for the month abbreviated to 3 letters, or something like 'D/MM/Y'
/// - Returns: Returns the String version of the date, with precision at any level
static func getMonth(format:String = "LLL",date: Date = Date())->String{
let formatter = DateFormatter()
let today = date // formatter.da(from: Calendar.current.dateComponents([.day], from: Date()))
formatter.dateFormat = format
let string = formatter.string(from: today)
return string
}
/// Used specifically to get the Day Abrreviation (ie 05 or 27), but can be used for formatting in other ways.
/// - Parameters:
/// - format: Can be 'LLL' for the month abbreviated to 3 letters, or something like 'D/MM/Y'
/// - date: optionally pass in your own date
/// - Returns: Returns the String version of the day, with precision at any level
static func getDay(format:String = "dd",date: Date = Date())->String{
let formatter = DateFormatter()
let today = date // formatter.da(from: Calendar.current.dateComponents([.day], from: Date()))
formatter.dateFormat = format
let string = formatter.string(from: today)
return string
}
}
Related
I have a date in string format, "yyyy-MM-dd" and would like to return an array of the difference in dates in that same format from today.
For example, the given date is "2019-06-29", and today's date is 2019-06-25. The returned array would contain: ["2019-06-25", "2019-06-26", "2019-06-27", "2019-06-28", "2019-06-29"].
The method I am trying to write needs to also work cross-months/years. Is something like this possible in Swift?
What I have tried: calculating the difference in dates numerically (difference of days) and adding a day to the given date until it reaches today's date. This is what brought on the issue of exceeding 30/31 days and not moving to the next months/exceeding 2019-12-31 and not moving to 2020. Surely there is a simpler concise way to achieve this result without having to write that date logic manually?
extension Formatter {
static let date: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.calendar = Calendar(identifier: .iso8601)
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd"
return dateFormatter
}()
}
extension Date {
var noon: Date {
return Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)!
}
}
func dates(for date: String) -> [String] {
// For calendrical calculations you should use noon time
// So lets get endDate's noon time
guard let endDate = Formatter.date.date(from: date)?.noon else { return [] }
// then lets get today's noon time
var date = Date().noon
var dates: [String] = []
// while date is less than or equal to endDate
while date <= endDate {
// add the formatted date to the array
dates.append( Formatter.date.string(from: date))
// increment the date by one day
date = Calendar.current.date(byAdding: .day, value: 1, to: date)!
}
return dates
}
dates(for: "2019-06-29") // ["2019-06-25", "2019-06-26", "2019-06-27", "2019-06-28", "2019-06-29"]
I created a weather application and for it I used API. (I used OpenWeatherMap and API as "Call 5 day / 3 hour forecast data")
I got all of the information that I need.
Then I got data for all days of the week. For example, Monday temp 22 degrees, Tuesday temp 12 degrees, and etc.
I can get the current day of the week and added to dictionary.
But I can't get new dayName (day of the week). What do I need to do?
I need help getting the names of the next days of the week and their indexes. And then add the key value to the dictionary.
// Indexed all days of the week Sun, Mon, Wed and a t.c
let setWeekDay = IndexSet([1, 2, 3, 4, 5, 6, 7])
//I detected current day of the weak and added to dictionary
var weakDay = [String:Int]()
let calendar = Calendar.current
var weekday = calendar.component(.weekday, from: Date())
let components = DateComponents(weekday: weekday)
var dayName = calendar.nextDate(after: Date(), matching: components, matchingPolicy: .nextTime)?.Days()
weakDay = [dayName!:weekday]
if let nextWeekday = setWeekDay.integerGreaterThan(weekday) {
weekday = nextWeekday
} else {
weekday = setWeekDay.first!
}
...
// I extended date formatter
extension Date{
func Days() -> String{
let dataFormatter = DateFormatter()
dataFormatter.dateStyle = .short
dataFormatter.dateFormat = "EEEE"
return dataFormatter.string(from: self)
}
}
//This is dictionary will be change key and value dynamically.
//I need got dictionary as:
let weakDay = [
"Sunday": 1,
"Monday": 2,
"Tuesday": 3,
"Wednesday": 4,
"Thursday": 5,
"Friday": 6,
"Saturday": 7,
]
Thanks all of the one for help, but i found answer on my question myself!
What i was need to be do:
if let cell = tableView.dequeueReusableCell(withIdentifier: "daysId", for: indexPath) as? DaysTVCell {
var nameWeakAndIndex = [String:Int]()
var dateComponents = DateComponents()
dateComponents.setValue(1, for: .day);
var now = Date()
for index in 0...5 {
let tomorrow = Calendar.current.date(byAdding: dateComponents, to: now)
let detectDayIndex = index
nameWeakAndIndex = [now.Days():detectDayIndex]
now = tomorrow!
daysWeak.sort(by: { (nameWeakAndIndex[$0.dayOfTheWeak] ?? 6) > (nameWeakAndIndex[$1.dayOfTheWeak] ?? 6) })
}
let day = daysWeak[indexPath.row]
cell.configureCell(daysForecast: day)
return cell
I am trying to create an candlestick chart using Charts Framework using with Codable as JSON passing myclass can be shown as :
struct ChartDataPair: Codable {
var DateTime: String = ""
var Open: Double = 0.0
var High: Double = 0.0
var Low: Double = 0.0
var Close: Double = 0.0
}
Which creates an array of chartDataPairs as shown :
struct ChartData: Codable {
var chartDataPairs: [ChartDataPair]
}
The value that I am fetching will be shows below a bit as example :
{"chartDataPairs":
[{
"DateTime": "2018/10/1 10:00:01",
"Open": 50.05,
"High": 50.05,
"Low": 49.00,
"Close":49.00
},
{
"DateTime": "2018/10/1 10:05:02",
"Open": 51.05,
"High": 54.06,
"Low": 40.00,
"Close":45.06
},
{
"DateTime": "2018/10/1 10:10:02",
"Open": 50.05,
"High": 64.06,
"Low": 40.00,
"Close":58.06
}]
}
The data is just a sample so just wrote 3 values. Now I have to fetch only time and convert the DateTime String to Double to plot it in x-axis of the charts. For with I m using :
var dataEntries: [ChartDataEntry] = []
guard let financialData = dataChart.self else {
return
}
for chartData in financialData{
let open = chartData.Open
let close = chartData.Close
let high = chartData.High
let low = chartData.Low
let datetime = chartData.DateTime
let formatter = DateFormatter()
formatter.dateFormat = "YYYY/MM/dd HH:mm:ss"
let yourDate = formatter.date(from: datetime)
formatter.dateFormat = "HH:mm"
let myStringafd = formatter.string(from: yourDate!)
let time = myStringafd
let components = time.characters.split { $0 == ":" } .map { (x) -> Int in return Int(String(x))! }
let hours = components[0]
let minutes = components[1]
let double1 = Double("\(hours).\(minutes)")
let dataEntry = CandleChartDataEntry(x: double1! , shadowH: high, shadowL: low, open: open, close: close)
dataEntries.append(dataEntry)
}
let chartDataSet = CandleChartDataSet(values: dataEntries, label: "")
chartDataSet.axisDependency = .left
chartDataSet.drawIconsEnabled = false
chartDataSet.shadowColor = .darkGray
chartDataSet.shadowWidth = 0.7
chartDataSet.decreasingColor = .red
chartDataSet.decreasingFilled = true // fill up the decreasing field color
chartDataSet.increasingColor = UIColor(red: 122/255, green: 242/255, blue: 84/255, alpha: 1)
chartDataSet.increasingFilled = true // fill up the increasing field color
chartDataSet.neutralColor = .blue
chartDataSet.barSpace = 1.0
chartDataSet.drawValuesEnabled = false
let chartData = CandleChartData(dataSet: chartDataSet)
candlestickView.data = chartData
I know that the conversion of the time to double ins't correct as per it is needed, Here I need some help on converting the datetime to double value.
The second issue is the bar width of the candlestick, I am unable to decrease the width of the candlestick.
And I want to fill up the x-axis with the time value like HH:MM with certain intervals like 15 mins, 50 mins, 4 hrs etc.
For which I followed few questions and suggestions here in given link below :
iOS-Charts Library: x-axis labels without backing data not showing
On this issue: candlestickView.xAxisRenderer = XAxisWeekRenderer()
isn't working. It is calling for viewporthandler, x-axis and transformation.
Though I can get the custom labels from the custom IAxisValueFormatter. The interval between the two values in the x-axis is not what I wanted it to be like in 15 mins or 50 mins or 4 hrs etc.
ios Charts 3.0 - Align x labels (dates) with plots
On the above mentioned link I am unable to get the minTimeInterval
and referenceTimeInterval
Basically What I want to do here is plot the hour and minute form the string that I am fetching from the JSON in x-axis and create a custom interval in between the values of x-axis while creating the custom x-axis labels.
My chart is currently shown as :
Candlestick Chart
I have same thing to display so I used this way to display the data
let xAxis = chartView.xAxis
var dataPoints = [String]()
for i in 0 ..< arrData.count
{
let timeStampFrom = arrData[i].time
dataPoints.append(self.stringFromTimestamp(timeStampFrom, strDateFormat: "h a"))
}
xAxis.valueFormatter = IndexAxisValueFormatter(values:dataPoints)
xAxis.setLabelCount(5, force: false)
Some needed function
func stringFromTimestamp(_ timeInterval : Double ,strDateFormat : String)->String
{
let dateFormatter = DateFormatter()
self.setDateFormat(dateFormatter,dateFormat: strDateFormat)
let date = Date(timeIntervalSince1970: TimeInterval(timeInterval))
return dateFormatter.string(from: date)
}
here is the output
I'm pretty new to swift (and programming altogether). I'm trying to convert an Int into a String. I've tried using switch statements but every time I use them, it never changes to the String (AKA it prints the number 4) An example of what I'm trying to do is as follows:
class Birthday(_ month: Int, _ day:Int, _ year:Int) -> String{
//Here is where I'd like to turn my month into April
Return (month)
}
let example = Birthday()
example(4,15,1988)
If you really just want to get a month name from a month number, you can do the following:
let formatter = DateFormatter()
let monthName = formatter.monthSymbols[month - 1] // Assuming 1 means January
But since you are passing in a month, day, and year, you presumably want to create a Date and then you want to format that Date into a `String.
Create a Date using Calendar and DateComponents.
let date = Calendar.current.date(from: DateComponents(year: year, month: month, day: day))
Then you format the Date into a String using DateFormatter.
let formatter = DateFormatter()
formatter.dateStyle = .long // choose a desired style
formatter.timeStyle = .none
let string = formatter.string(from: date)
You can use a dictionary which maps objects to each other. For example, a months dictionary could look like:
let months: [Int:String] = [1:"January", 2:"February",...]
return months[4] // returns "April"
Simple solution to get you started would be a method that takes an integer and return your month string.
func numberToMonth(number: Int) -> String {
guard number > 0, number < 13 else { return "" }
return DateFormatter().monthSymbols[number-1]
}
I need some help in order to establish a new array in Swift 2.0.
I have two arrays, one contains dates and the other payments at this date.
let year = [February 2016, March 2016, June 2017, October 2017, January 2018, April 2019] // Data at which a payment is initiated
let payment = ["1000","2000,"3000","1000","2000,"3000"] // payment amount at date in array year
I'd like to create two new arrays in Swift code based on this.
The final result should look like this:
let yearSum [2016, 2017, 2018,2019] // only full year
let paymentSum ["3000","4000","2000","3000"] // sum of all payment in the year
The array "yearSum" should contain only the full year number, while "paymentSum" should contain the sum of all payments in the year.
Has anybody an advice how I can code this?
Many thanks
The input
First of all let's assign good names to the input constants
let monthStrings = ["February 2016", "March 2016", "June 2017", "October 2017", "January 2018", "April 2019"]
let paymentStrings = ["1000", "2000", "3000", "1000", "2000", "3000"]
What can go wrong
We are working with strings as input, so many things could go wrong during the parsing of a Date or of an Int. For clarity lets define the following enum
enum Error: ErrorType {
case InputParamsHaveDifferentSizes(Int, Int)
case FirstParamHasInvalidDate
case SecondParamHasInvalidInt
}
The function
func groupData(monthStrings: [String], paymentsStrings:[String]) throws -> [Int:Int] {
// make sure both arrays have the same size
guard monthStrings.count == paymentStrings.count
else { throw Error.InputParamsHaveDifferentSizes(monthStrings.count, paymentStrings.count) }
let formatter = NSDateFormatter()
formatter.dateFormat = "MMM yyyy"
// creates dates: an array of NSDate representing monthStrings
// if dates has a different size of monthsString then throws and error
guard
case let dates = (monthStrings.flatMap { formatter.dateFromString($0) })
where dates.count == monthStrings.count
else { throw Error.FirstParamHasInvalidDate }
// creates payments: an array of Int representing paymentsStrings
// if payments has a different size of paymentsStrings then throws and error
guard
case let payments = (paymentStrings.flatMap { Int($0) })
where payments.count == paymentStrings.count
else { throw Error.SecondParamHasInvalidInt }
// put togheter dates and payments and group the results by year
return zip(dates, payments).reduce([Int:Int]()) { (var result, elm) -> [Int:Int] in
let year = NSCalendar.currentCalendar().components([NSCalendarUnit.Year], fromDate: elm.0).year
result[year] = elm.1 + (result[year] ?? 0)
return result
}
}
Usage
let res = try groupData(monthStrings, paymentsStrings: paymentStrings)
print(res) // [2018: 2000, 2017: 4000, 2016: 3000, 2019: 3000]
Update
In the comment below you say you need to access the keys by index and you need them sorted so
let sortedKeys = res.keys.sort()
func value(index:Int) -> String? {
let key = sortedKeys[index]
let value = res[key]
return value
}
Well, due to lack of response I'll just assume years is an array of strings:
// Data
let years = ["February 2016", "March 2016", "June 2017", "October 2017", "January 2018", "April 2019"]
let payed = ["1000", "2000", "3000", "1000", "2000", "3000"]
// Preparation
var result: [String: Double] = [:]
let digits = NSCharacterSet.decimalDigitCharacterSet().invertedSet
// Results
let yearOnly = years.flatMap { $0.componentsSeparatedByCharactersInSet(digits).filter { !$0.isEmpty } }
zip(yearOnly, payed).forEach { result[$0.0] = (result[$0.0] ?? 0) + (Double($0.1) ?? 0) }
Note that yearOnly is created by filtering out all non-digits, so if you'll have days inside of years then this won't work and you'll have to use another method to filter out the years.
// Output
["2018": 2000.0, "2019": 3000.0, "2016": 3000.0, "2017": 4000.0]