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