From 8e1ac67d8040dedfc0eb03a76397eb6d61781bf1 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 27 Sep 2022 16:21:54 +0800 Subject: [PATCH] Use a UIViewRepresentable to set slider images instead of introspection The slider was losing the custom images when being opened/closed sometimes because introspection doesn't get called. --- .../App/Views/AudioPlayer/MiniPlayer.swift | 38 +++------- .../App/Views/AudioPlayer/ScrubberView.swift | 73 +++++++++++++++++++ 2 files changed, 84 insertions(+), 27 deletions(-) create mode 100644 apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ScrubberView.swift diff --git a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift index 6e77cd92e..b2938f125 100644 --- a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift @@ -192,11 +192,11 @@ public struct MiniPlayer: View { if !expanded { Text(itemAudioProperties.title) - .font(expanded ? .appTitle : .appCallout) + .font(.appCallout) .lineSpacing(1.25) .foregroundColor(.appGrayTextContrast) .fixedSize(horizontal: false, vertical: false) - .frame(maxWidth: .infinity, alignment: expanded ? .center : .leading) + .frame(maxWidth: .infinity, alignment: .leading) .matchedGeometryEffect(id: "ArticleTitle", in: animation) playPauseButtonItem @@ -221,31 +221,15 @@ public struct MiniPlayer: View { .foregroundColor(.appGrayText) } - Slider(value: $audioController.timeElapsed, - in: 0 ... self.audioController.duration, - onEditingChanged: { scrubStarted in - if scrubStarted { - self.audioController.scrubState = .scrubStarted - } else { - self.audioController.scrubState = .scrubEnded(self.audioController.timeElapsed) - } - }) - .accentColor(.appCtaYellow) - .introspectSlider { slider in - // Make the thumb a little smaller than the default and give it the CTA color - // for some reason this doesn't work on my iPad though. - let tintColor = UIColor(Color.appCtaYellow) - - let image = UIImage(systemName: "circle.fill", - withConfiguration: UIImage.SymbolConfiguration(scale: .small))? - .withTintColor(tintColor) - .withRenderingMode(.alwaysOriginal) - - slider.setThumbImage(image, for: .selected) - slider.setThumbImage(image, for: .normal) - - slider.minimumTrackTintColor = tintColor - } + ScrubberView(value: $audioController.timeElapsed, + minValue: 0, maxValue: self.audioController.duration, + onEditingChanged: { scrubStarted in + if scrubStarted { + self.audioController.scrubState = .scrubStarted + } else { + self.audioController.scrubState = .scrubEnded(self.audioController.timeElapsed) + } + }) HStack { Text(audioController.timeElapsedString ?? "0:00") diff --git a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ScrubberView.swift b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ScrubberView.swift new file mode 100644 index 000000000..edff16b09 --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ScrubberView.swift @@ -0,0 +1,73 @@ +// +// ScrubberView.swift +// +// +// Created by Jackson Harper on 9/27/22. +// + +import Foundation +import SwiftUI + +struct ScrubberView: UIViewRepresentable { + typealias UIViewType = UISlider + + @Binding var value: Double + var minValue: Double + var maxValue: Double + var onEditingChanged: (Bool) -> Void + + init(value: Binding, minValue: Double, maxValue: Double, onEditingChanged: @escaping (Bool) -> Void) { + self._value = value + self.minValue = minValue + self.maxValue = maxValue + self.onEditingChanged = onEditingChanged + } + + func makeUIView(context: Context) -> UISlider { + let slider = UISlider(frame: .zero) + slider.maximumValue = Float(minValue) + slider.maximumValue = Float(maxValue) + + let tintColor = UIColor(Color.appCtaYellow) + + let image = UIImage(systemName: "circle.fill", + withConfiguration: UIImage.SymbolConfiguration(scale: .small))? + .withTintColor(tintColor) + .withRenderingMode(.alwaysOriginal) + + slider.setThumbImage(image, for: .selected) + slider.setThumbImage(image, for: .normal) + + slider.minimumTrackTintColor = tintColor + slider.addTarget(context.coordinator, + action: #selector(Coordinator.valueChanged(_:)), + for: .valueChanged) + + return slider + } + + func updateUIView(_ uiView: UISlider, context _: Context) { + uiView.value = Float(value) + } + + func makeCoordinator() -> Coordinator { + let coordinator = Coordinator(value: $value, onEditingChanged: onEditingChanged) + return coordinator + } + + class Coordinator: NSObject { + var value: Binding + var onEditingChanged: (Bool) -> Void + + init(value: Binding, onEditingChanged: @escaping (Bool) -> Void) { + self.value = value + self.onEditingChanged = onEditingChanged + super.init() + } + + @objc func valueChanged(_ sender: UISlider) { + value.wrappedValue = Double(sender.value) + onEditingChanged(sender.isTracking) + } + } +}