feat: 新增用户ID显示组件和头像样式优化

- 创建UserIDDisplay组件,支持ID显示和复制功能,增强用户交互体验。
- 更新MeView中的头像样式,调整尺寸和边框,提升视觉效果。
- 修改OptimizedDynamicCardView以使用新组件,确保一致性和复用性。
- 新增icon_copy图标资源,支持复制功能的视觉反馈。
- 更新AppSettingView中的布局,优化用户界面体验。
This commit is contained in:
edwinQQQ
2025-08-01 15:53:56 +08:00
parent 12dd03d5b3
commit 57ba103996
8 changed files with 165 additions and 36 deletions

View File

@@ -0,0 +1,54 @@
# MeView头像和ID显示优化
## 需求分析
1. 头像尺寸从80x80改为130x130
2. 头像外层添加白色边框2px
3. "ID: xxxx"中的数字不使用逗号分割
4. 在ID右侧添加"icon_icon"图片14x14
5. 点击整体复制ID数字
6. 抽象为独立组件,便于项目内复用
## 实施计划
### 文件结构
- ✅ 创建:`yana/Views/Components/UserIDDisplay.swift`
- ✅ 修改:`yana/Views/MeView.swift`
- ✅ 修改:`yana/Views/Components/OptimizedDynamicCardView.swift`
### 核心组件设计
1. **UserIDDisplay组件**
- 参数uid (Int), fontSize (CGFloat), textColor (Color)
- 功能:显示"ID: xxx"右侧复制图标点击复制ID
- 样式:数字不使用逗号分割
- 反馈:点击后显示"已复制"提示
2. **头像样式调整**
- 尺寸130x130
- 边框白色2px
### 实施步骤
1. ✅ 创建UserIDDisplay组件
2. ✅ 修改MeView中的头像和ID显示
3. ✅ 更新OptimizedDynamicCardView使用新组件
### 技术要点
- 使用UIPasteboard进行复制功能
- 使用现有的icon_copy图片资源
- 添加复制成功反馈动画
- 保持与现有代码风格一致
## 完成状态
- [x] UserIDDisplay组件创建
- [x] MeView头像样式更新
- [x] MeView ID显示组件化
- [x] OptimizedDynamicCardView组件更新
- [x] 复制功能实现
- [x] 视觉反馈实现
## 测试要点
1. 头像尺寸和边框显示正确
2. ID显示格式正确无逗号分割
3. 复制图标显示正确
4. 点击复制功能正常
5. 复制成功反馈显示
6. 组件在不同场景下复用正常

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "复制@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 B

View File

