I have a view that displays a chart based on an array. Each element in the array is all of the attributes associated with a core data entity. The data is properly displayed. I would like to animate the rendering of the charts data but cannot seem to figure out how to do it. If someone could point me in the right direction it would be appreciated. Below is the code.
struct ClosingValuesChart: View {
@State private var selectedDate: Date? = nil
@State private var selectedClose: Float? = nil
@State private var xAxisLabels: [Date] = []
var closingValues: [TradingDayClose] = []
var heading: String = ""
init(fundName: String, numYears: Int, closingValues: [TradingDayClose]) {
self.heading = fundName + String(" - \(numYears) Year")
self.closingValues = closingValues
}
var body: some View {
GroupBox (heading) {
let xMin = closingValues.first?.timeStamp
let xMax = closingValues.last?.timeStamp
let yMin = closingValues.map { $0.close }.min()!
let yMax = closingValues.map { $0.close }.max()!
let xAxisLabels: [Date] = GetXAxisLabels(xMin: xMin!, xMax: xMax!)
var yAxisLabels: [Float] {
stride(from: yMin, to: yMax + ((yMax - yMin)/7), by: (yMax - yMin) / 7).map { $0 }
}
Chart {
ForEach(closingValues) { value in
LineMark(
x: .value("Time", value.timeStamp!),
y: .value("Closing Value", value.close)
)
.foregroundStyle(Color.blue)
.lineStyle(StrokeStyle(lineWidth: 1.25))
}
}
.chartXScale(domain: xMin!...xMax!)
.chartXAxisLabel(position: .bottom, alignment: .center, spacing: 25) {
Text("Date")
.textFormatting(fontSize: 14)
}
.chartXAxis {
AxisMarks(position: .bottom, values: xAxisLabels) { value in
AxisGridLine(centered: true, stroke: StrokeStyle(lineWidth: 1))
AxisValueLabel(anchor: .top) {
if value.as(Date.self) != nil {
Text("")
}
}
}
}
.chartYScale(domain: yMin...yMax)
.chartYAxisLabel(position: .leading, alignment: .center, spacing: 25) {
Text("Closing Value")
.font(Font.custom("Arial", size: 14))
.foregroundColor(.black)
}
.chartYAxis {
AxisMarks(position: .leading, values: yAxisLabels) { value in
AxisGridLine(centered: true, stroke: StrokeStyle(lineWidth: 1))
AxisValueLabel() {
if let labelValue = value.as(Double.self) {
Text(String(format: "$ %.2f", labelValue))
.textFormatting(fontSize: 12)
}
}
}
}
.chartOverlay { proxy in
GeometryReader { geometry in
ChartOverlayRectangle(selectedDate: $selectedDate, selectedClose: $selectedClose, proxy: proxy, geometry: geometry, xMin: xMin!, xMax: xMax!, closingValues: closingValues)
}
}
.overlay {
XAxisDates(dateLabels: xAxisLabels)
}
.overlay {
DateAndClosingValue(selectedDate: selectedDate ?? Date(), selectedClose: selectedClose ?? 0.0)
}
}
.groupBoxStyle(ChartGroupBoxStyle())
}
}
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I have an SQLite table that holds a fund name, as a string, a time stamp, as an iso8601 string, and a close, as a double. SQLite appears to call strings text and a double a real. In the function below, I query this table with a select statement to return the last year of data based on the latest date in the table. To get the latest date I have a separate function that finds the latest date, converts if from a string to a date, subtracts 1 year from the date then converts it back to an iso8601 date string. It is this date string that is being passed into the function below as the variable startingDate. The function below works just fine and is relatively fast. What I am trying to determine is there a way to modify the select statement in the function below such that it does everything and I will no longer have to separately calculate the starting date and pass it in?
func QuerySQLiteData(db: OpaquePointer?, fundName: String, startingDate: String) -> [TradingDay] {
var queryResults: [TradingDay] = []
let timeStampFormatter = ISO8601DateFormatter()
let queryTradingDaysStatement = """
Select
FundName,
TimeStamp,
Close
FROM
TradingDays
WHERE
FundName = '\(fundName)'
AND
TimeStamp >= '\(startingDate)'
ORDER By
TimeStamp ASC
;
"""
var queryTradingDaysCPtr: OpaquePointer?
var tradingDay: TradingDay = TradingDay(fundName: "", timeStamp: Date(), close: 0.0)
if sqlite3_prepare_v2(db, queryTradingDaysStatement, -1, &queryTradingDaysCPtr, nil) == SQLITE_OK {
while (sqlite3_step(queryTradingDaysCPtr) == SQLITE_ROW) {
let fundName = sqlite3_column_text(queryTradingDaysCPtr, 0)
let timeStamp = sqlite3_column_text(queryTradingDaysCPtr, 1)
let close = sqlite3_column_double(queryTradingDaysCPtr, 2)
let fundNameAsString = String(cString: fundName!)
let timeStampAsString = String(cString: timeStamp!)
let timeStampAsDate = timeStampFormatter.date(from: timeStampAsString)!
tradingDay.fundName = fundNameAsString
tradingDay.timeStamp = timeStampAsDate
tradingDay.close = close
queryResults.append(tradingDay)
} // end while loop
} else {
let errorMessage = String(cString: sqlite3_errmsg(db))
print("\nQuery is not prepared \(errorMessage)")
}
sqlite3_finalize(queryTradingDaysCPtr)
return queryResults
}
I am creating my own NSTableView for macOS. I can display formatted data and provide scrolling by embedding it in a ScrollView. When the table is displayed there is a horizontal gap between adjacent cells. The same gap occurs between the ScrollView header cells. I do not know how to. solve this problem. There is one other issue, I do not know how to set the vertical height of the ScrollView header cells. I would like to make them taller. Below is my code along with the test data.
struct ClosingValue: Identifiable {
var id: UUID
var name: String
var date: String
var close: Float
static var closingValues = [
ClosingValue(id: UUID(), name: "Stock1", date: "Nov 01, 2009", close: 1.10),
ClosingValue(id: UUID(), name: "Stock1", date: "Nov 02, 2009", close: 2.10),
ClosingValue(id: UUID(), name: "Stock1", date: "Nov 03, 2009", close: 3.10),
ClosingValue(id: UUID(), name: "Stock1", date: "Nov 04, 2009", close: 4.10),
ClosingValue(id: UUID(), name: "Stock1", date: "Nov 05, 2009", close: 5.10)
]
}
struct BetterTableView: NSViewRepresentable {
class Coordinator: NSObject, NSTableViewDelegate, NSTableViewDataSource {
@State var closingValues: [ClosingValue] = ClosingValue.closingValues
func numberOfRows(in tableView: NSTableView) -> Int {
closingValues.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
var nsView = NSView()
let tableColumnWidth: CGFloat = 135
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
let attributes1: [NSAttributedString.Key: Any] = [
.foregroundColor: NSColor.blue,
.paragraphStyle: paragraphStyle,
.font: NSFont(name: "Arial", size: 14.0) as Any
]
switch tableColumn {
case tableView.tableColumns[0]:
tableColumn?.width = tableColumnWidth
let attributedString = NSAttributedString(string: closingValues[row].name, attributes: attributes1)
let tempNSView = NSTextField()
tempNSView.backgroundColor = NSColor.white
tempNSView.isBordered = true
tempNSView.isEditable = false
tempNSView.attributedStringValue = attributedString
nsView = tempNSView
case tableView.tableColumns[1]:
tableColumn?.width = tableColumnWidth
let attributedString = NSAttributedString(string: closingValues[row].date, attributes: attributes1)
let tempNSView = NSTextField()
tempNSView.backgroundColor = NSColor.white
tempNSView.isBordered = true
tempNSView.isEditable = false
tempNSView.attributedStringValue = attributedString
nsView = tempNSView
case tableView.tableColumns[2]:
tableColumn?.width = tableColumnWidth
let closeAsString = String(format: "$%.2f", closingValues[row].close)
let attributedString = NSAttributedString(string: closeAsString, attributes: attributes1)
let tempNSView = NSTextField()
tempNSView.backgroundColor = NSColor.white
tempNSView.isBordered = true
tempNSView.isEditable = false
tempNSView.attributedStringValue = attributedString
nsView = tempNSView
default:
print("problem in table view switch statement")
}
return nsView
}
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
func makeNSView(context: Context) -> NSScrollView {
let tableView = NSTableView()
tableView.delegate = context.coordinator
tableView.dataSource = context.coordinator
tableView.addTableColumn(NSTableColumn())
tableView.addTableColumn(NSTableColumn())
tableView.addTableColumn(NSTableColumn())
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: NSColor.black,
.paragraphStyle: paragraphStyle,
.font: NSFont.systemFont(ofSize: 14)
]
let column0AttributedString = NSAttributedString(string: "Stock", attributes: attributes)
let column0Header = tableView.tableColumns[0]
column0Header.headerCell.drawsBackground = true
column0Header.headerCell.backgroundColor = NSColor.systemMint
column0Header.headerCell.alignment = .center
column0Header.headerCell.attributedStringValue = column0AttributedString
let column1AttributedString = NSAttributedString(string: "Date", attributes: attributes)
let column1Header = tableView.tableColumns[1]
column1Header.headerCell.drawsBackground = true
column1Header.headerCell.backgroundColor = NSColor.systemMint
column1Header.headerCell.alignment = .center
column1Header.headerCell.attributedStringValue = column1AttributedString
let column2AttributedString = NSAttributedString(string: "Closing Value", attributes: attributes)
let column2Header = tableView.tableColumns[2]
column2Header.headerCell.drawsBackground = true
column2Header.headerCell.backgroundColor = NSColor.systemMint
column2Header.headerCell.alignment = .center
column2Header.headerCell.attributedStringValue = column2AttributedString
let scrollView = NSScrollView()
scrollView.documentView = tableView
return scrollView
}
func updateNSView(_ nsView: NSScrollView, context: Context) {
let tableView = (nsView.documentView as! NSTableView)
// work on this section
}
}
I have a program that utilizes a Navigation Stack. I want each view to be centered on the screen and be a specific size. I accomplished this with some position logic which sets the views frame size and origin. There is unique position logic for each of the 3 view sizes, the Content View and 2 navigation destination views. When the Content View is first displayed the associated position logic code runs and the view is the correct size and centered. The same is true every time one of the 2 navigation destination views is displayed. Unfortunately, when I return to the Content View from a navigation destination view the position logic does not run again and the Content View is now the same size and position as the previous navigation destination view. How do I resolve this problem? Below is the position logic code associated with the Content View and how it is called.
@main
struct TableViewTestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.hostingWindowPositionHome(
screen: .main
)
}
}
}
extension View {
func hostingWindowPositionHome(
screen: NSScreen? = nil
) -> some View {
modifier(
WindowPositionModifierHome(
screen: screen
)
)
}
}
private struct WindowPositionModifierHome: ViewModifier {
let screen: NSScreen?
func body(content: Content) -> some View {
content.background(
HostingWindowFinderHome {
$0?.setPositionHome(in: screen)
}
)
}
}
private struct HostingWindowFinderHome: NSViewRepresentable {
var callback: (NSWindow?) -> ()
func makeNSView(context: Self.Context) -> NSView {
let view = NSView()
DispatchQueue.main.async { self.callback(view.window) }
return view
}
func updateNSView(_ nsView: NSView, context: Context) {
DispatchQueue.main.async { self.callback(nsView.window) }
}
}
extension NSWindow {
func setPositionHome(in screen: NSScreen?) {
let nsRectangle: NSRect = NSRect(x: 1055.0, y: 370.0, width: 450, height: 700)
setFrame(nsRectangle, display: true)
}
}
Hello,
I am trying to learn Swift. I have the code below.
With a button click I can move to a new view. What I would like to do is once the new view is displayed have the navigation link destination changed to "CurrentView" and the button text to "Previous View".
Any insight on how to resolve this issue would be appreciated.
Regards,
Chris
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination: NextContentView()){
Text("Next View")
}
}
}
}