feat: 键盘唤起动画

This commit is contained in:
jinyaqiu 2025-09-01 14:46:16 +08:00
parent 8bf24a3afe
commit 85bd75b03a

View File

@ -23,20 +23,26 @@ public struct AvatarPicker: View {
self._uploadedFileId = uploadedFileId
}
private var avatarSize: CGFloat {
isKeyboardVisible ? 125 : 225
//
private var scaleFactor: CGFloat {
isKeyboardVisible ? 0.55 : 1.0
}
private var borderWidth: CGFloat {
isKeyboardVisible ? 3 : 4
}
//
private var animation: Animation {
.spring(response: 0.4, dampingFraction: 0.7, blendDuration: 0.3)
}
public var body: some View {
VStack() {
VStack(spacing: 20) {
VStack(spacing: 20) {
// Avatar Image Button
Button(action: {
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
withAnimation(animation) {
showMediaPicker = true
}
}) {
@ -45,18 +51,21 @@ public struct AvatarPicker: View {
Image(uiImage: selectedImage)
.resizable()
.scaledToFill()
.frame(width: avatarSize, height: avatarSize)
.clipShape(RoundedRectangle(cornerRadius: 20))
.frame(width: 225, height: 225)
.scaleEffect(scaleFactor)
.clipShape(RoundedRectangle(cornerRadius: 20 * scaleFactor))
.overlay(
RoundedRectangle(cornerRadius: 20)
.stroke(Color.themePrimary, lineWidth: borderWidth)
.scaleEffect(scaleFactor)
)
} else {
// Default SVG avatar with animated dashed border
SVGImage(svgName: "IP")
.frame(width: avatarSize, height: avatarSize)
.frame(width: 225, height: 225)
.scaleEffect(scaleFactor)
.contentShape(Rectangle())
.clipShape(RoundedRectangle(cornerRadius: 20))
.clipShape(RoundedRectangle(cornerRadius: 20 * scaleFactor))
.overlay(
RoundedRectangle(cornerRadius: 20)
.stroke(style: StrokeStyle(
@ -66,6 +75,7 @@ public struct AvatarPicker: View {
dashPhase: isAnimating ? 40 : 0
))
.foregroundColor(Color.themePrimary)
.scaleEffect(scaleFactor)
)
.onAppear {
withAnimation(Animation.linear(duration: 1.5).repeatForever(autoreverses: false)) {
@ -78,22 +88,23 @@ public struct AvatarPicker: View {
if isUploading {
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: .themePrimary))
.scaleEffect(1.5)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.scaleEffect(1.5 * scaleFactor)
.frame(width: 225, height: 225)
.scaleEffect(scaleFactor)
.background(Color.black.opacity(0.3))
.clipShape(RoundedRectangle(cornerRadius: 20))
.clipShape(RoundedRectangle(cornerRadius: 20 * scaleFactor))
.transition(.opacity)
}
}
.frame(width: avatarSize, height: avatarSize)
.animation(.spring(response: 0.4, dampingFraction: 0.7), value: isKeyboardVisible)
.frame(width: 225 * scaleFactor, height: 225 * scaleFactor)
.animation(animation, value: isKeyboardVisible)
}
.buttonStyle(ScaleButtonStyle())
// Upload Button (only shown when username is not shown)
if !showUsername {
Button(action: {
withAnimation {
withAnimation(animation) {
showMediaPicker = true
}
}) {
@ -107,11 +118,14 @@ public struct AvatarPicker: View {
RoundedRectangle(cornerRadius: 16)
.fill(Color.themePrimaryLight)
)
.scaleEffect(scaleFactor)
}
.frame(maxWidth: .infinity)
.transition(.opacity.combined(with: .move(edge: .bottom)))
.animation(animation, value: isKeyboardVisible)
}
}
.animation(animation, value: isKeyboardVisible)
.sheet(isPresented: $showMediaPicker) {
MediaPicker(
selectedMedia: Binding(
@ -123,7 +137,7 @@ public struct AvatarPicker: View {
uploadManager.addMedia(newMedia)
// Start upload process
withAnimation {
withAnimation(animation) {
isUploading = true
}
uploadManager.startUpload()
@ -168,7 +182,7 @@ public struct AvatarPicker: View {
print("🖼️ Updating selected image")
DispatchQueue.main.async {
withAnimation(.spring()) {
withAnimation(animation) {
self.selectedImage = image
self.uploadedFileId = fileId
self.isUploading = false
@ -190,7 +204,7 @@ public struct AvatarPicker: View {
if hasFailures {
print("❌ Some uploads failed")
DispatchQueue.main.async {
withAnimation {
withAnimation(animation) {
self.isUploading = false
}
}
@ -198,7 +212,7 @@ public struct AvatarPicker: View {
}
if !showUsername {
Button(action: {
withAnimation {
withAnimation(animation) {
showImageCapture = true
}
}) {
@ -212,14 +226,16 @@ public struct AvatarPicker: View {
RoundedRectangle(cornerRadius: 16)
.fill(Color.themePrimaryLight)
)
.scaleEffect(scaleFactor)
}
.frame(maxWidth: .infinity)
.animation(animation, value: isKeyboardVisible)
.sheet(isPresented: $showImageCapture) {
CustomCameraView(isPresented: $showImageCapture) { image in
selectedImage = image
uploadManager.clearAllMedia()
uploadManager.addMedia([.image(image)])
withAnimation {
withAnimation(animation) {
isUploading = true
}
uploadManager.startUpload()