How to create a Chart with Date / Time on the bottom Axis using iOS Charts Library? - ios

I am currently working with the iOS Library Charts and have some issues to implement a TimeLine for the x Axis.
Library Link: https://github.com/danielgindi/Charts
I have a List of Objects as the Input for the Chart.
let objectList : [Object] = fillObjectList()
class Object {
var timeCreated : Date
var value : Double
}
I already implemented a solution where I just used the timeCreated.timeIntervalsince1970 as the values for the x Axis. But this does not look so great.
So if some of you have some experience using iOS Charts Library, I hope you guys can think of a solution for this problem. Thanks a lot for your help in advance!

You need to create implementation class of IAxisValueFormatter, something like this
note: I did not compile it, so may need some correction
public class DateValueFormatter: NSObject, IAxisValueFormatter {
private let dateFormatter = DateFormatter()
private let objects:[Object]
init(objects: [Object]) {
self.objects = objects
super.init()
dateFormatter.dateFormat = "dd MMM HH:mm"
}
public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
if value >= 0 && value < objects.count{
let object = objects[Int(value)]
return dateFormatter.string(from: object.timeCreated)
}
return ""
}
}
and use the formatted with this code
xAxis.valueFormatter = DateValueFormatter(objects: objectList)
edit:
you can see some example what I try using charts lib in this repo

Related

How to format a date field in a Leaf template using Vapor 4

