Create a SwiftUI line graph using arrays and the Swift Charts framework - swift

In a SwiftUI app, I'm trying to pass an array of Y values to the Swift Charts framework to draw a line graph. I would like to create the X values based on the length or count of the Y data. I tried the approach shown below but it doesn't work because Charts wants the data as an array of structs. Is there a way to pass arrays directly to the Charts framework?
import SwiftUI
import Charts
struct ContentView: View {
let ydata = [1, 4.5, 3, 6, 7, 5.2, 9, 12.5]
let xdata = Array(0..<ydata.count)
let data = [xdata, ydata]
var body: some View {
Chart(data) {
LineMark(
x: .value("X values", $0),
y: .value("Y values", $1)
)
}
}
}

You can use an array of tuples, here I am using a computed property for the data
var data: [(Int, Double)] {
Array(zip(Array(0..<ydata.count), ydata))
}
When using a tuple we need to tell Chart what the id is so the use of data is a little different
var body: some View {
Chart(data, id: \.0) { tuple in
LineMark(
x: .value("X values", tuple.0),
y: .value("Y values", tuple.1)
)
}
}

Related

How to add a placeholder for a Swift Chart that doesn't have any data yet?

Imagine this chart doesn't have any data.
What api do i use to display a placeholder like "no data yet"?
I've searched WWDC and apple docs.
Maybe it's just a case of hiding the chart and showing some text.
import SwiftUI
import Charts
struct TopStyleChart: View {
let data = [
(name: "Cachapa", sales: 916),
(name: "Injera", sales: 850),
(name: "Crêpe", sales: 802),
(name: "Jian Bing", sales: 753),
(name: "Dosa", sales: 654),
(name: "American", sales: 618)
]
var body: some View {
Chart(data, id: \.name) {
BarMark(
x: .value("Sales", $0.sales),
y: .value("Name", $0.name)
)
// Set the foreground style of the bars.
.foregroundStyle(.pink)
// Customize the accessibility label and value.
.accessibilityLabel($0.name)
.accessibilityValue("\($0.sales) sold")
}
}
}
A simple if clause should do the trick here:
struct TopStyleChart: View {
let data: [(name: String, sales: Int)]?
var body: some View {
if let data = data{
Chart(data, id: \.name) {
BarMark(
x: .value("Sales", $0.sales),
y: .value("Name", $0.name)
)
// Set the foreground style of the bars.
.foregroundStyle(.pink)
// Customize the accessibility label and value.
.accessibilityLabel($0.name)
.accessibilityValue("\($0.sales) sold")
}
} else{
Text("no data yet")
}
}
}

swift use TabularData and Charts together

I tried to use specified columns from a DataFrame from TabularData in a Charts LineMark, but couldn't subtract the .value's.
ForEach(theXYdata, id: \.x) { //item in
LineMark(
x: .value("x", $0.x),
y: .value("y", $0.y)
)
.interpolationMethod(.catmullRom)
.symbol(Circle())
.foregroundStyle(.orange)
}
}

Swift create object matrix

I'm new to Swift and I am trying to create a array of arrays of a custom object. I did some research and what mostly came up online is:
Array(repeating: Array(repeating: [Object](), count: y), count: x)
or similar but I haven't been able to get them working for me. ( Deprecated because different swift versions,etc.. ) Right now I have
class ChessPiece {
// class definition...
}
class ChessBoard {
var board: [[ChessPiece]] = []
init() {
board = [[ChessPiece(),ChessPiece(),ChessPiece()],
[ChessPiece(),ChessPiece(),ChessPiece()],
[ChessPiece(),ChessPiece(),ChessPiece()]]
}
}
But what if I had 100 rows or columns? Isn't there a more efficient and direct way to create a matrix with x rows and y columns?
I just do with normal for-in loop
class ChessPiece {
// class definition...
}
class ChessBoard {
var board: [[ChessPiece]] = []
init(row: Int, column: Int) {
for _ in 1...row {
var innerArray: [ChessPiece] = []
for _ in 1...column {
innerArray.append(ChessPiece())
}
board.append(innerArray)
}
}
}
let chessBoard = ChessBoard(row: 8, column: 8)
The function you mentioned is fine Array(repeating:count:).
This works on my playground:
struct ChessPiece {}
func makeChessPlate(dimension: Int) -> [[ChessPiece]] {
return Array(repeating: Array(repeating: ChessPiece(), count: dimension), count: dimension)
}
print(makeChessPlate(dimension: 2)) // Result: [[ChessPiece, ChessPiece],[ChessPiece, ChessPiece]]
EDIT: Notice that my example works only because I used a struct instead of a class. On the contrary to classes, structs are copied by values, then this results in an array of unique objects.
You can use this loop to create a multidimensional array.
class ChessPiece{
}
var numColumns = 27
var numRows = 52
var array = [[ChessPiece]]()
for column in 0...numColumns {
array.append(Array(repeating: ChessPiece(), count:numRows))
}
This would create an array of array of ChessPieces.

