mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-28 15:44:32 +00:00
BugFix: LiveActivity expands too much
This commit is contained in:
parent
e500f5a5b2
commit
f8466c2027
5 changed files with 162 additions and 16 deletions
|
|
@ -16,6 +16,7 @@ struct AnimationEditorView: View {
|
|||
let sourceURL: URL
|
||||
let isRemoteURL: Bool
|
||||
@Binding var animation: CustomIdleAnimation?
|
||||
let existingAnimation: CustomIdleAnimation?
|
||||
|
||||
@State private var name: String
|
||||
@State private var speed: CGFloat = 1.0
|
||||
|
|
@ -26,6 +27,8 @@ struct AnimationEditorView: View {
|
|||
@State private var cropHeight: CGFloat = 20
|
||||
@State private var rotation: CGFloat = 0
|
||||
@State private var opacity: CGFloat = 1.0
|
||||
@State private var paddingBottom: CGFloat = 0
|
||||
@State private var expandWithAnimation: Bool = false
|
||||
|
||||
@State private var showingError = false
|
||||
@State private var errorMessage = ""
|
||||
|
|
@ -34,21 +37,37 @@ struct AnimationEditorView: View {
|
|||
// Preview state
|
||||
@State private var previewScale: CGFloat = 1.0
|
||||
|
||||
init(sourceURL: URL, isRemoteURL: Bool, animation: Binding<CustomIdleAnimation?>) {
|
||||
init(sourceURL: URL, isRemoteURL: Bool, animation: Binding<CustomIdleAnimation?>, existingAnimation: CustomIdleAnimation? = nil) {
|
||||
self.sourceURL = sourceURL
|
||||
self.isRemoteURL = isRemoteURL
|
||||
self._animation = animation
|
||||
self.existingAnimation = existingAnimation
|
||||
|
||||
// Initialize name from URL
|
||||
let fileName = sourceURL.deletingPathExtension().lastPathComponent
|
||||
_name = State(initialValue: fileName)
|
||||
// Initialize from existing animation if editing, otherwise from URL
|
||||
if let existing = existingAnimation {
|
||||
_name = State(initialValue: existing.name)
|
||||
_speed = State(initialValue: existing.speed)
|
||||
let config = existing.transformConfig
|
||||
_scale = State(initialValue: config.scale)
|
||||
_offsetX = State(initialValue: config.offsetX)
|
||||
_offsetY = State(initialValue: config.offsetY)
|
||||
_cropWidth = State(initialValue: config.cropWidth)
|
||||
_cropHeight = State(initialValue: config.cropHeight)
|
||||
_rotation = State(initialValue: config.rotation)
|
||||
_opacity = State(initialValue: config.opacity)
|
||||
_paddingBottom = State(initialValue: config.paddingBottom)
|
||||
_expandWithAnimation = State(initialValue: config.expandWithAnimation)
|
||||
} else {
|
||||
let fileName = sourceURL.deletingPathExtension().lastPathComponent
|
||||
_name = State(initialValue: fileName)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
// Header
|
||||
HStack {
|
||||
Text("Customize Animation")
|
||||
Text(existingAnimation != nil ? "Edit Animation" : "Customize Animation")
|
||||
.font(.title2)
|
||||
.fontWeight(.semibold)
|
||||
|
||||
|
|
@ -94,6 +113,7 @@ struct AnimationEditorView: View {
|
|||
.offset(x: offsetX * previewScale, y: offsetY * previewScale)
|
||||
.rotationEffect(.degrees(rotation))
|
||||
.opacity(opacity)
|
||||
.padding(.bottom, paddingBottom * previewScale)
|
||||
.clipped()
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
|
|
@ -218,6 +238,21 @@ struct AnimationEditorView: View {
|
|||
.buttonStyle(.bordered)
|
||||
.controlSize(.small)
|
||||
}
|
||||
|
||||
// Padding Bottom
|
||||
HStack {
|
||||
Text("Bottom Pad:")
|
||||
.frame(width: 80, alignment: .leading)
|
||||
Slider(value: $paddingBottom, in: -20...20)
|
||||
Text(String(format: "%.1f", paddingBottom))
|
||||
.frame(width: 50, alignment: .trailing)
|
||||
.foregroundStyle(.secondary)
|
||||
Button("Reset") {
|
||||
withAnimation { paddingBottom = 0 }
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.controlSize(.small)
|
||||
}
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
|
@ -282,6 +317,24 @@ struct AnimationEditorView: View {
|
|||
|
||||
Divider()
|
||||
|
||||
// Display Options
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("Display Options")
|
||||
.font(.headline)
|
||||
|
||||
Toggle(isOn: $expandWithAnimation) {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Expand Notch with Animation")
|
||||
.font(.body)
|
||||
Text("When enabled, the Dynamic Island will expand horizontally to match the animation size")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
// Presets
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("Quick Presets")
|
||||
|
|
@ -296,6 +349,8 @@ struct AnimationEditorView: View {
|
|||
offsetX = 0
|
||||
offsetY = 0
|
||||
rotation = 0
|
||||
paddingBottom = 0
|
||||
expandWithAnimation = false
|
||||
}
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
|
|
@ -307,6 +362,8 @@ struct AnimationEditorView: View {
|
|||
cropHeight = 20
|
||||
offsetX = 0
|
||||
offsetY = 0
|
||||
paddingBottom = 0
|
||||
expandWithAnimation = false
|
||||
}
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
|
|
@ -314,6 +371,7 @@ struct AnimationEditorView: View {
|
|||
Button("Scale Up 2x") {
|
||||
withAnimation {
|
||||
scale = 2.0
|
||||
expandWithAnimation = true
|
||||
}
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
|
|
@ -335,6 +393,8 @@ struct AnimationEditorView: View {
|
|||
rotation = 0
|
||||
opacity = 1.0
|
||||
speed = 1.0
|
||||
paddingBottom = 0
|
||||
expandWithAnimation = false
|
||||
}
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
|
|
@ -361,7 +421,7 @@ struct AnimationEditorView: View {
|
|||
|
||||
Spacer()
|
||||
|
||||
Button("Import") {
|
||||
Button(existingAnimation != nil ? "Save Changes" : "Import") {
|
||||
importAnimation()
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
|
|
@ -393,10 +453,23 @@ struct AnimationEditorView: View {
|
|||
cropWidth: cropWidth,
|
||||
cropHeight: cropHeight,
|
||||
rotation: rotation,
|
||||
opacity: opacity
|
||||
opacity: opacity,
|
||||
paddingBottom: paddingBottom,
|
||||
expandWithAnimation: expandWithAnimation
|
||||
)
|
||||
|
||||
// Import with config
|
||||
// If editing existing animation, update it directly
|
||||
if let existing = existingAnimation {
|
||||
var updatedAnimation = existing
|
||||
updatedAnimation.name = name
|
||||
updatedAnimation.speed = speed
|
||||
updatedAnimation.transformConfig = config
|
||||
animation = updatedAnimation
|
||||
dismiss()
|
||||
return
|
||||
}
|
||||
|
||||
// Import with config (new animation)
|
||||
let result = IdleAnimationManager.shared.importLottieFile(
|
||||
from: sourceURL,
|
||||
name: name,
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ struct IdleAnimationsSettingsSection: View {
|
|||
@State private var editorSourceURL: URL?
|
||||
@State private var editorIsRemote = false
|
||||
@State private var editedAnimation: CustomIdleAnimation?
|
||||
@State private var editingExistingAnimation: CustomIdleAnimation?
|
||||
|
||||
var body: some View {
|
||||
Section {
|
||||
|
|
@ -49,6 +50,18 @@ struct IdleAnimationsSettingsSection: View {
|
|||
onDelete: animation.isBuiltIn ? nil : {
|
||||
selectedForDeletion = animation
|
||||
showingDeleteAlert = true
|
||||
},
|
||||
onEdit: animation.isBuiltIn ? nil : {
|
||||
editingExistingAnimation = animation
|
||||
if case .lottieFile(let url) = animation.source {
|
||||
editorSourceURL = url
|
||||
editorIsRemote = false
|
||||
showingEditor = true
|
||||
} else if case .lottieURL(let url) = animation.source {
|
||||
editorSourceURL = url
|
||||
editorIsRemote = true
|
||||
showingEditor = true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -107,19 +120,35 @@ struct IdleAnimationsSettingsSection: View {
|
|||
AnimationEditorView(
|
||||
sourceURL: url,
|
||||
isRemoteURL: editorIsRemote,
|
||||
animation: $editedAnimation
|
||||
animation: $editedAnimation,
|
||||
existingAnimation: editingExistingAnimation
|
||||
)
|
||||
.frame(minWidth: 800, minHeight: 600)
|
||||
.onChange(of: editedAnimation) { oldValue, newValue in
|
||||
if let animation = newValue {
|
||||
// Animation was successfully imported via editor
|
||||
withAnimation {
|
||||
selectedIdleAnimation = animation
|
||||
// Animation was successfully imported/edited via editor
|
||||
if let existingAnim = editingExistingAnimation {
|
||||
// Editing existing animation - update it
|
||||
if var animations = customIdleAnimations as? [CustomIdleAnimation],
|
||||
let index = animations.firstIndex(where: { $0.id == existingAnim.id }) {
|
||||
animations[index] = animation
|
||||
customIdleAnimations = animations
|
||||
// Update selection if this was the selected animation
|
||||
if selectedIdleAnimation?.id == existingAnim.id {
|
||||
selectedIdleAnimation = animation
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// New import - just select it
|
||||
withAnimation {
|
||||
selectedIdleAnimation = animation
|
||||
}
|
||||
}
|
||||
showingEditor = false
|
||||
// Reset editor state
|
||||
editorSourceURL = nil
|
||||
editedAnimation = nil
|
||||
editingExistingAnimation = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -184,6 +213,7 @@ struct AnimationPreviewCard: View {
|
|||
let isSelected: Bool
|
||||
let onSelect: () -> Void
|
||||
let onDelete: (() -> Void)?
|
||||
let onEdit: (() -> Void)?
|
||||
|
||||
@State private var isHovering = false
|
||||
|
||||
|
|
@ -262,6 +292,19 @@ struct AnimationPreviewCard: View {
|
|||
.onTapGesture {
|
||||
onSelect()
|
||||
}
|
||||
.contextMenu {
|
||||
if let onEdit = onEdit {
|
||||
Button("Edit Animation") {
|
||||
onEdit()
|
||||
}
|
||||
Divider()
|
||||
}
|
||||
if let onDelete = onDelete {
|
||||
Button("Delete", role: .destructive) {
|
||||
onDelete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue