mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-28 23:54:32 +00:00
Update LockScreenMusicPanel for smooth vertical expansion
This commit is contained in:
parent
699b9402dc
commit
061564eb8b
3 changed files with 156 additions and 104 deletions
|
|
@ -20363,6 +20363,10 @@
|
||||||
"comment" : "A message displayed when no AirPlay devices are found.",
|
"comment" : "A message displayed when no AirPlay devices are found.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
|
"No AirPlay outputs available" : {
|
||||||
|
"comment" : "A message displayed when there are no available AirPlay outputs.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"No audio outputs available" : {
|
"No audio outputs available" : {
|
||||||
"comment" : "Displayed when no audio output devices can be listed in the media output selector.",
|
"comment" : "Displayed when no audio output devices can be listed in the media output selector.",
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
|
|
|
||||||
|
|
@ -45,9 +45,8 @@ struct LockScreenMusicPanel: View {
|
||||||
@State private var isActive = true
|
@State private var isActive = true
|
||||||
@State private var isExpanded = false
|
@State private var isExpanded = false
|
||||||
@State private var isVolumeSliderVisible = false
|
@State private var isVolumeSliderVisible = false
|
||||||
@State private var isMediaOutputPopoverPresented = false
|
|
||||||
@State private var isAirPlayPopoverPresented = false
|
@State private var isAirPlayPopoverPresented = false
|
||||||
@State private var measuredAirPlayHeight: CGFloat?
|
|
||||||
@State private var collapseWorkItem: DispatchWorkItem?
|
@State private var collapseWorkItem: DispatchWorkItem?
|
||||||
@State private var parallaxResumeWorkItem: DispatchWorkItem?
|
@State private var parallaxResumeWorkItem: DispatchWorkItem?
|
||||||
@State private var isParallaxSuspended = false
|
@State private var isParallaxSuspended = false
|
||||||
|
|
@ -139,8 +138,8 @@ struct LockScreenMusicPanel: View {
|
||||||
}
|
}
|
||||||
.shadow(color: Color.black.opacity(0.3), radius: 20, x: 0, y: 10)
|
.shadow(color: Color.black.opacity(0.3), radius: 20, x: 0, y: 10)
|
||||||
.contentShape(RoundedRectangle(cornerRadius: panelCornerRadius, style: .continuous))
|
.contentShape(RoundedRectangle(cornerRadius: panelCornerRadius, style: .continuous))
|
||||||
.animation(.spring(response: 0.48, dampingFraction: 0.82, blendDuration: 0.18), value: isExpanded)
|
.animation(.easeInOut(duration: 0.28), value: isExpanded)
|
||||||
.animation(.spring(response: 0.45, dampingFraction: 0.85, blendDuration: 0.18), value: shouldShowVolumeSlider)
|
.animation(.easeInOut(duration: 0.24), value: shouldShowVolumeSlider)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
sliderValue = musicManager.elapsedTime
|
sliderValue = musicManager.elapsedTime
|
||||||
isActive = true
|
isActive = true
|
||||||
|
|
@ -175,8 +174,6 @@ struct LockScreenMusicPanel: View {
|
||||||
if useMergedAirPlayOutput {
|
if useMergedAirPlayOutput {
|
||||||
if visible && isAppleMusicActive {
|
if visible && isAppleMusicActive {
|
||||||
Task { await airPlayManager.refreshDevices() }
|
Task { await airPlayManager.refreshDevices() }
|
||||||
} else {
|
|
||||||
measuredAirPlayHeight = nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updatePanelSize()
|
updatePanelSize()
|
||||||
|
|
@ -186,6 +183,11 @@ struct LockScreenMusicPanel: View {
|
||||||
updatePanelSize()
|
updatePanelSize()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onChange(of: routeManager.devices) { _, _ in
|
||||||
|
if !useMergedAirPlayOutput {
|
||||||
|
updatePanelSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
.onChange(of: enableLyrics) { _, _ in
|
.onChange(of: enableLyrics) { _, _ in
|
||||||
withAnimation(.spring(response: 0.45, dampingFraction: 0.85)) {
|
withAnimation(.spring(response: 0.45, dampingFraction: 0.85)) {
|
||||||
updatePanelSize()
|
updatePanelSize()
|
||||||
|
|
@ -334,7 +336,7 @@ struct LockScreenMusicPanel: View {
|
||||||
private func toggleExpanded() {
|
private func toggleExpanded() {
|
||||||
let newState = !isExpanded
|
let newState = !isExpanded
|
||||||
suspendParallaxInteraction()
|
suspendParallaxInteraction()
|
||||||
withAnimation(.spring(response: 0.5, dampingFraction: 0.82)) {
|
withAnimation(.easeInOut(duration: 0.28)) {
|
||||||
isExpanded = newState
|
isExpanded = newState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -353,7 +355,7 @@ struct LockScreenMusicPanel: View {
|
||||||
|
|
||||||
let workItem = DispatchWorkItem {
|
let workItem = DispatchWorkItem {
|
||||||
suspendParallaxInteraction()
|
suspendParallaxInteraction()
|
||||||
withAnimation(.spring(response: 0.5, dampingFraction: 0.85)) {
|
withAnimation(.easeInOut(duration: 0.28)) {
|
||||||
isExpanded = false
|
isExpanded = false
|
||||||
}
|
}
|
||||||
logPanelAppearance(event: "⏱️ Auto-collapsed")
|
logPanelAppearance(event: "⏱️ Auto-collapsed")
|
||||||
|
|
@ -461,6 +463,10 @@ struct LockScreenMusicPanel: View {
|
||||||
airPlaySection
|
airPlaySection
|
||||||
.frame(maxWidth: .infinity, alignment: alignment)
|
.frame(maxWidth: .infinity, alignment: alignment)
|
||||||
.transition(.scale(scale: 0.98, anchor: .top).combined(with: .opacity))
|
.transition(.scale(scale: 0.98, anchor: .top).combined(with: .opacity))
|
||||||
|
} else if shouldShowRouteSelector {
|
||||||
|
mediaOutputDevicesSection
|
||||||
|
.frame(maxWidth: .infinity, alignment: alignment)
|
||||||
|
.transition(.scale(scale: 0.98, anchor: .top).combined(with: .opacity))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -471,9 +477,10 @@ struct LockScreenMusicPanel: View {
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, alignment: alignment)
|
.frame(maxWidth: .infinity, alignment: alignment)
|
||||||
.padding(.top, isExpanded ? 6 : 2)
|
.padding(.top, isExpanded ? 6 : 2)
|
||||||
.animation(.spring(response: 0.45, dampingFraction: 0.82), value: shouldShowVolumeSlider)
|
.animation(.easeInOut(duration: 0.24), value: shouldShowVolumeSlider)
|
||||||
.animation(.spring(response: 0.45, dampingFraction: 0.82), value: shouldShowAirPlay)
|
.animation(.easeInOut(duration: 0.24), value: shouldShowAirPlay)
|
||||||
.animation(.spring(response: 0.45, dampingFraction: 0.82), value: enableLyrics)
|
.animation(.easeInOut(duration: 0.24), value: shouldShowRouteSelector)
|
||||||
|
.animation(.easeInOut(duration: 0.24), value: enableLyrics)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func controlsRow(alignment: Alignment, spacing: CGFloat) -> some View {
|
private func controlsRow(alignment: Alignment, spacing: CGFloat) -> some View {
|
||||||
|
|
@ -570,11 +577,7 @@ struct LockScreenMusicPanel: View {
|
||||||
musicManager.toggleRepeat()
|
musicManager.toggleRepeat()
|
||||||
}
|
}
|
||||||
case .mediaOutput:
|
case .mediaOutput:
|
||||||
if useMergedAirPlayOutput {
|
mediaOutputControlButton
|
||||||
mediaOutputControlButton
|
|
||||||
} else {
|
|
||||||
standaloneMediaOutputButton
|
|
||||||
}
|
|
||||||
case .airPlay:
|
case .airPlay:
|
||||||
if useMergedAirPlayOutput {
|
if useMergedAirPlayOutput {
|
||||||
mediaOutputControlButton
|
mediaOutputControlButton
|
||||||
|
|
@ -658,36 +661,6 @@ struct LockScreenMusicPanel: View {
|
||||||
.accessibilityLabel("Media output")
|
.accessibilityLabel("Media output")
|
||||||
}
|
}
|
||||||
|
|
||||||
private var standaloneMediaOutputButton: some View {
|
|
||||||
let frameSize: CGFloat = isExpanded ? 56 : 32
|
|
||||||
let iconSize: CGFloat = isExpanded ? 26 : 18
|
|
||||||
|
|
||||||
return PanelControlButton(
|
|
||||||
icon: mediaOutputIcon,
|
|
||||||
frameSize: frameSize,
|
|
||||||
iconSize: iconSize,
|
|
||||||
iconColor: isMediaOutputPopoverPresented ? .accentColor : .white.opacity(0.8),
|
|
||||||
backgroundOpacity: isMediaOutputPopoverPresented ? 0.22 : 0.0,
|
|
||||||
interaction: .none,
|
|
||||||
symbolEffect: .replace
|
|
||||||
) {
|
|
||||||
registerInteraction()
|
|
||||||
isMediaOutputPopoverPresented.toggle()
|
|
||||||
if isMediaOutputPopoverPresented {
|
|
||||||
routeManager.refreshDevices()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.accessibilityLabel("Media output")
|
|
||||||
.popover(isPresented: $isMediaOutputPopoverPresented, arrowEdge: .bottom) {
|
|
||||||
MediaOutputSelectorPopover(
|
|
||||||
routeManager: routeManager,
|
|
||||||
volumeModel: volumeModel,
|
|
||||||
onHoverChanged: { _ in },
|
|
||||||
dismiss: { isMediaOutputPopoverPresented = false }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var standaloneAirPlayButton: some View {
|
private var standaloneAirPlayButton: some View {
|
||||||
let frameSize: CGFloat = isExpanded ? 56 : 32
|
let frameSize: CGFloat = isExpanded ? 56 : 32
|
||||||
let iconSize: CGFloat = isExpanded ? 26 : 18
|
let iconSize: CGFloat = isExpanded ? 26 : 18
|
||||||
|
|
@ -792,58 +765,113 @@ struct LockScreenMusicPanel: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private var airPlaySection: some View {
|
private var airPlaySection: some View {
|
||||||
VStack(spacing: 6) {
|
accessorySectionContainer {
|
||||||
ForEach(airPlayManager.devices) { device in
|
if airPlayManager.devices.isEmpty {
|
||||||
VStack(spacing: 4) {
|
Text("No AirPlay outputs available")
|
||||||
Button {
|
.font(.system(size: isExpanded ? 13 : 11, weight: .medium))
|
||||||
registerInteraction()
|
.foregroundColor(.white.opacity(0.68))
|
||||||
Task { await airPlayManager.toggleDevice(device) }
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
} label: {
|
.padding(.vertical, 8)
|
||||||
HStack(spacing: 8) {
|
.padding(.horizontal, 12)
|
||||||
Image(systemName: device.iconName)
|
} else {
|
||||||
.font(.system(size: isExpanded ? 14 : 12, weight: .medium))
|
ScrollView(.vertical, showsIndicators: true) {
|
||||||
.foregroundColor(.white.opacity(0.8))
|
VStack(spacing: 6) {
|
||||||
Text(device.name)
|
ForEach(airPlayManager.devices) { device in
|
||||||
.font(.system(size: isExpanded ? 13 : 11, weight: .medium))
|
VStack(spacing: 4) {
|
||||||
.foregroundColor(.white)
|
Button {
|
||||||
.lineLimit(1)
|
registerInteraction()
|
||||||
Spacer()
|
Task { await airPlayManager.toggleDevice(device) }
|
||||||
if device.isSelected {
|
} label: {
|
||||||
Image(systemName: "checkmark")
|
HStack(spacing: 8) {
|
||||||
.font(.system(size: isExpanded ? 12 : 10, weight: .bold))
|
Image(systemName: device.iconName)
|
||||||
.foregroundColor(.accentColor)
|
.font(.system(size: isExpanded ? 14 : 12, weight: .medium))
|
||||||
|
.foregroundColor(.white.opacity(0.8))
|
||||||
|
Text(device.name)
|
||||||
|
.font(.system(size: isExpanded ? 13 : 11, weight: .medium))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.lineLimit(1)
|
||||||
|
Spacer()
|
||||||
|
if device.isSelected {
|
||||||
|
Image(systemName: "checkmark")
|
||||||
|
.font(.system(size: isExpanded ? 12 : 10, weight: .bold))
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.vertical, 4)
|
||||||
|
.padding(.horizontal, 12)
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
|
||||||
|
if device.isSelected {
|
||||||
|
AirPlayVolumeSlider(
|
||||||
|
airPlayManager: airPlayManager,
|
||||||
|
deviceID: device.id
|
||||||
|
)
|
||||||
|
.padding(.horizontal, 12)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.vertical, 4)
|
|
||||||
.padding(.horizontal, 12)
|
|
||||||
}
|
|
||||||
.buttonStyle(.plain)
|
|
||||||
|
|
||||||
if device.isSelected {
|
|
||||||
AirPlayVolumeSlider(
|
|
||||||
airPlayManager: airPlayManager,
|
|
||||||
deviceID: device.id
|
|
||||||
)
|
|
||||||
.padding(.horizontal, 12)
|
|
||||||
}
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
|
.frame(maxHeight: accessorySectionScrollMaxHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
.background(
|
}
|
||||||
RoundedRectangle(cornerRadius: isExpanded ? 16 : 12, style: .continuous)
|
|
||||||
.fill(sliderBackgroundFill)
|
private var mediaOutputDevicesSection: some View {
|
||||||
)
|
accessorySectionContainer {
|
||||||
.background(
|
if routeManager.devices.isEmpty {
|
||||||
GeometryReader { geo in
|
Text("No audio outputs available")
|
||||||
Color.clear
|
.font(.system(size: isExpanded ? 13 : 11, weight: .medium))
|
||||||
.onAppear { measuredAirPlayHeight = geo.size.height }
|
.foregroundColor(.white.opacity(0.68))
|
||||||
.onChange(of: geo.size.height) { _, newHeight in
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
measuredAirPlayHeight = newHeight
|
.padding(.vertical, 8)
|
||||||
updatePanelSize()
|
.padding(.horizontal, 12)
|
||||||
|
} else {
|
||||||
|
ScrollView(.vertical, showsIndicators: true) {
|
||||||
|
VStack(alignment: .leading, spacing: 6) {
|
||||||
|
ForEach(routeManager.devices) { device in
|
||||||
|
Button {
|
||||||
|
registerInteraction()
|
||||||
|
routeManager.select(device: device)
|
||||||
|
} label: {
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
Image(systemName: device.iconName)
|
||||||
|
.font(.system(size: isExpanded ? 14 : 12, weight: .medium))
|
||||||
|
.foregroundColor(.white.opacity(0.82))
|
||||||
|
Text(device.name)
|
||||||
|
.font(.system(size: isExpanded ? 13 : 11, weight: .medium))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.lineLimit(1)
|
||||||
|
Spacer()
|
||||||
|
if device.id == routeManager.activeDeviceID {
|
||||||
|
Image(systemName: "checkmark")
|
||||||
|
.font(.system(size: isExpanded ? 12 : 10, weight: .bold))
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.vertical, 5)
|
||||||
|
.padding(.horizontal, 12)
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
.frame(maxHeight: accessorySectionScrollMaxHeight)
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
.padding(.vertical, 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func accessorySectionContainer<Content: View>(@ViewBuilder content: () -> Content) -> some View {
|
||||||
|
content()
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: isExpanded ? 16 : 12, style: .continuous)
|
||||||
|
.fill(sliderBackgroundFill)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var sliderBackgroundFill: Color {
|
private var sliderBackgroundFill: Color {
|
||||||
|
|
@ -865,6 +893,14 @@ struct LockScreenMusicPanel: View {
|
||||||
useMergedAirPlayOutput && shouldShowVolumeSlider && !airPlayManager.devices.isEmpty
|
useMergedAirPlayOutput && shouldShowVolumeSlider && !airPlayManager.devices.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var shouldShowRouteSelector: Bool {
|
||||||
|
!useMergedAirPlayOutput && shouldShowVolumeSlider
|
||||||
|
}
|
||||||
|
|
||||||
|
private var shouldShowAccessorySection: Bool {
|
||||||
|
shouldShowAirPlay || shouldShowRouteSelector
|
||||||
|
}
|
||||||
|
|
||||||
private var shouldShowVolumeSlider: Bool {
|
private var shouldShowVolumeSlider: Bool {
|
||||||
showMediaOutputControl && isVolumeSliderVisible
|
showMediaOutputControl && isVolumeSliderVisible
|
||||||
}
|
}
|
||||||
|
|
@ -873,25 +909,31 @@ struct LockScreenMusicPanel: View {
|
||||||
sliderHeight(forExpanded: isExpanded, visible: shouldShowVolumeSlider)
|
sliderHeight(forExpanded: isExpanded, visible: shouldShowVolumeSlider)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var accessorySectionScrollMaxHeight: CGFloat {
|
||||||
|
isExpanded ? 170 : 130
|
||||||
|
}
|
||||||
|
|
||||||
private var lyricsExtraHeight: CGFloat {
|
private var lyricsExtraHeight: CGFloat {
|
||||||
lyricsHeight(forExpanded: isExpanded, enabled: enableLyrics)
|
lyricsHeight(forExpanded: isExpanded, enabled: enableLyrics)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var airPlayExtraHeight: CGFloat {
|
private var accessorySectionExtraHeight: CGFloat {
|
||||||
guard shouldShowAirPlay else { return 0 }
|
guard shouldShowAccessorySection else { return 0 }
|
||||||
if let measured = measuredAirPlayHeight {
|
if shouldShowAirPlay {
|
||||||
return measured
|
let selectedCount = airPlayManager.devices.filter(\.isSelected).count
|
||||||
|
let totalCount = airPlayManager.devices.count
|
||||||
|
let deviceRows: CGFloat = CGFloat(totalCount) * 30 + CGFloat(max(totalCount - 1, 0)) * 6
|
||||||
|
let sliders: CGFloat = CGFloat(selectedCount) * 34
|
||||||
|
return min(deviceRows + sliders + 24, accessorySectionScrollMaxHeight + 16)
|
||||||
}
|
}
|
||||||
// Fallback estimate used only until the first GeometryReader measurement arrives
|
|
||||||
let selectedCount = airPlayManager.devices.filter(\.isSelected).count
|
let totalCount = max(routeManager.devices.count, 1)
|
||||||
let totalCount = airPlayManager.devices.count
|
let deviceRows: CGFloat = CGFloat(totalCount) * 30 + CGFloat(max(totalCount - 1, 0)) * 6
|
||||||
let deviceRows: CGFloat = CGFloat(totalCount) * 22 + CGFloat(max(totalCount - 1, 0)) * 6
|
return min(deviceRows + 24, accessorySectionScrollMaxHeight + 16)
|
||||||
let sliders: CGFloat = CGFloat(selectedCount) * 24
|
|
||||||
return deviceRows + sliders + 16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var totalExtraHeight: CGFloat {
|
private var totalExtraHeight: CGFloat {
|
||||||
sliderExtraHeight + airPlayExtraHeight + lyricsExtraHeight
|
sliderExtraHeight + accessorySectionExtraHeight + lyricsExtraHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
private func lyricsHeight(forExpanded expanded: Bool, enabled: Bool) -> CGFloat {
|
private func lyricsHeight(forExpanded expanded: Bool, enabled: Bool) -> CGFloat {
|
||||||
|
|
@ -901,7 +943,7 @@ struct LockScreenMusicPanel: View {
|
||||||
|
|
||||||
private func panelAdditionalHeight(forExpanded expanded: Bool) -> CGFloat {
|
private func panelAdditionalHeight(forExpanded expanded: Bool) -> CGFloat {
|
||||||
sliderHeight(forExpanded: expanded, visible: shouldShowVolumeSlider) +
|
sliderHeight(forExpanded: expanded, visible: shouldShowVolumeSlider) +
|
||||||
airPlayExtraHeight +
|
accessorySectionExtraHeight +
|
||||||
lyricsHeight(forExpanded: expanded, enabled: enableLyrics)
|
lyricsHeight(forExpanded: expanded, enabled: enableLyrics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -938,9 +980,12 @@ struct LockScreenMusicPanel: View {
|
||||||
let newState = !isVolumeSliderVisible
|
let newState = !isVolumeSliderVisible
|
||||||
if newState {
|
if newState {
|
||||||
routeManager.refreshDevices()
|
routeManager.refreshDevices()
|
||||||
|
if useMergedAirPlayOutput {
|
||||||
|
Task { await airPlayManager.refreshDevices() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
withAnimation(.spring(response: 0.45, dampingFraction: 0.85)) {
|
withAnimation(.easeInOut(duration: 0.24)) {
|
||||||
isVolumeSliderVisible = newState
|
isVolumeSliderVisible = newState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,8 @@ class LockScreenPanelManager {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let resizeDuration: CFTimeInterval = 0.28
|
||||||
|
|
||||||
let baseSize = expanded ? LockScreenMusicPanel.expandedSize : LockScreenMusicPanel.collapsedSize
|
let baseSize = expanded ? LockScreenMusicPanel.expandedSize : LockScreenMusicPanel.collapsedSize
|
||||||
let targetWidth = baseSize.width
|
let targetWidth = baseSize.width
|
||||||
let targetHeight = baseSize.height + additionalHeight
|
let targetHeight = baseSize.height + additionalHeight
|
||||||
|
|
@ -175,7 +177,7 @@ class LockScreenPanelManager {
|
||||||
|
|
||||||
if animated {
|
if animated {
|
||||||
NSAnimationContext.runAnimationGroup { context in
|
NSAnimationContext.runAnimationGroup { context in
|
||||||
context.duration = 0.45
|
context.duration = resizeDuration
|
||||||
context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
||||||
window.animator().setFrame(targetFrame, display: true)
|
window.animator().setFrame(targetFrame, display: true)
|
||||||
}
|
}
|
||||||
|
|
@ -191,7 +193,8 @@ class LockScreenPanelManager {
|
||||||
let targetRadius = expanded ? expandedPanelCornerRadius : collapsedPanelCornerRadius
|
let targetRadius = expanded ? expandedPanelCornerRadius : collapsedPanelCornerRadius
|
||||||
if animated {
|
if animated {
|
||||||
CATransaction.begin()
|
CATransaction.begin()
|
||||||
CATransaction.setAnimationDuration(0.28)
|
CATransaction.setAnimationDuration(resizeDuration)
|
||||||
|
CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: .easeInEaseOut))
|
||||||
window.contentView?.layer?.cornerRadius = targetRadius
|
window.contentView?.layer?.cornerRadius = targetRadius
|
||||||
CATransaction.commit()
|
CATransaction.commit()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue