feat: 键盘动效

This commit is contained in:
jinyaqiu 2025-08-22 09:41:01 +08:00
parent df82849352
commit 12bcc46f45
4 changed files with 198 additions and 97 deletions

View File

@ -34,6 +34,8 @@ struct Theme {
static let textInverse = Color.white //
static let textMessage = Color(hex: "7B7B7B") //
static let textMessageMain = Color(hex: "000000") //
static let textWhite = Color(hex: "FFFFFF") //
static let textWhiteSecondary = Color(hex: "FAFAFA") //
// MARK: -
static let success = Color(hex: "10B981") //
@ -119,6 +121,8 @@ extension Color {
static var themeTextSecondary: Color { Theme.Colors.textSecondary }
static var themeTextMessage: Color { Theme.Colors.textMessage }
static var themeTextMessageMain: Color { Theme.Colors.textMessageMain }
static var themeTextWhite: Color { Theme.Colors.textWhite }
static var themeTextWhiteSecondary: Color { Theme.Colors.textWhiteSecondary }
}
// MARK: -

View File

@ -6,10 +6,12 @@ public struct AvatarPicker: View {
@State private var isUploading = false
@Binding var selectedImage: UIImage?
@Binding var showUsername: Bool
@Binding var isKeyboardVisible: Bool
public init(selectedImage: Binding<UIImage?>, showUsername: Binding<Bool>) {
public init(selectedImage: Binding<UIImage?>, showUsername: Binding<Bool>, isKeyboardVisible: Binding<Bool>) {
self._selectedImage = selectedImage
self._showUsername = showUsername
self._isKeyboardVisible = isKeyboardVisible
}
public var body: some View {
@ -18,18 +20,22 @@ public struct AvatarPicker: View {
Button(action: {
showMediaPicker = true
}) {
ZStack {
ZStack {
if let selectedImage = selectedImage {
Image(uiImage: selectedImage)
.resizable()
.scaledToFill()
.frame(width: 225, height: 225)
.frame(width: isKeyboardVisible ? 125 : 225,
height: isKeyboardVisible ? 125 : 225)
.clipShape(RoundedRectangle(cornerRadius: 20))
.animation(.spring(response: 0.4, dampingFraction: 1), value: isKeyboardVisible)
} else {
// Default SVG avatar
SVGImage(svgName: "Avatar")
.frame(width: 225, height: 225)
.frame(width: isKeyboardVisible ? 125 : 225,
height: isKeyboardVisible ? 125 : 225)
.contentShape(Rectangle())
.animation(.spring(response: 0.4, dampingFraction: 1), value: isKeyboardVisible)
}
if isUploading {

View File

@ -12,107 +12,198 @@ struct UserInfo: View {
@State private var avatarImage: UIImage?
@State private var showUsername: Bool = false
//
@State private var isKeyboardVisible = false
var body: some View {
VStack(spacing: 0) {
HStack {
ReturnButton {
print("返回")
}
Spacer()
Text("Complete Your Profile")
.font(Typography.font(for: .title2, family: .quicksandBold))
.foregroundColor(.themeTextMessageMain)
Spacer()
}
.padding()
HStack(spacing: 20) {
Text("Choose a photo as your avatar, and we'll generate a video mystery box for you.")
.font(Typography.font(for: .caption))
.foregroundColor(.black)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.vertical, 10)
.background(
LinearGradient(
gradient: Gradient(colors: [
Color(red: 1.0, green: 0.97, blue: 0.87),
.white,
Color(red: 1.0, green: 0.97, blue: 0.84)
]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
}
.padding(10)
Spacer()
VStack(spacing: 20) {
// Title
Text(showUsername ? "Add Your Avatar" : "Whats Your Name")
.font(Typography.font(for: .body, family: .quicksandBold))
.frame(maxWidth: .infinity, alignment: .center)
// Avatar
ZStack {
AvatarPicker(
selectedImage: $avatarImage,
showUsername: $showUsername
)
}
.padding(.top, 20)
if !showUsername {
ZStack {
//
Color.themeTextWhiteSecondary
.edgesIgnoringSafeArea(.all)
VStack(spacing: 0) {
//
HStack {
Button(action: {
// Action for second button
dismiss()
}) {
Text("Take a Photo")
.font(Typography.font(for: .subtitle, family: .inter))
.fontWeight(.regular)
.frame(maxWidth: .infinity)
.padding()
.foregroundColor(.black)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color.themePrimaryLight)
)
Image(systemName: "chevron.left")
.font(.system(size: 17, weight: .semibold))
.foregroundColor(.themeTextMessageMain)
}
.padding(.leading, 16)
Spacer()
Text("Complete Your Profile")
.font(Typography.font(for: .title2, family: .quicksandBold))
.foregroundColor(.themeTextMessageMain)
Spacer()
//
Color.clear
.frame(width: 24, height: 24)
.padding(.trailing, 16)
}
.padding(.vertical, 12)
.background(Color.themeTextWhiteSecondary)
.zIndex(1) //
//
ScrollView {
VStack(spacing: 0) {
//
if isKeyboardVisible {
HStack(spacing: 20) {
Text("Choose a photo as your avatar, and we'll generate a video mystery box for you.")
.font(Typography.font(for: .caption))
.foregroundColor(.black)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
.padding(.vertical, 10)
.background(
LinearGradient(
gradient: Gradient(colors: [
Color(red: 1.0, green: 0.97, blue: 0.87),
.white,
Color(red: 1.0, green: 0.97, blue: 0.84)
]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
}
.padding(10)
.transition(AnyTransition.opacity.combined(with: .move(edge: .top)))
.animation(.easeInOut(duration: 0.4), value: isKeyboardVisible)
}
//
else {
HStack(spacing: 20) {
Text("Choose a photo as your avatar, and we'll generate a video mystery box for you.")
.font(Typography.font(for: .caption))
.foregroundColor(.black)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(2)
.padding(.vertical, 10)
.background(
LinearGradient(
gradient: Gradient(colors: [
Color(red: 1.0, green: 0.97, blue: 0.87),
.white,
Color(red: 1.0, green: 0.97, blue: 0.84)
]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
}
.padding(10)
.transition(AnyTransition.opacity.combined(with: .move(edge: .top)))
.animation(.easeInOut(duration: 0.25), value: isKeyboardVisible)
}
if !isKeyboardVisible { Spacer() }
VStack(spacing: 20) {
// Title
Text(showUsername ? "Add Your Avatar" : "Whats Your Name")
.font(Typography.font(for: .body, family: .quicksandBold))
.frame(maxWidth: .infinity, alignment: .center)
// Avatar
ZStack {
AvatarPicker(
selectedImage: $avatarImage,
showUsername: $showUsername,
isKeyboardVisible: $isKeyboardVisible
)
}
.padding(.top, isKeyboardVisible ? 0 : 20)
if !showUsername {
Button(action: {
// Action for second button
}) {
Text("Take a Photo")
.font(Typography.font(for: .subtitle, family: .inter))
.fontWeight(.regular)
.frame(maxWidth: .infinity)
.padding()
.foregroundColor(.black)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color.themePrimaryLight)
)
}
}
if showUsername {
TextField("Username", text: $userName)
.font(Typography.font(for: .subtitle, family: .inter))
.multilineTextAlignment(.center)
.frame(maxWidth: .infinity)
.padding()
.foregroundColor(.black)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color.themePrimaryLight)
)
}
}
.padding()
.background(Color(.white))
.cornerRadius(20)
.padding(.horizontal)
if !isKeyboardVisible { Spacer() }
Button(action: {
showUsername = true
}) {
Text("Continue")
.font(Typography.font(for: .body))
.fontWeight(.bold)
.frame(maxWidth: .infinity)
.padding(16)
.foregroundColor(.black)
.background(
RoundedRectangle(cornerRadius: 25)
.fill(Color.themePrimary)
)
}
.padding()
.padding(.bottom, isKeyboardVisible ? 20 : 40)
}
}
if showUsername {
TextField("Username", text: $userName)
.font(Typography.font(for: .subtitle, family: .inter))
.multilineTextAlignment(.center)
.frame(maxWidth: .infinity)
.padding()
.foregroundColor(.black)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color.themePrimaryLight)
)
}
.background(Color.themeTextWhiteSecondary)
}
.padding()
.background(Color(.white))
.cornerRadius(20)
Spacer()
Button(action: {
showUsername = true
}) {
Text("Continue")
.font(Typography.font(for: .body))
.fontWeight(.bold)
.frame(maxWidth: .infinity)
.padding(16)
.foregroundColor(.black)
.background(
RoundedRectangle(cornerRadius: 25)
.fill(Color.themePrimary)
)
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
//
if isKeyboardVisible {
Color.black.opacity(0.001)
.edgesIgnoringSafeArea(.all)
.onTapGesture {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
.padding()
}
.padding()
.navigationBarTitleDisplayMode(.inline)
.background(Color(red: 0.98, green: 0.98, blue: 0.98)) // #FAFAFA
.onAppear {
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { _ in
isKeyboardVisible = true
}
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { _ in
isKeyboardVisible = false
}
}
.onDisappear {
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
}
}