How to have 'adaptive' structure name?

So I am creating a struct and putting the results into an array like so
struct roundResults {
let round: Int
let team1Total: Int
let team2Total: Int
}
var allResults = [roundResults]()
Then I am filling the struct and appending it to the array like so
func createStruct(){
let (result) = roundResults(round:RoundNumber , team1Total:team1GameTotal , team2Total:team2GameTotal)
allResults.append(result)
}
And when I print the data from the array it gives me this
[Game.ScoreScreen.roundResults(round: 1, team1Total: 0, team2Total: 3),
Game.ScoreScreen.roundResults(round: 2, team1Total: 0, team2Total: 10)]
What I want to achieve is that instead of the struct being labeled like
Game.ScoreScreen.roundResults
I get something where roundResults represents the current round
I created a new variable and tried to use this var in lieu of round results when created the structure but this doesn't seem to work
func createCall(){
roundCall = "results" + String(RoundNumber)
}
What I would like to have is something like let (result) = (roundCall)(round:RoundNumber... so I have an easy way to pull each array depending on what round I am currently in and the printed data would look something like
[Game.ScoreScreen.results1(round: 1,
Game.ScoreScreen.results2(round: 2, etc...
You're currently just using the default print behaviour of structs.
You can make your struct conform to the [CustomStringConvertible][1] protocol by implementing thedescription` property. This lets you specify how you would like your struct to be represented in a string format, such as for printing.
Here's an example:
struct roundResults: CustomStringConvertible {
let round: Int
let team1Total: Int
let team2Total: Int
var description: String {
return "Round \(round) results: Team 1: \(team1Total) vs Team 2: \(team2Total)" // TODO: Adjust this to work as you wish.
}
}
Now when you print your array, it'll show:
["Round 1 results: Team 1: 0 vs Team 2: 3", "Round 2 results: Team 1: 0 vs Team 2: 10"]

Storing Swift value types in Core Data

Is there a way to store Swift's value types, such as enums or structs in Core Data?
What's the best way of doing this?
It's actually pretty easy: Just initialise NSData with the bytes of your value type.
Your value type:
struct MyStruct {
let x : Int
let y : Bool
let z : Double
}
Functions to set and get the data
func valueToData<T>(var t: T) -> NSData {
return NSData(bytes: &t, length: sizeof(T))
}
func dataToValue<T>(inout t: T, data: NSData) {
memcpy(&t, data.bytes, data.length)
}
Tests:
let originalValue = MyStruct(x: 3, y: true, z: 1.3)
let data = valueToData(originalValue)
// Save data to CoreData
var s = MyStruct(x: 0, y: false, z: 0) // Empty Value
dataToValue(&s, data: data) // Initialize with data
// s is MyStruct(x: 3, y: true, z: 1.3)
A suitable way for enum is to declare the enum with a raw type supported by Core Data and use a custom instance property to convert the enum case to raw value and vice versa.
For example:
enum State : Int32 {
case None, Empty, Full
}
#NSManaged var state: Int32
var stateEnum: State {
get {
return State(rawValue: state)!
}
set {
state = newValue.rawValue
}
}
A struct has to be "serialized" in a similar way – maybe it could be even represented by an Core Data entity – but this depends on the struct(ure) of the struct.