feat: 键盘唤起动画
This commit is contained in:
parent
8bf24a3afe
commit
85bd75b03a
@ -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()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user