@@ -13,7 +13,7 @@ struct HeartChartDataPoint: Identifiable, Equatable {
1313 let date : Date
1414 let min : Double
1515 let max : Double
16- let average : Double
16+ let median : Double
1717 let values : [ Double ]
1818}
1919
@@ -67,7 +67,7 @@ struct HeartChartView: View {
6767 date: Calendar . current. date ( byAdding: . minute, value: 30 , to: bucket) ?? bucket,
6868 min: values. min ( ) ?? 0 ,
6969 max: values. max ( ) ?? 0 ,
70- average : {
70+ median : {
7171 let sorted = values. sorted ( )
7272 let mid = sorted. count / 2
7373 return sorted. count % 2 == 0
@@ -119,7 +119,7 @@ struct HeartChartView: View {
119119
120120 PointMark (
121121 x: . value( " Time " , point. date) ,
122- y: . value( " BPM " , point. average )
122+ y: . value( " BPM " , point. median )
123123 )
124124 . foregroundStyle ( heartColor)
125125 . symbolSize ( CGSize ( width: 7 , height: 7 ) )
@@ -136,26 +136,25 @@ struct HeartChartView: View {
136136
137137 // MARK: iOS 16- fixed chart
138138 func chartPage( for offset: Int ) -> some View {
139- let start = Calendar . current. startOfDay ( for: Calendar . current. date ( byAdding: . day, value: offset, to: Date ( ) ) !)
140- let end = Date ( timeInterval: 86400 , since: start)
141- let pagePoints = points. filter { $0. date >= start && $0. date <= end }
142- let pageMin = Int ( pagePoints. map ( { $0. min } ) . min ( ) ?? 0 )
143- let pageMax = Int ( pagePoints. map ( { $0. max } ) . max ( ) ?? 0 )
139+ let xMin = Calendar . current. startOfDay ( for: Calendar . current. date ( byAdding: . day, value: offset, to: Date ( ) ) !)
140+ let xMax = Date ( timeInterval: 86400 , since: xMin)
141+ let yMin = displayedMin - 20
142+ let yMax = displayedMax + 20
144143
145144 return Chart {
146145 if let selectedViewHour {
147146 RuleMark ( x: . value( " Selected Hour " , selectedViewHour. date, unit: . hour) )
148147 . foregroundStyle ( Color . gray)
149148 }
150149
151- ForEach ( pagePoints ) { point in
150+ ForEach ( windowPoints ) { point in
152151 chartContent ( for: point, selected: selectedViewHour)
153152 }
154153 }
155154 . frame ( height: 280 )
156155 . padding ( . horizontal, 8 )
157- . chartYScale ( domain: ( pageMin - 20 ) ... ( pageMax + 20 ) )
158- . chartXScale ( domain: start ... end )
156+ . chartYScale ( domain: ( yMin ... yMax ) )
157+ . chartXScale ( domain: xMin ... xMax )
159158 . chartXAxis {
160159 AxisMarks ( values: . stride( by: . hour, count: 6 ) ) { value in
161160 AxisGridLine ( stroke: StrokeStyle ( lineWidth: 0.5 , dash: [ 4 ] ) )
@@ -174,13 +173,9 @@ struct HeartChartView: View {
174173 . contentShape ( Rectangle ( ) )
175174 . gesture ( DragGesture ( minimumDistance: 0 )
176175 . onChanged { value in
177- let leadingPadding : CGFloat = 8
178- let yAxisWidth : CGFloat = 40
179- let adjustedWidth = geo. size. width - yAxisWidth - leadingPadding
180- let clampedX = min ( max ( value. location. x - leadingPadding, 0 ) , adjustedWidth)
181- let fraction = clampedX / adjustedWidth
182- let totalSeconds : TimeInterval = 86400
183- rawSelectedHour = start. addingTimeInterval ( fraction * totalSeconds)
176+ let adjustedWidth = geo. size. width - 48 // 40 y-axis + 8 horizontal padding
177+ let normalizedXPosition = min ( max ( value. location. x - 8 , 0 ) , adjustedWidth) / adjustedWidth
178+ rawSelectedHour = xMin. addingTimeInterval ( normalizedXPosition * 86400 )
184179 }
185180 . onEnded { _ in
186181 rawSelectedHour = nil
@@ -295,7 +290,7 @@ struct HeartChartView: View {
295290 + Text( " BPM " )
296291
297292 let style = Date . FormatStyle ( ) . hour ( . defaultDigits( amPM: . abbreviated) )
298- Text ( " \( rangeFirstHour. formatted ( . dateTime. month ( . abbreviated) . day ( ) ) ) , \( rangeFirstHour. formatted ( style) ) – \( rangeLastHour. formatted ( style) ) · \( selectedViewHour. values. count) \( selectedViewHour. values. count == 1 ? " reading " : " readings " ) · \( Int ( selectedViewHour. average ) ) BPM avg " )
293+ Text ( " \( rangeFirstHour. formatted ( . dateTime. month ( . abbreviated) . day ( ) ) ) , \( rangeFirstHour. formatted ( style) ) – \( rangeLastHour. formatted ( style) ) · \( selectedViewHour. values. count) \( selectedViewHour. values. count == 1 ? " reading " : " readings " ) \( selectedViewHour . values . count > 1 ? " · \( Int ( selectedViewHour. median ) ) BPM avg " : " " ) " )
299294 . foregroundColor ( . secondary)
300295 . font ( . subheadline)
301296 }
0 commit comments