fix: 统一应用名称为 "E-Party" 并更新相关描述
主要变更: 1. 在 Info.plist 中将应用名称和描述中的 "E-Parti" 替换为 "E-Party"。 2. 更新多个本地化字符串和提示信息,确保一致性。 3. 修改部分代码中的错误提示信息,使用本地化字符串替代硬编码文本。 此更新旨在提升品牌一致性,确保用户在使用过程中获得统一的体验。
This commit is contained in:
@@ -58,7 +58,7 @@ class EPImageUploader {
|
||||
lock.unlock()
|
||||
semaphore.signal()
|
||||
DispatchQueue.main.async {
|
||||
failure("图片压缩失败")
|
||||
failure(YMLocalizedString("error.image_compress_failed"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@@ -35,13 +35,13 @@ import Foundation
|
||||
|
||||
if let hud = currentHUD {
|
||||
// 更新现有 HUD
|
||||
hud.label.text = "上传中 \(uploaded)/\(total)"
|
||||
hud.label.text = String(format: YMLocalizedString("upload.progress_format"), uploaded, total)
|
||||
hud.progress = Float(uploaded) / Float(total)
|
||||
} else {
|
||||
// 创建新 HUD
|
||||
let hud = MBProgressHUD.showAdded(to: window, animated: true)
|
||||
hud.mode = .determinateHorizontalBar
|
||||
hud.label.text = "上传中 \(uploaded)/\(total)"
|
||||
hud.label.text = String(format: YMLocalizedString("upload.progress_format"), uploaded, total)
|
||||
hud.progress = Float(uploaded) / Float(total)
|
||||
hud.removeFromSuperViewOnHide = true
|
||||
currentHUD = hud
|
||||
@@ -49,6 +49,36 @@ import Foundation
|
||||
}
|
||||
}
|
||||
|
||||
/// 显示错误提示
|
||||
/// - Parameter message: 错误信息
|
||||
@objc static func showError(_ message: String) {
|
||||
DispatchQueue.main.async {
|
||||
guard let window = keyWindow else { return }
|
||||
|
||||
let hud = MBProgressHUD.showAdded(to: window, animated: true)
|
||||
hud.mode = .text
|
||||
hud.label.text = message
|
||||
hud.label.numberOfLines = 0
|
||||
hud.removeFromSuperViewOnHide = true
|
||||
hud.hide(animated: true, afterDelay: 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// 显示成功提示
|
||||
/// - Parameter message: 成功信息
|
||||
@objc static func showSuccess(_ message: String) {
|
||||
DispatchQueue.main.async {
|
||||
guard let window = keyWindow else { return }
|
||||
|
||||
let hud = MBProgressHUD.showAdded(to: window, animated: true)
|
||||
hud.mode = .text
|
||||
hud.label.text = message
|
||||
hud.label.numberOfLines = 0
|
||||
hud.removeFromSuperViewOnHide = true
|
||||
hud.hide(animated: true, afterDelay: 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// 关闭 HUD
|
||||
@objc static func dismiss() {
|
||||
DispatchQueue.main.async {
|
||||
|
@@ -65,7 +65,7 @@ import Foundation
|
||||
ensureQCloudReady { [weak self] isReady, errorMsg in
|
||||
guard let self = self, isReady else {
|
||||
DispatchQueue.main.async {
|
||||
failure(errorMsg ?? "QCloud 初始化失败")
|
||||
failure(errorMsg ?? YMLocalizedString("error.qcloud_init_failed"))
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -164,7 +164,7 @@ import Foundation
|
||||
self.qcloudInitCallbacks.removeAll()
|
||||
self.lock.unlock()
|
||||
|
||||
let errorMsg = msg ?? "获取 QCloud 配置失败"
|
||||
let errorMsg = msg ?? YMLocalizedString("error.qcloud_config_failed")
|
||||
DispatchQueue.main.async {
|
||||
callbacks.forEach { $0(false, errorMsg) }
|
||||
}
|
||||
@@ -209,7 +209,7 @@ import Foundation
|
||||
) {
|
||||
guard let config = qcloudConfig else {
|
||||
let error = NSError(domain: "com.yumi.qcloud", code: -1,
|
||||
userInfo: [NSLocalizedDescriptionKey: "QCloud 配置未初始化"])
|
||||
userInfo: [NSLocalizedDescriptionKey: YMLocalizedString("error.qcloud_config_not_initialized")])
|
||||
compelete(nil, error)
|
||||
return
|
||||
}
|
||||
@@ -235,7 +235,7 @@ import Foundation
|
||||
) {
|
||||
guard let config = qcloudConfig else {
|
||||
let error = NSError(domain: "com.yumi.qcloud", code: -1,
|
||||
userInfo: [NSLocalizedDescriptionKey: "QCloud 配置未初始化"])
|
||||
userInfo: [NSLocalizedDescriptionKey: YMLocalizedString("error.qcloud_config_not_initialized")])
|
||||
continueBlock(nil, error)
|
||||
return
|
||||
}
|
||||
|
@@ -33,6 +33,14 @@ import UIKit
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// 验证 DEBUG 编译条件
|
||||
#if DEBUG
|
||||
print("✅ [EPLogin] DEBUG 模式已激活")
|
||||
#else
|
||||
print("⚠️ [EPLogin] 当前为 Release 模式")
|
||||
#endif
|
||||
|
||||
navigationController?.setNavigationBarHidden(true, animated: false)
|
||||
setupUI()
|
||||
loadPolicyStatus()
|
||||
@@ -76,9 +84,9 @@ import UIKit
|
||||
make.height.equalTo(EPLoginConfig.Layout.logoHeight)
|
||||
}
|
||||
|
||||
// E-PARTI 标题
|
||||
// E-PARTY 标题
|
||||
view.addSubview(epartiTitleLabel)
|
||||
epartiTitleLabel.text = "E-PARTI"
|
||||
epartiTitleLabel.text = "E-PARTY"
|
||||
epartiTitleLabel.font = .systemFont(ofSize: EPLoginConfig.Layout.epartiTitleFontSize, weight: .bold)
|
||||
epartiTitleLabel.textColor = EPLoginConfig.Colors.textLight
|
||||
epartiTitleLabel.transform = CGAffineTransform(a: 1, b: 0, c: -0.2, d: 1, tx: 0, ty: 0) // 斜体效果
|
||||
@@ -160,6 +168,7 @@ import UIKit
|
||||
}
|
||||
|
||||
private func setupNavigationBar() {
|
||||
#if DEBUG
|
||||
view.addSubview(feedbackButton)
|
||||
feedbackButton.setTitle(YMLocalizedString(EPLoginConfig.LocalizedKeys.feedback), for: .normal)
|
||||
feedbackButton.titleLabel?.font = .systemFont(ofSize: EPLoginConfig.Layout.smallFontSize)
|
||||
@@ -173,7 +182,6 @@ import UIKit
|
||||
make.height.equalTo(EPLoginConfig.Layout.feedbackButtonHeight)
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
view.addSubview(debugButton)
|
||||
debugButton.setTitle("切换环境", for: .normal)
|
||||
debugButton.setTitleColor(.blue, for: .normal)
|
||||
@@ -183,7 +191,7 @@ import UIKit
|
||||
make.leading.equalToSuperview().offset(EPLoginConfig.Layout.compactHorizontalPadding)
|
||||
make.top.equalTo(view.safeAreaLayoutGuide).offset(8)
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
@@ -39,11 +39,11 @@ struct EPLoginConfig {
|
||||
/// Logo 距离顶部的距离
|
||||
static let logoTopOffset: CGFloat = 80
|
||||
|
||||
/// E-PARTI 标题字号
|
||||
/// E-PARTY 标题字号
|
||||
static let epartiTitleFontSize: CGFloat = 56
|
||||
/// E-PARTI 标题距离 view leading
|
||||
/// E-PARTY 标题距离 view leading
|
||||
static let epartiTitleLeading: CGFloat = 40
|
||||
/// E-PARTI 标题距离 logoImage bottom 的偏移(负值表示向上)
|
||||
/// E-PARTY 标题距离 logoImage bottom 的偏移(负值表示向上)
|
||||
static let epartiTitleBottomOffset: CGFloat = -30
|
||||
|
||||
/// 输入框之间的垂直间距
|
||||
|
@@ -36,10 +36,10 @@ import Foundation
|
||||
AccountInfoStorage.instance().saveAccountInfo(accountModel)
|
||||
completion(accountModel)
|
||||
} else {
|
||||
failure(Int(code), "账号信息解析失败")
|
||||
failure(Int(code), YMLocalizedString("error.account_parse_failed"))
|
||||
}
|
||||
} else {
|
||||
failure(Int(code), "操作失败")
|
||||
failure(Int(code), YMLocalizedString("error.operation_failed"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,10 +61,10 @@ import Foundation
|
||||
let ticket = firstTicket["ticket"] as? String {
|
||||
completion(ticket)
|
||||
} else {
|
||||
failure(Int(code), "Ticket 解析失败")
|
||||
failure(Int(code), YMLocalizedString("error.ticket_parse_failed"))
|
||||
}
|
||||
} else {
|
||||
failure(Int(code), msg ?? "请求 Ticket 失败")
|
||||
failure(Int(code), msg ?? YMLocalizedString("error.request_ticket_failed"))
|
||||
}
|
||||
}, access_token: accessToken, issue_type: "multi")
|
||||
}
|
||||
@@ -89,7 +89,7 @@ import Foundation
|
||||
if code == 200 {
|
||||
completion()
|
||||
} else {
|
||||
failure(Int(code), msg ?? "发送邮箱验证码失败")
|
||||
failure(Int(code), msg ?? YMLocalizedString("error.send_email_code_failed"))
|
||||
}
|
||||
}, emailAddress: encryptedEmail, type: NSNumber(value: type))
|
||||
}
|
||||
@@ -114,7 +114,7 @@ import Foundation
|
||||
if code == 200 {
|
||||
completion()
|
||||
} else {
|
||||
failure(Int(code), msg ?? "发送手机验证码失败")
|
||||
failure(Int(code), msg ?? YMLocalizedString("error.send_phone_code_failed"))
|
||||
}
|
||||
}, mobile: encryptedPhone, type: String(type), phoneAreaCode: areaCode)
|
||||
}
|
||||
@@ -142,7 +142,7 @@ import Foundation
|
||||
code: Int64(code),
|
||||
completion: completion,
|
||||
failure: { errorCode, _ in
|
||||
failure(errorCode, msg ?? "登录失败")
|
||||
failure(errorCode, msg ?? YMLocalizedString("error.login_failed"))
|
||||
})
|
||||
},
|
||||
phone: encryptedId,
|
||||
@@ -173,7 +173,7 @@ import Foundation
|
||||
code: Int64(code),
|
||||
completion: completion,
|
||||
failure: { errorCode, _ in
|
||||
failure(errorCode, msg ?? "登录失败")
|
||||
failure(errorCode, msg ?? YMLocalizedString("error.login_failed"))
|
||||
})
|
||||
},
|
||||
email: encryptedEmail,
|
||||
@@ -206,7 +206,7 @@ import Foundation
|
||||
code: Int64(code),
|
||||
completion: completion,
|
||||
failure: { errorCode, _ in
|
||||
failure(errorCode, msg ?? "登录失败")
|
||||
failure(errorCode, msg ?? YMLocalizedString("error.login_failed"))
|
||||
})
|
||||
},
|
||||
phone: encryptedPhone,
|
||||
@@ -241,7 +241,7 @@ import Foundation
|
||||
if code == 200 {
|
||||
completion()
|
||||
} else {
|
||||
failure(Int(code), msg ?? "重置密码失败")
|
||||
failure(Int(code), msg ?? YMLocalizedString("error.reset_password_failed"))
|
||||
}
|
||||
}, email: encryptedEmail, newPwd: encryptedPassword, code: code)
|
||||
}
|
||||
@@ -269,7 +269,7 @@ import Foundation
|
||||
if code == 200 {
|
||||
completion()
|
||||
} else {
|
||||
failure(Int(code), msg ?? "重置密码失败")
|
||||
failure(Int(code), msg ?? YMLocalizedString("error.reset_password_failed"))
|
||||
}
|
||||
}, phone: encryptedPhone, newPwd: encryptedPassword, smsCode: code, phoneAreaCode: areaCode)
|
||||
}
|
||||
@@ -293,7 +293,7 @@ import Foundation
|
||||
code: Int64(code),
|
||||
completion: completion,
|
||||
failure: { errorCode, _ in
|
||||
failure(errorCode, msg ?? "快速登录失败")
|
||||
failure(errorCode, msg ?? YMLocalizedString("error.quick_login_failed"))
|
||||
})
|
||||
},
|
||||
accessToken: accessToken,
|
||||
|
@@ -89,13 +89,35 @@ class EPPolicyLabel: UILabel {
|
||||
|
||||
textContainer.lineFragmentPadding = 0
|
||||
textContainer.maximumNumberOfLines = numberOfLines
|
||||
textContainer.lineBreakMode = lineBreakMode
|
||||
|
||||
let locationOfTouchInLabel = gesture.location(in: self)
|
||||
let textBoundingBox = layoutManager.usedRect(for: textContainer)
|
||||
let textContainerOffset = CGPoint(x: (bounds.width - textBoundingBox.width) / 2,
|
||||
y: (bounds.height - textBoundingBox.height) / 2)
|
||||
|
||||
// 根据 textAlignment 计算偏移
|
||||
var textContainerOffset = CGPoint.zero
|
||||
switch textAlignment {
|
||||
case .left, .natural, .justified:
|
||||
textContainerOffset = CGPoint(x: 0, y: (bounds.height - textBoundingBox.height) / 2)
|
||||
case .center:
|
||||
textContainerOffset = CGPoint(x: (bounds.width - textBoundingBox.width) / 2,
|
||||
y: (bounds.height - textBoundingBox.height) / 2)
|
||||
case .right:
|
||||
textContainerOffset = CGPoint(x: bounds.width - textBoundingBox.width,
|
||||
y: (bounds.height - textBoundingBox.height) / 2)
|
||||
@unknown default:
|
||||
textContainerOffset = CGPoint(x: 0, y: (bounds.height - textBoundingBox.height) / 2)
|
||||
}
|
||||
|
||||
let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x,
|
||||
y: locationOfTouchInLabel.y - textContainerOffset.y)
|
||||
|
||||
// 确保点击在文本区域内
|
||||
guard textBoundingBox.contains(locationOfTouchInTextContainer) else {
|
||||
print("[EPPolicyLabel] Tap outside text bounds")
|
||||
return
|
||||
}
|
||||
|
||||
let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer,
|
||||
in: textContainer,
|
||||
fractionOfDistanceBetweenInsertionPoints: nil)
|
||||
|
@@ -338,7 +338,7 @@ class EPEditSettingViewController: BaseViewController {
|
||||
self?.hideHUD()
|
||||
|
||||
// 显示错误提示
|
||||
let errorMsg = msg ?? "昵称更新失败,请稍后重试"
|
||||
let errorMsg = msg ?? YMLocalizedString("setting.nickname_update_failed")
|
||||
self?.showErrorToast(errorMsg)
|
||||
|
||||
print("[EPEditSetting] 昵称更新失败: \(code) - \(errorMsg)")
|
||||
@@ -453,7 +453,7 @@ extension EPEditSettingViewController: UITableViewDataSource, UITableViewDelegat
|
||||
|
||||
// 添加用户昵称标签
|
||||
let nicknameLabel = UILabel()
|
||||
nicknameLabel.text = userInfo?.nick ?? "未设置"
|
||||
nicknameLabel.text = userInfo?.nick ?? YMLocalizedString("user.not_set")
|
||||
nicknameLabel.textColor = .lightGray
|
||||
nicknameLabel.font = UIFont.systemFont(ofSize: 16)
|
||||
cell.contentView.addSubview(nicknameLabel)
|
||||
@@ -576,8 +576,8 @@ extension EPEditSettingViewController: UIImagePickerControllerDelegate, UINaviga
|
||||
|
||||
// 显示错误提示
|
||||
DispatchQueue.main.async {
|
||||
let alert = UIAlertController(title: "上传失败", message: errorMsg, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "确定", style: .default))
|
||||
let alert = UIAlertController(title: YMLocalizedString("common.upload_failed"), message: errorMsg, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: YMLocalizedString("common.confirm"), style: .default))
|
||||
self?.present(alert, animated: true)
|
||||
}
|
||||
}
|
||||
@@ -599,13 +599,13 @@ extension EPEditSettingViewController: UIImagePickerControllerDelegate, UINaviga
|
||||
print("[EPEditSetting] 头像更新失败: \(code) - \(msg ?? "未知错误")")
|
||||
|
||||
// 显示错误提示
|
||||
DispatchQueue.main.async {
|
||||
let alert = UIAlertController(
|
||||
title: "更新失败",
|
||||
message: msg ?? "头像更新失败,请稍后重试",
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alert.addAction(UIAlertAction(title: "确定", style: .default))
|
||||
DispatchQueue.main.async {
|
||||
let alert = UIAlertController(
|
||||
title: YMLocalizedString("common.update_failed"),
|
||||
message: msg ?? YMLocalizedString("setting.avatar_update_failed"),
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alert.addAction(UIAlertAction(title: YMLocalizedString("common.confirm"), style: .default))
|
||||
self?.present(alert, animated: true)
|
||||
}
|
||||
})
|
||||
|
@@ -16,6 +16,9 @@
|
||||
/// 头像视图
|
||||
@property (nonatomic, strong) UIImageView *avatarImageView;
|
||||
|
||||
/// 呼吸光晕层
|
||||
@property (nonatomic, strong) CALayer *glowLayer;
|
||||
|
||||
/// 昵称标签
|
||||
@property (nonatomic, strong) UILabel *nicknameLabel;
|
||||
|
||||
@@ -36,13 +39,21 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
// 更新光晕层 frame(跟随头像位置)
|
||||
if (self.glowLayer) {
|
||||
self.glowLayer.frame = CGRectInset(self.avatarImageView.frame, -8, -8);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupUI {
|
||||
// 大圆形头像
|
||||
self.avatarImageView = [[UIImageView alloc] init];
|
||||
self.avatarImageView.layer.cornerRadius = 60;
|
||||
self.avatarImageView.layer.masksToBounds = NO; // 改为 NO 以显示阴影
|
||||
self.avatarImageView.layer.borderWidth = 3;
|
||||
self.avatarImageView.layer.borderColor = [UIColor whiteColor].CGColor;
|
||||
self.avatarImageView.layer.borderWidth = 0; // 移除边框
|
||||
self.avatarImageView.backgroundColor = [UIColor whiteColor];
|
||||
self.avatarImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
|
||||
@@ -100,7 +111,7 @@
|
||||
|
||||
- (void)updateWithUserInfo:(NSDictionary *)userInfoDict {
|
||||
// 更新昵称
|
||||
NSString *nickname = userInfoDict[@"nickname"] ?: @"未设置昵称";
|
||||
NSString *nickname = userInfoDict[@"nickname"] ?: YMLocalizedString(@"user.nickname_not_set");
|
||||
self.nicknameLabel.text = nickname;
|
||||
|
||||
// 更新 ID
|
||||
@@ -129,9 +140,8 @@
|
||||
// 有专属颜色,使用该颜色
|
||||
UIColor *color = [self colorFromHex:signatureColor];
|
||||
|
||||
// 设置边框颜色
|
||||
self.avatarImageView.layer.borderColor = color.CGColor;
|
||||
self.avatarImageView.layer.borderWidth = 4; // 稍微加粗边框
|
||||
// 取消边框
|
||||
self.avatarImageView.layer.borderWidth = 0;
|
||||
|
||||
// 设置阴影(使用情绪颜色)
|
||||
self.avatarImageView.layer.shadowColor = color.CGColor;
|
||||
@@ -140,19 +150,75 @@
|
||||
self.avatarImageView.layer.shadowRadius = 12;
|
||||
|
||||
NSLog(@"[EPMineHeaderView] 应用专属颜色: %@", signatureColor);
|
||||
|
||||
// 应用呼吸光晕动效 ⭐
|
||||
[self applyBreathingGlow];
|
||||
} else {
|
||||
// 没有专属颜色,使用默认白色边框
|
||||
self.avatarImageView.layer.borderColor = [UIColor whiteColor].CGColor;
|
||||
self.avatarImageView.layer.borderWidth = 3;
|
||||
// 没有专属颜色,保持无边框
|
||||
self.avatarImageView.layer.borderWidth = 0;
|
||||
|
||||
// 默认轻微阴影
|
||||
self.avatarImageView.layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
self.avatarImageView.layer.shadowOffset = CGSizeMake(0, 2);
|
||||
self.avatarImageView.layer.shadowOpacity = 0.2;
|
||||
self.avatarImageView.layer.shadowRadius = 8;
|
||||
|
||||
// 移除光晕层
|
||||
if (self.glowLayer) {
|
||||
[self.glowLayer removeFromSuperlayer];
|
||||
self.glowLayer = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 应用呼吸光晕动效
|
||||
- (void)applyBreathingGlow {
|
||||
NSString *signatureColor = [EPEmotionColorStorage userSignatureColor];
|
||||
if (!signatureColor) return;
|
||||
|
||||
UIColor *color = [self colorFromHex:signatureColor];
|
||||
|
||||
// 创建光晕层(如果不存在)
|
||||
if (!self.glowLayer) {
|
||||
self.glowLayer = [CALayer layer];
|
||||
self.glowLayer.frame = CGRectInset(self.avatarImageView.frame, -8, -8); // 比头像大 16pt
|
||||
self.glowLayer.cornerRadius = 68; // 头像 60 + 扩展 8
|
||||
self.glowLayer.backgroundColor = [color colorWithAlphaComponent:0.75].CGColor; // 大幅加深
|
||||
|
||||
// 插入到头像 layer 下方
|
||||
[self.layer insertSublayer:self.glowLayer below:self.avatarImageView.layer];
|
||||
} else {
|
||||
// 更新颜色
|
||||
self.glowLayer.backgroundColor = [color colorWithAlphaComponent:0.75].CGColor; // 大幅加深
|
||||
}
|
||||
|
||||
// 移除旧动画
|
||||
[self.glowLayer removeAllAnimations];
|
||||
|
||||
// 创建呼吸动画组
|
||||
CAAnimationGroup *breathingGroup = [CAAnimationGroup animation];
|
||||
breathingGroup.duration = 1.8; // 加速
|
||||
breathingGroup.repeatCount = HUGE_VALF; // 无限循环
|
||||
breathingGroup.autoreverses = YES;
|
||||
breathingGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
||||
|
||||
// 动画 1:透明度变化(呼吸亮度)
|
||||
CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:@"opacity"];
|
||||
opacityAnim.fromValue = @(0.65);
|
||||
opacityAnim.toValue = @(1.0); // 接近完全不透明,颜色饱和
|
||||
|
||||
// 动画 2:缩放变化(呼吸扩散)
|
||||
CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
|
||||
scaleAnim.fromValue = @(1.0);
|
||||
scaleAnim.toValue = @(1.1);
|
||||
|
||||
breathingGroup.animations = @[opacityAnim, scaleAnim];
|
||||
|
||||
[self.glowLayer addAnimation:breathingGroup forKey:@"breathing"];
|
||||
|
||||
NSLog(@"[EPMineHeaderView] 启动呼吸光晕动效");
|
||||
}
|
||||
|
||||
/// Hex 转 UIColor
|
||||
- (UIColor *)colorFromHex:(NSString *)hexString {
|
||||
unsigned rgbValue = 0;
|
||||
|
@@ -200,8 +200,7 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot
|
||||
|
||||
// 验证:文本或图片至少有一项
|
||||
if (self.textView.text.length == 0 && self.images.count == 0) {
|
||||
// TODO: 显示错误提示 "请输入内容或选择图片"
|
||||
NSLog(@"请输入内容或选择图片");
|
||||
[EPProgressHUD showError:YMLocalizedString(@"publish.content_or_image_required")];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -336,7 +335,7 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot
|
||||
- (UILabel *)titleLabel {
|
||||
if (!_titleLabel) {
|
||||
_titleLabel = [UILabel new];
|
||||
_titleLabel.text = @"图文发布";
|
||||
_titleLabel.text = YMLocalizedString(@"publish.title");
|
||||
_titleLabel.textColor = [UIColor whiteColor]; // 白色适配深色背景
|
||||
_titleLabel.font = [UIFont systemFontOfSize:17];
|
||||
}
|
||||
@@ -345,7 +344,7 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot
|
||||
- (UIButton *)publishButton {
|
||||
if (!_publishButton) {
|
||||
_publishButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[_publishButton setTitle:@"发布" forState:UIControlStateNormal];
|
||||
[_publishButton setTitle:YMLocalizedString(@"common.publish") forState:UIControlStateNormal];
|
||||
[_publishButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
_publishButton.titleLabel.font = [UIFont systemFontOfSize:17 weight:UIFontWeightMedium];
|
||||
_publishButton.layer.cornerRadius = 25;
|
||||
|
@@ -85,7 +85,7 @@
|
||||
UIImage *addIcon = [UIImage imageNamed:@"icon_moment_add"];
|
||||
UIButton *publishButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[publishButton setImage:addIcon forState:UIControlStateNormal];
|
||||
publishButton.frame = CGRectMake(0, 0, 40, 50);
|
||||
publishButton.frame = CGRectMake(0, 0, 40, 40);
|
||||
[publishButton addTarget:self action:@selector(onPublishButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||
UIBarButtonItem *publishItem = [[UIBarButtonItem alloc] initWithCustomView:publishButton];
|
||||
self.navigationItem.rightBarButtonItem = publishItem;
|
||||
@@ -105,10 +105,10 @@
|
||||
}
|
||||
|
||||
- (void)showAlertWithMessage:(NSString *)message {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示"
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:YMLocalizedString(@"common.tips")
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:YMLocalizedString(@"common.confirm") style:UIAlertActionStyleDefault handler:nil]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
__weak typeof(self) weakSelf = self;
|
||||
_listView.onSelectMoment = ^(NSInteger index) {
|
||||
__strong typeof(weakSelf) self = weakSelf;
|
||||
[self showAlertWithMessage:[NSString stringWithFormat:@"点击了第 %ld 条动态", (long)index]];
|
||||
[self showAlertWithMessage:[NSString stringWithFormat:YMLocalizedString(@"moment.item_clicked"), (long)index]];
|
||||
};
|
||||
}
|
||||
return _listView;
|
||||
|
@@ -37,7 +37,7 @@ import Foundation
|
||||
completion([], "")
|
||||
}
|
||||
} else {
|
||||
failure(Int(code), msg ?? "请求失败")
|
||||
failure(Int(code), msg ?? YMLocalizedString("error.request_failed"))
|
||||
}
|
||||
}, dynamicId: nextID, pageSize: pageSize, types: types)
|
||||
}
|
||||
@@ -57,7 +57,7 @@ import Foundation
|
||||
failure: @escaping (Int, String) -> Void
|
||||
) {
|
||||
guard let uid = AccountInfoStorage.instance().getUid() else {
|
||||
failure(-1, "用户未登录")
|
||||
failure(-1, YMLocalizedString("error.not_logged_in"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ import Foundation
|
||||
if code == 200 {
|
||||
completion()
|
||||
} else {
|
||||
failure(Int(code), msg ?? "发布失败")
|
||||
failure(Int(code), msg ?? YMLocalizedString("error.publish_failed"))
|
||||
}
|
||||
}, uid: uid, type: type, worldId: "", content: content, resList: resList)
|
||||
}
|
||||
@@ -92,7 +92,7 @@ import Foundation
|
||||
failure: @escaping (Int, String) -> Void
|
||||
) {
|
||||
guard let uid = AccountInfoStorage.instance().getUid() else {
|
||||
failure(-1, "用户未登录")
|
||||
failure(-1, YMLocalizedString("error.not_logged_in"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ import Foundation
|
||||
if code == 200 {
|
||||
completion()
|
||||
} else {
|
||||
failure(Int(code), msg ?? "点赞操作失败")
|
||||
failure(Int(code), msg ?? YMLocalizedString("error.like_failed"))
|
||||
}
|
||||
}, dynamicId: dynamicId, uid: uid, status: status, likedUid: likedUid, worldId: worldIdStr)
|
||||
}
|
||||
|
@@ -21,6 +21,9 @@
|
||||
/// 卡片容器
|
||||
@property (nonatomic, strong) UIView *cardView;
|
||||
|
||||
/// 彩色背景层(毛玻璃下方)
|
||||
@property (nonatomic, strong) UIView *colorBackgroundView;
|
||||
|
||||
/// 毛玻璃效果视图
|
||||
@property (nonatomic, strong) UIVisualEffectView *blurEffectView;
|
||||
|
||||
@@ -83,7 +86,13 @@
|
||||
make.bottom.equalTo(self.contentView).offset(-8);
|
||||
}];
|
||||
|
||||
// 毛玻璃效果视图
|
||||
// 彩色背景层(最底层)
|
||||
[self.cardView addSubview:self.colorBackgroundView];
|
||||
[self.colorBackgroundView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self.cardView);
|
||||
}];
|
||||
|
||||
// 毛玻璃效果视图(在彩色背景层之上)
|
||||
[self.cardView addSubview:self.blurEffectView];
|
||||
[self.blurEffectView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self.cardView);
|
||||
@@ -153,7 +162,7 @@
|
||||
self.currentModel = model;
|
||||
|
||||
// 配置用户名
|
||||
self.nameLabel.text = model.nick ?: @"匿名用户";
|
||||
self.nameLabel.text = model.nick ?: YMLocalizedString(@"user.anonymous");
|
||||
|
||||
// 配置时间(将时间戳转换为 MM/dd 格式)
|
||||
self.timeLabel.text = [self formatTimestampToDate:model.publishTime];
|
||||
@@ -176,20 +185,21 @@
|
||||
[self applyEmotionColorEffect:model.emotionColor];
|
||||
}
|
||||
|
||||
/// 应用情绪颜色视觉效果(Border + Shadow)
|
||||
/// 应用情绪颜色视觉效果(Background + Shadow)
|
||||
- (void)applyEmotionColorEffect:(NSString *)emotionColorHex {
|
||||
NSString *displayColorHex = emotionColorHex;
|
||||
|
||||
// 如果没有保存的颜色,使用随机颜色(不持久化)
|
||||
if (!displayColorHex) {
|
||||
displayColorHex = [EPEmotionColorStorage randomEmotionColor];
|
||||
// 获取颜色(已在列表加载时处理,这里直接使用)
|
||||
if (!emotionColorHex) {
|
||||
NSLog(@"[EPMomentCell] 警告:emotionColorHex 为 nil");
|
||||
return;
|
||||
}
|
||||
|
||||
UIColor *color = [self colorFromHex:displayColorHex];
|
||||
UIColor *color = [self colorFromHex:emotionColorHex];
|
||||
|
||||
// 设置 border
|
||||
self.cardView.layer.borderWidth = 3.0;
|
||||
self.cardView.layer.borderColor = color.CGColor;
|
||||
// 移除边框
|
||||
self.cardView.layer.borderWidth = 0;
|
||||
|
||||
// 设置彩色背景(50% 透明度,在毛玻璃下方)
|
||||
self.colorBackgroundView.backgroundColor = [color colorWithAlphaComponent:0.5];
|
||||
|
||||
// 设置 shadow(使用情绪颜色)
|
||||
self.cardView.layer.shadowColor = color.CGColor;
|
||||
@@ -295,18 +305,18 @@
|
||||
|
||||
/// 格式化时间戳为相对时间
|
||||
- (NSString *)formatTimeInterval:(NSInteger)timestamp {
|
||||
if (timestamp <= 0) return @"刚刚";
|
||||
if (timestamp <= 0) return YMLocalizedString(@"time.just_now");
|
||||
|
||||
NSTimeInterval interval = [[NSDate date] timeIntervalSince1970] - timestamp / 1000.0;
|
||||
|
||||
if (interval < 60) {
|
||||
return @"刚刚";
|
||||
return YMLocalizedString(@"time.just_now");
|
||||
} else if (interval < 3600) {
|
||||
return [NSString stringWithFormat:@"%.0f分钟前", interval / 60];
|
||||
return [NSString stringWithFormat:YMLocalizedString(@"time.minutes_ago"), interval / 60];
|
||||
} else if (interval < 86400) {
|
||||
return [NSString stringWithFormat:@"%.0f小时前", interval / 3600];
|
||||
return [NSString stringWithFormat:YMLocalizedString(@"time.hours_ago"), interval / 3600];
|
||||
} else if (interval < 604800) {
|
||||
return [NSString stringWithFormat:@"%.0f天前", interval / 86400];
|
||||
return [NSString stringWithFormat:YMLocalizedString(@"time.days_ago"), interval / 86400];
|
||||
} else {
|
||||
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
||||
formatter.dateFormat = @"yyyy-MM-dd";
|
||||
@@ -415,14 +425,23 @@
|
||||
- (UIView *)cardView {
|
||||
if (!_cardView) {
|
||||
_cardView = [[UIView alloc] init];
|
||||
_cardView.backgroundColor = [UIColor clearColor]; // 透明背景,毛玻璃效果由 blurEffectView 提供
|
||||
_cardView.backgroundColor = [UIColor clearColor]; // 透明背景,颜色由 colorBackgroundView 提供
|
||||
_cardView.layer.cornerRadius = 12; // 圆角
|
||||
// Shadow 和 Border 将由 applyEmotionColorEffect 动态设置
|
||||
// Shadow 将由 applyEmotionColorEffect 动态设置
|
||||
_cardView.layer.masksToBounds = NO;
|
||||
}
|
||||
return _cardView;
|
||||
}
|
||||
|
||||
- (UIView *)colorBackgroundView {
|
||||
if (!_colorBackgroundView) {
|
||||
_colorBackgroundView = [[UIView alloc] init];
|
||||
_colorBackgroundView.layer.cornerRadius = 12;
|
||||
_colorBackgroundView.layer.masksToBounds = YES;
|
||||
}
|
||||
return _colorBackgroundView;
|
||||
}
|
||||
|
||||
- (UIVisualEffectView *)blurEffectView {
|
||||
if (!_blurEffectView) {
|
||||
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
|
||||
|
@@ -154,8 +154,13 @@
|
||||
NSString *savedColor = [EPEmotionColorStorage colorForDynamicId:model.dynamicId];
|
||||
if (savedColor) {
|
||||
model.emotionColor = savedColor;
|
||||
} else {
|
||||
// 无保存颜色,生成随机颜色并立即持久化
|
||||
NSString *randomColor = [EPEmotionColorStorage randomEmotionColor];
|
||||
model.emotionColor = randomColor;
|
||||
[EPEmotionColorStorage saveColor:randomColor forDynamicId:model.dynamicId];
|
||||
NSLog(@"[EPMomentListView] 为动态 %@ 分配随机颜色: %@", model.dynamicId, randomColor);
|
||||
}
|
||||
// 不设置则保持 nil,Cell 渲染时会随机生成
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +224,7 @@
|
||||
|
||||
// MJRefresh Footer - 加载更多
|
||||
__weak typeof(self) weakSelf = self;
|
||||
_tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
|
||||
MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
|
||||
__strong typeof(weakSelf) self = weakSelf;
|
||||
if (!self.isLoading && self.nextID.length > 0) {
|
||||
[self requestNextPage];
|
||||
@@ -229,6 +234,10 @@
|
||||
[self.tableView.mj_footer endRefreshing];
|
||||
}
|
||||
}];
|
||||
// 设置白色文字和指示器
|
||||
footer.stateLabel.textColor = [UIColor whiteColor];
|
||||
footer.loadingView.color = [UIColor whiteColor];
|
||||
_tableView.mj_footer = footer;
|
||||
}
|
||||
return _tableView;
|
||||
}
|
||||
@@ -236,6 +245,7 @@
|
||||
- (UIRefreshControl *)refreshControl {
|
||||
if (!_refreshControl) {
|
||||
_refreshControl = [[UIRefreshControl alloc] init];
|
||||
_refreshControl.tintColor = [UIColor whiteColor]; // 白色加载指示器
|
||||
[_refreshControl addTarget:self action:@selector(reloadFirstPage) forControlEvents:UIControlEventValueChanged];
|
||||
}
|
||||
return _refreshControl;
|
||||
|
@@ -197,7 +197,7 @@ import SnapKit
|
||||
selectedIndex = newIndex
|
||||
}
|
||||
|
||||
let tabNames = ["动态", "我的"]
|
||||
let tabNames = [YMLocalizedString("tab.moment"), YMLocalizedString("tab.mine")]
|
||||
NSLog("[EPTabBarController] 选中 Tab: \(tabNames[newIndex])")
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ import SnapKit
|
||||
let blankVC1 = UIViewController()
|
||||
blankVC1.view.backgroundColor = .white
|
||||
blankVC1.tabBarItem = createTabBarItem(
|
||||
title: "动态",
|
||||
title: YMLocalizedString("tab.moment"),
|
||||
normalImage: "tab_moment_normal",
|
||||
selectedImage: "tab_moment_selected"
|
||||
)
|
||||
@@ -262,7 +262,7 @@ import SnapKit
|
||||
let blankVC2 = UIViewController()
|
||||
blankVC2.view.backgroundColor = .white
|
||||
blankVC2.tabBarItem = createTabBarItem(
|
||||
title: "我的",
|
||||
title: YMLocalizedString("tab.mine"),
|
||||
normalImage: "tab_mine_normal",
|
||||
selectedImage: "tab_mine_selected"
|
||||
)
|
||||
@@ -313,20 +313,20 @@ import SnapKit
|
||||
|
||||
// 创建动态页
|
||||
let momentVC = EPMomentViewController()
|
||||
momentVC.title = "动态"
|
||||
momentVC.title = YMLocalizedString("tab.moment")
|
||||
let momentNav = createTransparentNavigationController(
|
||||
rootViewController: momentVC,
|
||||
tabTitle: "动态",
|
||||
tabTitle: YMLocalizedString("tab.moment"),
|
||||
normalImage: "tab_moment_normal",
|
||||
selectedImage: "tab_moment_selected"
|
||||
)
|
||||
|
||||
// 创建我的页
|
||||
let mineVC = EPMineViewController()
|
||||
mineVC.title = "我的"
|
||||
mineVC.title = YMLocalizedString("tab.mine")
|
||||
let mineNav = createTransparentNavigationController(
|
||||
rootViewController: mineVC,
|
||||
tabTitle: "我的",
|
||||
tabTitle: YMLocalizedString("tab.mine"),
|
||||
normalImage: "tab_mine_normal",
|
||||
selectedImage: "tab_mine_selected"
|
||||
)
|
||||
|
Reference in New Issue
Block a user