From 85355336c9766c8470604463a30096473de44caf Mon Sep 17 00:00:00 2001 From: Seiike <122684677+Seeike@users.noreply.github.com> Date: Thu, 3 Apr 2025 23:06:01 +0200 Subject: [PATCH] a beautiful brightness slider --- ...r.swift => VerticalBrightnessSlider.swift} | 43 ++++++++-------- .../CustomPlayer/CustomPlayer.swift | 49 +++++++++++++------ Sulfur.xcodeproj/project.pbxproj | 8 +-- 3 files changed, 58 insertions(+), 42 deletions(-) rename Sora/Utils/MediaPlayer/CustomPlayer/Components/{VerticalVolumeSlider.swift => VerticalBrightnessSlider.swift} (80%) diff --git a/Sora/Utils/MediaPlayer/CustomPlayer/Components/VerticalVolumeSlider.swift b/Sora/Utils/MediaPlayer/CustomPlayer/Components/VerticalBrightnessSlider.swift similarity index 80% rename from Sora/Utils/MediaPlayer/CustomPlayer/Components/VerticalVolumeSlider.swift rename to Sora/Utils/MediaPlayer/CustomPlayer/Components/VerticalBrightnessSlider.swift index 3807967..3e33aea 100644 --- a/Sora/Utils/MediaPlayer/CustomPlayer/Components/VerticalVolumeSlider.swift +++ b/Sora/Utils/MediaPlayer/CustomPlayer/Components/VerticalBrightnessSlider.swift @@ -1,6 +1,6 @@ // -// VerticalVolumeSlider.swift -// Custom Seekbar +// VerticalBrightnessSlider.swift +// Custom Brighness bar // // Created by Pratik on 08/01/23. // Modified to update screen brightness when used as a brightness slider. @@ -8,7 +8,7 @@ import SwiftUI -struct VerticalVolumeSlider: View { +struct VerticalBrightnessSlider: View { @Binding var value: T let inRange: ClosedRange let activeFillColor: Color @@ -41,16 +41,16 @@ struct VerticalVolumeSlider: View { }) Image(systemName: getIconName) - .font(.system(size: 16, weight: .medium, design: .rounded)) - .foregroundColor(fillColor) - .animation(.spring(), value: localRealProgress) + .font(.system(size: isActive ? 16 : 12, weight: .medium, design: .rounded)) + .foregroundColor(isActive ? fillColor : Color.white) // bright white when not active + .animation(.spring(), value: isActive) .frame(maxHeight: .infinity, alignment: .bottom) .padding(.bottom) .overlay { Image(systemName: getIconName) - .font(.system(size: 16, weight: .medium, design: .rounded)) - .foregroundColor(.gray) - .animation(.spring(), value: localRealProgress) + .font(.system(size: isActive ? 16 : 12, weight: .medium, design: .rounded)) + .foregroundColor(isActive ? Color.gray : Color.white.opacity(0.8)) + .animation(.spring(), value: isActive) .frame(maxHeight: .infinity, alignment: .bottom) .padding(.bottom) .mask { @@ -62,8 +62,8 @@ struct VerticalVolumeSlider: View { } } } - .frame(maxWidth: isActive ? .infinity : 0) - .opacity(isActive ? 1 : 0) + //.frame(maxWidth: isActive ? .infinity : 0) + // .opacity(isActive ? 1 : 0) } .clipped() } @@ -98,7 +98,7 @@ struct VerticalVolumeSlider: View { } } } - .frame(width: isActive ? width * 4 : width, alignment: .center) + .frame(width: isActive ? width * 2.2 : width, alignment: .center) .offset(x: isActive ? -10 : 0) .onChange(of: value) { newValue in UIScreen.main.brightness = CGFloat(newValue) @@ -106,18 +106,17 @@ struct VerticalVolumeSlider: View { } private var getIconName: String { - var name = "speaker.wave." - switch CGFloat((localRealProgress + localTempProgress)) { - case ..<0.01: - name = "speaker.slash.fill" - case ..<0.3: - name += "1.fill" - case ..<0.6: - name += "2.fill" + let brightnessLevel = CGFloat(localRealProgress + localTempProgress) + switch brightnessLevel { + case ..<0.2: + return "moon.fill" + case 0.2..<0.38: + return "sun.min" + case 0.38..<0.7: + return "sun.max" default: - name += "3.fill" + return "sun.max.fill" } - return name } private var animation: Animation { diff --git a/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift b/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift index c61a75f..13e6ecb 100644 --- a/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift +++ b/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift @@ -42,7 +42,7 @@ class CustomMediaPlayerViewController: UIViewController { var isVideoLoaded = false var brightnessValue: Double = Double(UIScreen.main.brightness) - var brightnessSliderHostingController: UIHostingController>? + var brightnessSliderHostingController: UIHostingController>? var showWatchNextButton = true var watchNextButtonTimer: Timer? @@ -411,40 +411,57 @@ class CustomMediaPlayerViewController: UIViewController { func brightnessControl() { - let brightnessSlider = VerticalVolumeSlider( + let brightnessSlider = VerticalBrightnessSlider( value: Binding( get: { self.brightnessValue }, set: { newValue in self.brightnessValue = newValue - // No need to update UIScreen.main.brightness here since it's handled inside VerticalVolumeSlider. + // UIScreen.main.brightness is updated inside VerticalVolumeSlider. } ), inRange: 0...1, activeFillColor: .white, // Preserves original active color fillColor: .white.opacity(0.5), // Preserves original fill color emptyColor: .white.opacity(0.3), // Preserves original empty color - width: 15, // Keeps the original width and corner style + width: 16, // Keeps the original width and corner style onEditingChanged: { editing in // Optionally handle editing events here. - } + } ) - // Embed the slider in a UIHostingController. - brightnessSliderHostingController = UIHostingController(rootView: brightnessSlider) - guard let brightnessSliderView = brightnessSliderHostingController?.view else { return } + // Create a container view for the brightness slider. + let brightnessContainer = UIView() + brightnessContainer.translatesAutoresizingMaskIntoConstraints = false + // Optionally, set backgroundColor to clear. + brightnessContainer.backgroundColor = .clear - brightnessSliderView.backgroundColor = .clear - brightnessSliderView.translatesAutoresizingMaskIntoConstraints = false - controlsContainerView.addSubview(brightnessSliderView) + // Add the container to the controls container. + controlsContainerView.addSubview(brightnessContainer) - // Position the slider on the left side of the player while preserving its original appearance. + // Constrain the container so that its bounds follow the slider's visual position. NSLayoutConstraint.activate([ - brightnessSliderView.leadingAnchor.constraint(equalTo: controlsContainerView.leadingAnchor, constant: -10), - brightnessSliderView.centerYAnchor.constraint(equalTo: controlsContainerView.centerYAnchor), - brightnessSliderView.widthAnchor.constraint(equalToConstant: 15), // Match the slider's defined width - brightnessSliderView.heightAnchor.constraint(equalToConstant: 170) // Adjust height to properly display the slider + brightnessContainer.leadingAnchor.constraint(equalTo: controlsContainerView.leadingAnchor, constant: 1.8), + brightnessContainer.centerYAnchor.constraint(equalTo: controlsContainerView.centerYAnchor, constant: -10), + brightnessContainer.widthAnchor.constraint(equalToConstant: 16), + brightnessContainer.heightAnchor.constraint(equalToConstant: 170) ]) + // Embed the brightness slider in a UIHostingController. + brightnessSliderHostingController = UIHostingController(rootView: brightnessSlider) + guard let brightnessSliderView = brightnessSliderHostingController?.view else { return } + brightnessSliderView.backgroundColor = .clear + brightnessSliderView.translatesAutoresizingMaskIntoConstraints = false + + // Add the slider view to the container. + brightnessContainer.addSubview(brightnessSliderView) + + // Constrain the slider view to the container’s bounds. + NSLayoutConstraint.activate([ + brightnessSliderView.topAnchor.constraint(equalTo: brightnessContainer.topAnchor), + brightnessSliderView.bottomAnchor.constraint(equalTo: brightnessContainer.bottomAnchor), + brightnessSliderView.leadingAnchor.constraint(equalTo: brightnessContainer.leadingAnchor), + brightnessSliderView.trailingAnchor.constraint(equalTo: brightnessContainer.trailingAnchor) + ]) } func addInvisibleControlOverlays() { diff --git a/Sulfur.xcodeproj/project.pbxproj b/Sulfur.xcodeproj/project.pbxproj index 6aae054..a0d0a49 100644 --- a/Sulfur.xcodeproj/project.pbxproj +++ b/Sulfur.xcodeproj/project.pbxproj @@ -62,7 +62,7 @@ 13EA2BD52D32D97400C1EBD7 /* CustomPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD12D32D97400C1EBD7 /* CustomPlayer.swift */; }; 13EA2BD62D32D97400C1EBD7 /* Double+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD32D32D97400C1EBD7 /* Double+Extension.swift */; }; 13EA2BD92D32D98400C1EBD7 /* NormalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD82D32D98400C1EBD7 /* NormalPlayer.swift */; }; - 1E3F5EC82D9F16B7003F310F /* VerticalVolumeSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E3F5EC72D9F16B7003F310F /* VerticalVolumeSlider.swift */; }; + 1E3F5EC82D9F16B7003F310F /* VerticalBrightnessSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E3F5EC72D9F16B7003F310F /* VerticalBrightnessSlider.swift */; }; 1E9FF1D32D403E49008AC100 /* SettingsViewLoggerFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9FF1D22D403E42008AC100 /* SettingsViewLoggerFilter.swift */; }; 1EAC7A322D888BC50083984D /* MusicProgressSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EAC7A312D888BC50083984D /* MusicProgressSlider.swift */; }; 73D164D52D8B5B470011A360 /* JavaScriptCore+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73D164D42D8B5B340011A360 /* JavaScriptCore+Extensions.swift */; }; @@ -124,7 +124,7 @@ 13EA2BD12D32D97400C1EBD7 /* CustomPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomPlayer.swift; sourceTree = ""; }; 13EA2BD32D32D97400C1EBD7 /* Double+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Double+Extension.swift"; sourceTree = ""; }; 13EA2BD82D32D98400C1EBD7 /* NormalPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NormalPlayer.swift; sourceTree = ""; }; - 1E3F5EC72D9F16B7003F310F /* VerticalVolumeSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerticalVolumeSlider.swift; sourceTree = ""; }; + 1E3F5EC72D9F16B7003F310F /* VerticalBrightnessSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerticalBrightnessSlider.swift; sourceTree = ""; }; 1E9FF1D22D403E42008AC100 /* SettingsViewLoggerFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewLoggerFilter.swift; sourceTree = ""; }; 1EAC7A312D888BC50083984D /* MusicProgressSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MusicProgressSlider.swift; sourceTree = ""; }; 73D164D42D8B5B340011A360 /* JavaScriptCore+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JavaScriptCore+Extensions.swift"; sourceTree = ""; }; @@ -442,7 +442,7 @@ 13EA2BD22D32D97400C1EBD7 /* Components */ = { isa = PBXGroup; children = ( - 1E3F5EC72D9F16B7003F310F /* VerticalVolumeSlider.swift */, + 1E3F5EC72D9F16B7003F310F /* VerticalBrightnessSlider.swift */, 13EA2BD32D32D97400C1EBD7 /* Double+Extension.swift */, 1EAC7A312D888BC50083984D /* MusicProgressSlider.swift */, ); @@ -574,7 +574,7 @@ 133D7C6E2D2BE2500075467E /* SoraApp.swift in Sources */, 138AA1B92D2D66FD0021F9DF /* CircularProgressBar.swift in Sources */, 1334FF4D2D786C93007E289F /* TMDB-Seasonal.swift in Sources */, - 1E3F5EC82D9F16B7003F310F /* VerticalVolumeSlider.swift in Sources */, + 1E3F5EC82D9F16B7003F310F /* VerticalBrightnessSlider.swift in Sources */, 13DB468D2D90093A008CBC03 /* Anilist-Login.swift in Sources */, 73D164D52D8B5B470011A360 /* JavaScriptCore+Extensions.swift in Sources */, 132AF1232D9995C300A0140B /* JSController-Details.swift in Sources */,