@@ -93,6 +93,12 @@ final class RNCSliderModel: ObservableObject {
9393 return thumbValues . contains { abs ( $0 - stepValue ) <= step * Self . stepTolerance }
9494 }
9595
96+ /// How faint a slider switched off is drawn, at the faintness a control that cannot be
97+ /// reached is drawn at. Shared by the slider `RNCSliderContent` hosts and by the views
98+ /// JS renders over it, so that the two go faint together - see
99+ /// `RNCSliderView.applyDisabledLook`.
100+ static let disabledAlpha : CGFloat = 0.3
101+
96102 /// Leeway, as a fraction of a step, for a value to count as landing on one.
97103 static let stepTolerance = 1e-9
98104
@@ -303,11 +309,23 @@ struct RNCSliderGeometry {
303309 rightOffset = CGFloat ( Self . fraction ( of : model . valueRight , in : range ) ) * travel
304310 }
305311
312+ /// How far along the slider the centre of the thumb whose near edge sits at the given
313+ /// offset stands.
314+ ///
315+ /// The offsets place a thumb by its near edge, while a thumb covers the track around
316+ /// its *centre* - so that is where the parts of the track have to be cut into, and not
317+ /// at the near edge. Cutting at the near edge leaves the strip between the two on show,
318+ /// which is only ever covered again by a thumb drawn of its own: a thumb JS replaces
319+ /// draws no track, and leaves that strip as a gap of unfilled track beside it.
320+ func thumbCenterOffset ( at offset : CGFloat ) -> CGFloat {
321+ offset + thumbLength / 2
322+ }
323+
306324 // Centre of the thumb whose near edge sits the given distance along the slider, in
307325 // the coordinates of the view. The thumbs lie along the middle of the view, and a
308326 // vertical slider grows upwards.
309327 func thumbCenter ( atOffset offset : CGFloat ) -> CGPoint {
310- let along = offset + thumbLength / 2
328+ let along = thumbCenterOffset ( at : offset )
311329
312330 return vertical
313331 ? CGPoint ( x : size . width / 2 , y : size . height - along )
@@ -322,7 +340,8 @@ struct RNCSliderGeometry {
322340 /// being the middle of their left edge - they are not turned round with the slider,
323341 /// which keeps whatever they read the right way up.
324342 func stepMarkerAnchor ( at value : Double ) -> CGPoint {
325- let along = CGFloat ( Self . fraction ( of : value , in : range ) ) * travel + thumbLength / 2
343+ let nearEdge = CGFloat ( Self . fraction ( of : value , in : range ) ) * travel
344+ let along = thumbCenterOffset ( at : nearEdge )
326345 let across = breadth / 2 + thumbThickness / 2 + Self . stepMarkerGap
327346
328347 return vertical
@@ -396,7 +415,8 @@ struct RNCSliderGeometry {
396415 let offset = offset ( of : point )
397416
398417 return thumbs . min {
399- abs ( $0 . offset + thumbLength / 2 - offset ) < abs ( $1 . offset + thumbLength / 2 - offset )
418+ abs ( thumbCenterOffset ( at : $0 . offset ) - offset )
419+ < abs ( thumbCenterOffset ( at : $1 . offset ) - offset )
400420 } ? . thumb
401421 }
402422
@@ -461,14 +481,11 @@ struct RNCSliderContent: View {
461481 // and their places. The values stand wherever the props put them, so nothing here
462482 // moves.
463483 //
464- // Only the slider this view draws is washed out. A track, a thumb or a step marker
465- // JS rendered is its own view, outside this one, and stays as bright as JS drew it.
466- . opacity ( model . disabled ? Self . disabledOpacity : 1 )
484+ // What is washed out here is only the slider this view draws: a track, a thumb or a
485+ // step marker JS rendered is mounted over it, as a view of its own, and is dimmed
486+ // to this same faintness there - see `RNCSliderView.applyDisabledLook`.
487+ . opacity ( model . disabled ? Double ( RNCSliderModel . disabledAlpha ) : 1 )
467488 }
468-
469- /// How faint a slider switched off is drawn, at the faintness a control that cannot be
470- /// reached is drawn at.
471- private static let disabledOpacity : Double = 0.3
472489}
473490
474491struct RNCSingleSliderContent : View {
@@ -524,7 +541,14 @@ struct RNCRangedSliderContent: View {
524541 let lowerColor = tint ( model . minimumTrackColor , or : model . ranged ? Self . trackColor : . accentColor )
525542 let middleColor = tint ( model . middleRangeTrackColor , or : . accentColor )
526543 let upperColor = tint ( model . maximumTrackColor , or : Self . trackColor )
527- let upperStart = model . ranged ? geometry . rightOffset : geometry . leftOffset
544+ // The parts of the track are cut where the thumbs cover it, which is at their
545+ // centre and not at the near edge the offsets give: cutting any earlier leaves a
546+ // strip of the track left untinted beside a thumb - unmistakable once JS replaces
547+ // the thumb with a view that draws none of its own.
548+ let lowerEnd = geometry . thumbCenterOffset ( at : geometry . leftOffset )
549+ let upperStart = geometry . thumbCenterOffset (
550+ at : model . ranged ? geometry . rightOffset : geometry . leftOffset
551+ )
528552
529553 // Everything is placed by how far along the slider it sits, so the stack is
530554 // anchored at the end the minimum value is at: the leading edge across the view,
@@ -533,13 +557,15 @@ struct RNCRangedSliderContent: View {
533557 if ! model . customTrack {
534558 track ( Self . trackColor , length : nil )
535559
536- track ( lowerColor , length : geometry . leftOffset )
560+ track ( lowerColor , length : lowerEnd )
537561
538- // Drawn between the two thumb centres, which is why it is inset by half a
539- // thumb. `max` keeps it from inverting on values crossed by JS.
562+ // Drawn between the two thumb centres, which is where the parts of the track
563+ // are cut - so its length is the gap between the near edges the offsets give,
564+ // and it is inset by half a thumb from the nearer of them. `max` keeps it from
565+ // inverting on values crossed by JS.
540566 if model . ranged {
541567 track ( middleColor , length : max ( geometry . rightOffset - geometry . leftOffset , 0 ) )
542- . offset ( offsetAlong ( min ( geometry . leftOffset , geometry . rightOffset ) + geometry . thumbLength / 2 ) )
568+ . offset ( offsetAlong ( geometry . thumbCenterOffset ( at : min ( geometry . leftOffset , geometry . rightOffset ) ) ) )
543569 }
544570
545571 track ( upperColor , length : geometry . length - upperStart ) . offset ( offsetAlong ( upperStart ) )
@@ -1105,6 +1131,9 @@ public final class RNCSliderView: UIView {
11051131 /// report: the drags of the hosted thumbs, the tap and the hold on the track and
11061132 /// VoiceOver's adjustments all hang off it, and what it is drawn as comes from the
11071133 /// same flag.
1134+ ///
1135+ /// The views JS renders in place of the track, the thumbs and the step markers are
1136+ /// switched off along with the rest of the slider - see `applyDisabledLook`.
11081137 @ objc public var disabled : Bool {
11091138 get { model . disabled }
11101139 set {
@@ -1118,9 +1147,30 @@ public final class RNCSliderView: UIView {
11181147 }
11191148
11201149 isUserInteractionEnabled = ! newValue
1150+ applyDisabledLook ( )
11211151 }
11221152 }
11231153
1154+ /// Draws the views JS rendered in place of the track, of a thumb and of the step
1155+ /// markers at the faintness of a control nobody can reach.
1156+ ///
1157+ /// The hosted slider is the one thing here that switches itself off to be drawn: it
1158+ /// washes out its own track and thumbs. A view JS renders is mounted over it as a view
1159+ /// of its own, outside that, and nothing reaches it as a state to draw itself by - so
1160+ /// the containers holding them are dimmed to the same faintness, and the whole of a
1161+ /// switched-off slider goes faint together, over whatever JS drew into them.
1162+ ///
1163+ /// Run over again whenever the children are handed out afresh: a container made or
1164+ /// remounted once the slider is already switched off would otherwise stay as bright as
1165+ /// the slider was before it was.
1166+ private func applyDisabledLook ( ) {
1167+ let alpha : CGFloat = model . disabled ? RNCSliderModel . disabledAlpha : 1
1168+
1169+ trackContainer . alpha = alpha
1170+ thumbContainers . forEach { $0 . alpha = alpha }
1171+ stepMarkerContainers . forEach { $0 . alpha = alpha }
1172+ }
1173+
11241174 /// Tints of the parts of the track - see `RNCSliderModel.minimumTrackColor`.
11251175 @ objc public var minimumTrackColor : UIColor ? {
11261176 get { model . minimumTrackColor }
@@ -1327,6 +1377,7 @@ public final class RNCSliderView: UIView {
13271377 }
13281378
13291379 model . customThumbCount = min ( thumbViews . count , thumbContainers . count )
1380+ applyDisabledLook ( )
13301381 setNeedsLayout ( )
13311382 }
13321383
0 commit comments