@@ -57,12 +57,8 @@ struct AppSettingView: View {
WithPerceptionTracking {
let baseView = GeometryReader { geometry in
ZStack {
//
Image("bg")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.clipped()
//
Color(hex: 0x0C0527)
.ignoresSafeArea(.all)
VStack(spacing: 0) {
@@ -248,7 +244,7 @@ struct AppSettingView: View {
.aspectRatio(contentMode: .fill)
.foregroundColor(.gray)
}
.frame(width: 100, height: 100)
.frame(width: 120, height: 120)
.clipShape(Circle())
//
@@ -266,7 +262,7 @@ struct AppSettingView: View {
)
}
}
.frame(width: 100, height: 100)
.frame(width: 120, height: 120)
}
}
}
@@ -280,7 +276,6 @@ struct AppSettingView: View {
VStack(spacing: 0) {
//
SettingRow(
icon: "person",
title: LocalizedString("appSetting.nickname", comment: "昵称"),
subtitle: store.userInfo?.nick ?? LocalizedString("app_settings.not_set", comment: "未设置"),
action: {
@@ -297,7 +292,6 @@ struct AppSettingView: View {
WithPerceptionTracking {
VStack(spacing: 0) {
SettingRow(
icon: "hand.raised",
title: LocalizedString("appSetting.personalInfoPermissions", comment: "个人信息与权限"),
subtitle: "",
action: { store.send(.personalInfoPermissionsTapped) }
@@ -305,10 +299,9 @@ struct AppSettingView: View {
Divider()
.background(Color.white.opacity(0.2))
.padding(.leading, 50)
.padding(.leading, 16)
SettingRow(
icon: "questionmark.circle",
title: LocalizedString("appSetting.help", comment: "帮助"),
subtitle: "",
action: { store.send(.helpTapped) }
@@ -316,10 +309,9 @@ struct AppSettingView: View {
Divider()
.background(Color.white.opacity(0.2))
.padding(.leading, 50)
.padding(.leading, 16)
SettingRow(
icon: "trash",
title: LocalizedString("appSetting.clearCache", comment: "清除缓存"),
subtitle: "",
action: { store.send(.clearCacheTapped) }
@@ -327,10 +319,9 @@ struct AppSettingView: View {
Divider()
.background(Color.white.opacity(0.2))
.padding(.leading, 50)
.padding(.leading, 16)
SettingRow(
icon: "arrow.clockwise",
title: LocalizedString("appSetting.checkUpdates", comment: "检查更新"),
subtitle: "",
action: { store.send(.checkUpdatesTapped) }
@@ -338,10 +329,9 @@ struct AppSettingView: View {
Divider()
.background(Color.white.opacity(0.2))
.padding(.leading, 50)
.padding(.leading, 16)
SettingRow(
icon: "person.crop.circle.badge.minus",
title: LocalizedString("appSetting.deactivateAccount", comment: "注销账号"),
subtitle: "",
action: { store.send(.deactivateAccountTapped) }
@@ -349,10 +339,9 @@ struct AppSettingView: View {
Divider()
.background(Color.white.opacity(0.2))
.padding(.leading, 50)
.padding(.leading, 16)
SettingRow(
icon: "info.circle",
title: LocalizedString("appSetting.aboutUs", comment: "关于我们"),
subtitle: "",
action: { store.send(.aboutUsTapped) }
@@ -385,7 +374,6 @@ struct AppSettingView: View {
// MARK: -
struct SettingRow: View {
let icon: String
let title: String
let subtitle: String
let action: (() -> Void)?
@@ -395,11 +383,6 @@ struct SettingRow: View {
action?()
}) {
HStack(spacing: 16) {
Image(systemName: icon)
.font(.system(size: 18))
.foregroundColor(.white)
.frame(width: 24)
HStack {
Text(title)
.font(.system(size: 16))

View File

@@ -43,7 +43,7 @@ struct OptimizedDynamicCardView: View {
}
//
VStack(alignment: .leading, spacing: 12) {
VStack(alignment: .leading, spacing: 10) {
//
HStack(alignment: .top) {
//
@@ -69,9 +69,7 @@ struct OptimizedDynamicCardView: View {
.font(.system(size: 16, weight: .medium))
.foregroundColor(.white)
.allowsHitTesting(false) //
Text("ID: \(moment.uid)")
.font(.system(size: 12))
.foregroundColor(.white.opacity(0.6))
UserIDDisplay(uid: moment.uid, fontSize: 12, textColor: .white.opacity(0.6))
.allowsHitTesting(false) //
}
Spacer()
@@ -102,7 +100,8 @@ struct OptimizedDynamicCardView: View {
let urls = images.map { $0.resUrl ?? "" }
onImageTap(urls, tappedIndex)
}
.padding(.bottom, images.count == 2 ? 46 : 0) //
.padding(.leading, 40 + 8)
.padding(.bottom, images.count == 2 ? 30 : 0) //
.allowsHitTesting(true) //
}

View File

@@ -0,0 +1,68 @@
import SwiftUI
struct UserIDDisplay: View {
let uid: Int
let fontSize: CGFloat
let textColor: Color
@State private var showCopiedFeedback: Bool = false
init(uid: Int, fontSize: CGFloat = 14, textColor: Color = .white.opacity(0.7)) {
self.uid = uid
self.fontSize = fontSize
self.textColor = textColor
}
var body: some View {
HStack(spacing: 4) {
Text("ID: \(String(uid))")
.font(.system(size: fontSize))
.foregroundColor(textColor)
Image("icon_copy")
.resizable()
.frame(width: 14, height: 14)
.foregroundColor(textColor)
}
.onTapGesture {
copyToClipboard()
}
.overlay(
Group {
if showCopiedFeedback {
Text("已复制")
.font(.system(size: 12))
.foregroundColor(.white)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(Color.black.opacity(0.7))
.cornerRadius(4)
.offset(y: -30)
.transition(.opacity)
}
}
)
}
private func copyToClipboard() {
UIPasteboard.general.string = String(uid)
withAnimation(.easeInOut(duration: 0.2)) {
showCopiedFeedback = true
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
withAnimation(.easeInOut(duration: 0.2)) {
showCopiedFeedback = false
}
}
}
}
#Preview {
VStack(spacing: 20) {
UserIDDisplay(uid: 123456789)
UserIDDisplay(uid: 987654321, fontSize: 16, textColor: .black)
}
.padding()
}

View File

@@ -98,11 +98,11 @@ struct MeView: View {
.aspectRatio(contentMode: .fill)
.foregroundColor(.gray)
}
.frame(width: 80, height: 80)
.frame(width: 130, height: 130)
.clipShape(Circle())
.overlay(
Circle()
.stroke(Color.white.opacity(0.3), lineWidth: 2)
.stroke(Color.white, lineWidth: 2)
)
//
@@ -111,9 +111,7 @@ struct MeView: View {
.foregroundColor(.white)
// ID
Text("ID: \(store.userInfo?.uid ?? 0)")
.font(.system(size: 14))
.foregroundColor(.white.opacity(0.7))
UserIDDisplay(uid: store.userInfo?.uid ?? 0)
}
.padding(.horizontal, 32)
}