In a list, I want to show dates fetched form the database. If I use:
#Date(timeStamp: appointment.appointmentDate,localizedFormat: "E, dd-MM-yyyy")
I would expect: Wed, 30/12/2020 BUT I get Wed, 12/30/2020 which I find very strange since I specifically ask for dd-MM
I then tried:
#Date(timeStamp: appointment.appointmentDate,fixedFormat: "E, dd-MM-yyyy")
with works okay and provides me with: Wed, 30/12/2020
However, I'm still not happy...
I want to have it presented with - instead of / : Wed, 30-12-2020
and
Since my app will be localized, I would like to have control on the way the day is shown: Wed (Eng), Mer(French), Woe (Dutch) so how do I set which locale should be used in Leaf? (I know how to do it in Vapor but I'd rather leave it up to Leaf if it is possible.)
Create Leaf method:
import Foundation
import Leaf
public struct DataLeafFunction: LeafFunction, StringReturn, Invariant {
public static var callSignature: [LeafCallParameter] { [
.double,
.string(labeled: nil, optional: true, defaultValue: "yyyy-MM-dd")
] }
public func evaluate(_ params: LeafCallValues) -> LeafData {
guard let timestamp = params[0].double else { return .string(params[0].string) }
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = params[1].string
let date = Date(timeIntervalSinceReferenceDate: timestamp)
return .string(dateFormatter.string(from: date))
}
}
Add function to configure:
func configure(_ app: Application) throws {
LeafEngine.entities.use(DataLeafFunction(), asFunction: "date")
// ...
}
Use this function in your templates:
#date(date)
#date(date, "YY/MM/dd")

Displaying integer values on SliderRow

I'm trying display the integer values instead of float. I tried the following:
section.append(SliderRow() {
$0.title = "Number of items"
$0.tag = "numberOfItems"
$0.minimumValue = 1
$0.maximumValue = 10
$0.steps = 10
$0.value = 5
$0.onChange { row in
let isInteger = floor(row.value!) == row.value!
if (!isInteger) {
row.value = floor(row.value!)
}
let formattedString = String(format: "%.0f", row.value!)
row.cell.valueLabel.text = formattedString
}
})
formattedString displays the values I want but I'm not able to display them on the screen. I can access all other attributes by row.cell. I change change the text colour, for instance, but not the text. I'm guessing I'm setting the text but it gets overwritten shortly.
Is there a way to make the slider show integer values?
Thanks.
since Dec 2, 2016, there's a much simpler approach.....
$0.displayValueFor = {
return "\(Int($0 ?? 0))"
}
(credit to https://github.com/xmartlabs/Eureka/issues/817 showing how to do it - and the associated fix)
This works great for showing an Int in the Eureka SliderRow as well as StepperRow
See this picture of xcode Simulator showing Int values on StepperRow and SliderRow
To resolve the issue I cloned the Eureka repository. Turns out I was right about overwriting the values. To test it I set the text to a constant value inside onChange handler, then added print statements in the SliderRow source code in valueChanged function:
if shouldShowTitle() {
print("current value: \(valueLabel.text)" )
valueLabel.text = "\(row.value!)"
print("updated value: \(valueLabel.text)" )
}
The output looked like this:
current value: Optional("xyz")
updated value: Optional("3.0")
current value: Optional("xyz")
updated value: Optional("5.0")
So I created myself a SliderRowInt class that uses integers:
public final class SliderRowInt: Row<Int, SliderCellInt>, RowType {
public var minimumValue: Int = 0
public var maximumValue: Int = 10
public var steps: UInt = 20
required public init(tag: String?) {
super.init(tag: tag)
} }
Now I'm using the integer version. Maybe not the most elegant solution but it worked fine for me.
There's a similar issue with the StepperRow which I solved the same way.
I checked in both classes to my cloned repository in case anyone encounters the same problem they can be found here: https://github.com/volkanx/Eureka/

Swift: Appending object inside dictionary

I'd like to add my dictionary with array in it to my arrayOfDictionary, i tried the suggested answer on this link, but it didn't work for me. I'm having problem on appending object in my dictionary.
Here's my code:
func formatADate() {
var dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.LongStyle
var journalDictionary = Dictionary<String, [Journal]>()
for index in 0..<self.sortedJournal.count {
let dateString = dateFormatter.stringFromDate(self.sortedJournal[index].journalDate)
var oldDateString = ""
// Split Date
var dateStringArr = split(dateString){$0 == " "}
var newDateString = "\(dateStringArr[0]) \(dateStringArr[2])"
// Append journal to corresponding dates
// journalDictionary[newDateString]? += [self.sortedJournal[index]]
// journalDictionary[newDateString]!.append(self.sortedJournal[index])
journalDictionary[newDateString]?.append(self.sortedJournal[index])
// add to array of journal
if !(newDateString == oldDateString) || (oldDateString == "") {
arrayOfJournalDictionary.append(journalDictionary)
}
}
println(arrayOfJournalDictionary)
}
With this line of code, it gives me empty object.
journalDictionary[newDateString]?.append(self.sortedJournal[index])
Any suggestion on how I can accomplish/fix this? I'm pretty amateur in Swift, so any help would be appreciated! Thank you.
Woah, date magic with string splits and stuff, yeiks. You already are using NSDateFormatters, for the wrong reasons though. Since journalDate is a date, use the formatter to give you the exact string you want from that date, dont let it give you some generic string and split and concatenate and argh... NOBODY will understand what you are trying to do there.
Back to the question:
Your journalDictionary is empty, if you write
journalDictionary[newDateString]?.append(self.sortedJournal[index])
You tell the program to read at "index" newDateString and if there is something take that and append a new value to it.
Do you see the mistake already?
if there is something -> You have no logic yet to insert the initial empty [Journal] for any given key.
At some point in your code you have to set up the array like so:
journalDictionary["myKey"] = []
Or probably more likely:
if journalDictionary[newDateString] == nil {
journalDictionary[newDateString] = [self.sortedJournal[index]]
} else {
journalDictionary[newDateString]?.append(self.sortedJournal[index])
}

Main.Type does not have a member named 'motionManager' [Swift] [duplicate]

I'm playing around with a Swift playground working on a new class. For some reason I keep getting an error that the class "does not have a member type" with the name of a constant defined three lines earlier. Here is the code:
import Foundation
class DataModel {
let myCalendar = NSCalendar.autoupdatingCurrentCalendar()
var myData = [NSDate : Float]()
let now = NSDate()
let components = myCalendar.components(.CalendarUnitYear | .CalendarUnitMonth, fromDate: now)
}
Xcode Beta6 keeps give me an error on the second to last line, saying that "DataModel.Type does not have a member named 'myCalendar'
Though I don't think it should make a difference, I have tried defining myCalendar as a var.
You cannot initialize an instance class property referencing another instance property of the same class, because it's not guaranteed in which order they will be initialized - and swift prohibits that, hence the (misleading) compiler error.
You have to move the initialization in a constructor as follows:
let components: NSDateComponents
init() {
self.components = myCalendar.components(.CalendarUnitYear | .CalendarUnitMonth, fromDate: now)
}
I agree with #Antonio The other way might be to create struct if you don't want to use init:
class DataModel {
struct MyStruct {
static var myCalendar:NSCalendar = NSCalendar.autoupdatingCurrentCalendar()
static let now = NSDate()
}
var myData = [NSDate : Float]()
var components = MyStruct.myCalendar.components(.CalendarUnitYear | .CalendarUnitMonth, fromDate: MyStruct.now)
}
Test
var model:DataModel = DataModel()
var c = model.components.year // 2014

String to Double in XCode 6's Swift [duplicate]

This question already has answers here:
Swift - How to convert String to Double
(30 answers)
Closed 7 years ago.
How do I convert a string to double in Swift? I've tried string.doubleValue or string.bridgeToObjectiveC().doubleValue and none of it works. Any ideas?
You can create a read-only computed property string extension to help you convert your strings to double:
You can use NSNumberFormatter
extension String {
struct Number {
static let formatter = NSNumberFormatter()
}
var doubleValue: Double {
return Number.formatter.numberFromString(self)?.doubleValue ?? 0
}
}
or you can cast it to NSString and extract its doubleValue property:
extension String {
var ns: NSString {
return self
}
var doubleValue: Double {
return ns.doubleValue
}
}
"2.35".doubleValue + 3.3 // 5.65
According to Stanford CS193p course Winter 2015, the correct way to get a double from a String in Swift is to use NSNumberFormatter instead of NSString:
let decimalAsString = "123.45"
let decimalAsDouble = NSNumberFormatter().numberFromString(decimalAsString)!.doubleValue
If you want to be safe (regarding the optional unwrapping and the decimal separator), you'd use:
let decimalAsString = "123.45"
var formatter = NSNumberFormatter()
formatter.locale = NSLocale(localeIdentifier: "en_US")
if let decimalAsDoubleUnwrapped = formatter.numberFromString(decimalAsString) {
decimalAsDouble = decimalAsDoubleUnwrapped.doubleValue
}
Safe unwrapping is particularly useful if you parse a XML or JSON file and you need to make sure you have a String that can be converted into a Double (the program will crash if you force-unwrap an optional that is actually nil).
/!\
EDIT: be careful, NSNumberFormatter works differently than NSString.
NSString allowed you to do things like : (dictionary[key] as NSString).doubleValue, provided that dictionary[key] used the '.' as decimal separator (like in "123.45").
But NSNumberFormatter(), by default, initializes an instance of NSNumberFormatter with your current system Locale!
Therefore, NSNumberFormatter().numberFromString(decimalAsString)!.doubleValue would work with 123.45 on a US device, but not on a French one for example!
If you are parsing a JSON file for example, and you know that values are stored using '.' as the decimal separator, you need to set the locale of your NSNumberFormatter instance accordingly:
var formatter = NSNumberFormatter()
formatter.locale = NSLocale(localeIdentifier: "en_US")
then:
let decimalAsString = "123.45"
if let decimalAsDoubleUnwrapped = NSNumberFormatter().numberFromString(decimalAsString) {
decimalAsDouble = decimalAsDoubleUnwrapped.doubleValue
}
In that case, decimalAsDouble will correctly return 123.45 as a doubleValue.
But it would return nil if decimalAsString = "123,45".
Or if the NSLocale was set as "fr_FR".
On the other hand, a NSNumberFormatter using a NSLocale with fr_FR would work perfectly with "123,45", but return nil with "123.45".
I thought that was worth reminding.
I updated my answer accordingly.
EDIT : also, NSNumberFormatter wouldn't know what do with things like "+2.45%" or "0.1146"(you would have to define the properties of your NSNumberFormatter instance very precisely). NSString natively does.
you can always just cast from String to NSString like this
let str = "5"
let dbl = (str as NSString).doubleValue
Try this :
var a:Double=NSString(string: "232.3").doubleValue
Try this:
let str = "5"
let double = Double(str.toInt()!)
another way is:
let mySwiftString = "5"
var string = NSString(string: mySwiftString)
string.doubleValue
this latter one posted here:
Swift - How to convert String to Double

Resources