feat: 新增用户ID显示组件和头像样式优化
- 创建UserIDDisplay组件,支持ID显示和复制功能,增强用户交互体验。 - 更新MeView中的头像样式,调整尺寸和边框,提升视觉效果。 - 修改OptimizedDynamicCardView以使用新组件,确保一致性和复用性。 - 新增icon_copy图标资源,支持复制功能的视觉反馈。 - 更新AppSettingView中的布局,优化用户界面体验。
This commit is contained in:
54
issues/MeView头像和ID显示优化.md
Normal file
54
issues/MeView头像和ID显示优化.md
Normal 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. 组件在不同场景下复用正常
|
6
yana/Assets.xcassets/Common/Contents.json
Normal file
6
yana/Assets.xcassets/Common/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
21
yana/Assets.xcassets/Common/icon_copy.imageset/Contents.json
vendored
Normal file
21
yana/Assets.xcassets/Common/icon_copy.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
BIN
yana/Assets.xcassets/Common/icon_copy.imageset/复制@3x.png
vendored
Normal file
BIN
yana/Assets.xcassets/Common/icon_copy.imageset/复制@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 646 B |
@@ -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))
|
||||
|
@@ -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) // 图片网格需要响应点击事件
|
||||
}
|
||||
|
||||
|
68
yana/Views/Components/UserIDDisplay.swift
Normal file
68
yana/Views/Components/UserIDDisplay.swift
Normal 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()
|
||||
}
|
@@ -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)
|
||||
}
|
||||
|
Reference in New Issue
Block a user