Atoll/DynamicIsland/components/LockScreen/LockScreenLiveActivityOverlay.swift
2026-01-08 16:51:12 +01:00

80 lines
2.4 KiB
Swift

import SwiftUI
final class LockScreenLiveActivityOverlayModel: ObservableObject {
@Published var scale: CGFloat = 0.6
@Published var opacity: Double = 0
}
struct LockScreenLiveActivityOverlay: View {
@ObservedObject var model: LockScreenLiveActivityOverlayModel
@ObservedObject var animator: LockIconAnimator
let notchSize: CGSize
private var indicatorSize: CGFloat {
max(0, notchSize.height - 12)
}
private var horizontalPadding: CGFloat {
cornerRadiusInsets.closed.bottom
}
private var totalWidth: CGFloat {
notchSize.width + (indicatorSize * 2) + (horizontalPadding * 2)
}
private var collapsedScale: CGFloat {
Self.collapsedScale(for: notchSize)
}
@State private var isHovering: Bool = false
var body: some View {
HStack(spacing: 0) {
Color.clear
.overlay(alignment: .leading) {
LockIconProgressView(progress: animator.progress)
.frame(width: indicatorSize, height: indicatorSize)
}
.frame(width: indicatorSize, height: notchSize.height)
Rectangle()
.fill(.black)
.frame(width: notchSize.width, height: notchSize.height)
Color.clear
.frame(width: indicatorSize, height: notchSize.height)
}
.frame(width: notchSize.width + (indicatorSize * 2), height: notchSize.height)
.padding(.horizontal, horizontalPadding)
.background(Color.black)
.clipShape(
NotchShape(
topCornerRadius: cornerRadiusInsets.closed.top,
bottomCornerRadius: cornerRadiusInsets.closed.bottom
)
)
.frame(width: totalWidth, height: notchSize.height)
.scaleEffect(x: max(model.scale, collapsedScale) * (isHovering ? 1.03 : 1.0),
y: 1 * (isHovering ? 1.03 : 1.0),
anchor: .center)
.opacity(model.opacity)
.onHover { hovering in
withAnimation(.smooth(duration: 0.2)) {
isHovering = hovering
}
if hovering {
NSHapticFeedbackManager.defaultPerformer.perform(.alignment, performanceTime: .default)
}
}
}
}
extension LockScreenLiveActivityOverlay {
static func collapsedScale(for notchSize: CGSize) -> CGFloat {
let indicatorSize = max(0, notchSize.height - 12)
let horizontalPadding = cornerRadiusInsets.closed.bottom
let totalWidth = notchSize.width + (indicatorSize * 2) + (horizontalPadding * 2)
guard totalWidth > 0 else { return 1 }
return notchSize.width / totalWidth
}
}