diff --git a/DynamicIsland.xcodeproj/project.pbxproj b/DynamicIsland.xcodeproj/project.pbxproj index cdd49d3..ea84660 100644 --- a/DynamicIsland.xcodeproj/project.pbxproj +++ b/DynamicIsland.xcodeproj/project.pbxproj @@ -383,6 +383,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + AUTOMATION_APPLE_EVENTS = NO; CODE_SIGN_ENTITLEMENTS = DynamicIsland/DynamicIsland.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; @@ -394,6 +395,12 @@ ENABLE_APP_SANDBOX = NO; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; + ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO; + ENABLE_RESOURCE_ACCESS_CALENDARS = YES; + ENABLE_RESOURCE_ACCESS_CAMERA = YES; + ENABLE_RESOURCE_ACCESS_CONTACTS = NO; + ENABLE_RESOURCE_ACCESS_LOCATION = YES; + ENABLE_RESOURCE_ACCESS_PHOTO_LIBRARY = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -423,6 +430,12 @@ PRODUCT_BUNDLE_IDENTIFIER = com.Ebullioscopic.Atoll; PRODUCT_NAME = Atoll; PROVISIONING_PROFILE_SPECIFIER = ""; + RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO; + RUNTIME_EXCEPTION_ALLOW_JIT = NO; + RUNTIME_EXCEPTION_ALLOW_UNSIGNED_EXECUTABLE_MEMORY = NO; + RUNTIME_EXCEPTION_DEBUGGING_TOOL = NO; + RUNTIME_EXCEPTION_DISABLE_EXECUTABLE_PAGE_PROTECTION = NO; + RUNTIME_EXCEPTION_DISABLE_LIBRARY_VALIDATION = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; SYSTEM_FRAMEWORK_SEARCH_PATHS = ( @@ -437,6 +450,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + AUTOMATION_APPLE_EVENTS = NO; CODE_SIGN_ENTITLEMENTS = DynamicIsland/DynamicIsland.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; @@ -448,6 +462,12 @@ ENABLE_APP_SANDBOX = NO; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; + ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO; + ENABLE_RESOURCE_ACCESS_CALENDARS = YES; + ENABLE_RESOURCE_ACCESS_CAMERA = YES; + ENABLE_RESOURCE_ACCESS_CONTACTS = NO; + ENABLE_RESOURCE_ACCESS_LOCATION = YES; + ENABLE_RESOURCE_ACCESS_PHOTO_LIBRARY = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -477,6 +497,12 @@ PRODUCT_BUNDLE_IDENTIFIER = com.Ebullioscopic.Atoll; PRODUCT_NAME = Atoll; PROVISIONING_PROFILE_SPECIFIER = ""; + RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO; + RUNTIME_EXCEPTION_ALLOW_JIT = NO; + RUNTIME_EXCEPTION_ALLOW_UNSIGNED_EXECUTABLE_MEMORY = NO; + RUNTIME_EXCEPTION_DEBUGGING_TOOL = NO; + RUNTIME_EXCEPTION_DISABLE_EXECUTABLE_PAGE_PROTECTION = NO; + RUNTIME_EXCEPTION_DISABLE_LIBRARY_VALIDATION = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; SYSTEM_FRAMEWORK_SEARCH_PATHS = ( diff --git a/DynamicIsland/components/MinimalisticMusicView.swift b/DynamicIsland/components/MinimalisticMusicView.swift index 38655ec..af5cd40 100644 --- a/DynamicIsland/components/MinimalisticMusicView.swift +++ b/DynamicIsland/components/MinimalisticMusicView.swift @@ -52,15 +52,7 @@ struct MinimalisticMusicView: View { // Lyrics under the author name (same font size as author) if enableLyrics { - Text(musicManager.currentLyrics) - .font(.system(size: 10, weight: .regular)) - .foregroundColor(.white.opacity(0.8)) - .lineLimit(2) - .multilineTextAlignment(.center) - .frame(maxWidth: .infinity, alignment: .center) - .padding(.horizontal, 4) - .transition(.opacity) - .animation(.easeInOut(duration: 0.3), value: musicManager.currentLyrics) + lyricsLineView } } .frame(width: geo.size.width, height: geo.size.height, alignment: .center) @@ -75,7 +67,7 @@ struct MinimalisticMusicView: View { // (lyrics are displayed inline under the artist name) } // reserve extra height when lyrics are enabled - .frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0) + (enableLyrics ? 22 : 0), alignment: .center) + .frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0), alignment: .center) .onHover { hovering in isHovering = hovering } @@ -118,3 +110,33 @@ struct MinimalisticMusicView: View { height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), alignment: .center) } } + +private extension MinimalisticMusicView { + var lyricsLineView: some View { + let line = musicManager.currentLyrics.trimmingCharacters(in: .whitespacesAndNewlines) + + return HStack(spacing: 6) { + if !line.isEmpty { + Image(systemName: "music.note") + .font(.system(size: 11, weight: .semibold)) + .foregroundColor(.white.opacity(0.7)) + .symbolRenderingMode(.monochrome) + + Text(line) + .font(.system(size: 11, weight: .semibold)) + .foregroundColor(.white.opacity(0.88)) + .lineLimit(2) + .multilineTextAlignment(.center) + .frame(maxWidth: .infinity, alignment: .center) + .padding(.horizontal, 6) + .id(line) + .transition(.asymmetric( + insertion: .move(edge: .bottom).combined(with: .opacity), + removal: .move(edge: .top).combined(with: .opacity) + )) + } + } + .frame(maxWidth: .infinity, alignment: .center) + .animation(.smooth(duration: 0.32), value: line) + } +} diff --git a/DynamicIsland/components/Notch/MinimalisticMusicPlayerView.swift b/DynamicIsland/components/Notch/MinimalisticMusicPlayerView.swift index 9b1dff8..b1dcbb0 100644 --- a/DynamicIsland/components/Notch/MinimalisticMusicPlayerView.swift +++ b/DynamicIsland/components/Notch/MinimalisticMusicPlayerView.swift @@ -50,18 +50,6 @@ struct MinimalisticMusicPlayerView: View { .foregroundColor(Defaults[.playerColorTinting] ? Color(nsColor: musicManager.avgColor).ensureMinimumBrightness(factor: 0.6) : .gray) .lineLimit(1) - // Lyrics shown under the author name, same font size as author - if enableLyrics { - Text(musicManager.currentLyrics) - .font(.system(size: 10, weight: .regular)) - .foregroundColor(.white.opacity(0.8)) - .lineLimit(2) - .multilineTextAlignment(.leading) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.top, 4) - .transition(.opacity) - .animation(.easeInOut(duration: 0.3), value: musicManager.currentLyrics) - } } .frame(width: textWidth, alignment: .leading) @@ -81,7 +69,10 @@ struct MinimalisticMusicPlayerView: View { playbackControls .padding(.top, 4) - // Lyrics are displayed inline under the artist name (see header area) + if enableLyrics { + lyricsView + .padding(.top, 10) + } reminderList } @@ -90,7 +81,6 @@ struct MinimalisticMusicPlayerView: View { .padding(.bottom, shouldShowReminderList ? ReminderLiveActivityManager.listBottomPadding : ReminderLiveActivityManager.baselineMinimalisticBottomPadding) .frame(maxWidth: .infinity) .frame(height: calculateDynamicHeight(), alignment: .center) - .animation(.easeInOut(duration: 0.3), value: musicManager.currentLyrics) } // MARK: - TypingLyricView @@ -174,8 +164,10 @@ struct MinimalisticMusicPlayerView: View { height += 40 + 2 // controls + top padding // Add lyrics height if enabled in settings (reserve space even while loading) - if Defaults[.enableLyrics] { - height += 20 + 4 // lyrics + top padding + if enableLyrics { + let lyricsTopPadding: CGFloat = 10 + let lyricsEstimatedHeight: CGFloat = 30 + height += lyricsTopPadding + lyricsEstimatedHeight } // Add reminder list height if showing @@ -198,6 +190,36 @@ struct MinimalisticMusicPlayerView: View { .animation(.easeInOut(duration: 0.18), value: shouldShowReminderList) .environmentObject(vm) } + + private var lyricsView: some View { + let line = musicManager.currentLyrics.trimmingCharacters(in: .whitespacesAndNewlines) + let transition: AnyTransition = .asymmetric( + insertion: .move(edge: .bottom).combined(with: .opacity), + removal: .move(edge: .top).combined(with: .opacity) + ) + + return HStack(spacing: 6) { + if !line.isEmpty { + Image(systemName: "music.note") + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.white.opacity(0.7)) + .symbolRenderingMode(.monochrome) + + Text(line) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.white.opacity(0.88)) + .lineLimit(2) + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.trailing, 6) + .id(line) + .transition(transition) + } + } + .padding(.horizontal, 6) + .frame(maxWidth: .infinity, alignment: .leading) + .animation(.smooth(duration: 0.32), value: line) + } private struct MinimalisticReminderEventListView: View { diff --git a/DynamicIsland/components/Notch/NotchHomeView.swift b/DynamicIsland/components/Notch/NotchHomeView.swift index c148f95..f072590 100644 --- a/DynamicIsland/components/Notch/NotchHomeView.swift +++ b/DynamicIsland/components/Notch/NotchHomeView.swift @@ -157,6 +157,11 @@ struct MusicControlsView: View { .fontWeight(.medium) // Lyrics shown under the author name (same font size as author) when enabled in settings if Defaults[.enableLyrics] { + let transition = AnyTransition.asymmetric( + insertion: .move(edge: .bottom).combined(with: .opacity), + removal: .move(edge: .top).combined(with: .opacity) + ) + Text(musicManager.currentLyrics) .font(.headline) .foregroundColor(.white.opacity(0.8)) @@ -164,8 +169,9 @@ struct MusicControlsView: View { .multilineTextAlignment(.leading) .frame(maxWidth: .infinity, alignment: .leading) .padding(.top, 2) - .transition(.opacity) - .animation(.easeInOut(duration: 0.3), value: musicManager.currentLyrics) + .id(musicManager.currentLyrics) + .transition(transition) + .animation(.easeInOut(duration: 0.32), value: musicManager.currentLyrics) } } } diff --git a/DynamicIsland/models/DynamicIslandViewModel.swift b/DynamicIsland/models/DynamicIslandViewModel.swift index 803f3e0..811bb7d 100644 --- a/DynamicIsland/models/DynamicIslandViewModel.swift +++ b/DynamicIsland/models/DynamicIslandViewModel.swift @@ -198,11 +198,6 @@ class DynamicIslandViewModel: NSObject, ObservableObject { if Defaults[.enableMinimalisticUI] { let extraHeight = ReminderLiveActivityManager.additionalHeight(forRowCount: minimalisticReminderRowCount) baseSize.height += extraHeight - - // Add extra height for lyrics if enabled in settings - if Defaults[.enableLyrics] { - baseSize.height += 24 // Additional height for lyrics display - } } // Only apply dynamic sizing when on stats tab and stats are enabled diff --git a/DynamicIsland/sizing/matters.swift b/DynamicIsland/sizing/matters.swift index 5a70013..f48ba08 100644 --- a/DynamicIsland/sizing/matters.swift +++ b/DynamicIsland/sizing/matters.swift @@ -15,7 +15,18 @@ let downloadSneakSize: CGSize = .init(width: 65, height: 1) let batterySneakSize: CGSize = .init(width: 160, height: 1) let openNotchSize: CGSize = .init(width: 640, height: 190) -let minimalisticOpenNotchSize: CGSize = .init(width: 420, height: 200) +private let minimalisticBaseOpenNotchSize: CGSize = .init(width: 420, height: 180) +private let minimalisticLyricsExtraHeight: CGFloat = 30 + +var minimalisticOpenNotchSize: CGSize { + var size = minimalisticBaseOpenNotchSize + + if Defaults[.enableLyrics] { + size.height += minimalisticLyricsExtraHeight + } + + return size +} let cornerRadiusInsets: (opened: (top: CGFloat, bottom: CGFloat), closed: (top: CGFloat, bottom: CGFloat)) = (opened: (top: 19, bottom: 24), closed: (top: 6, bottom: 14)) let minimalisticCornerRadiusInsets: (opened: (top: CGFloat, bottom: CGFloat), closed: (top: CGFloat, bottom: CGFloat)) = (opened: (top: 35, bottom: 35), closed: cornerRadiusInsets.closed)