Compare commits

...

63 Commits

Author SHA1 Message Date
edwinQQQ
96c44cb032 删除阿拉伯语首充相关图像资源及其配置文件;在 UIImage+Utils.m 中优化语言图像处理逻辑,新增对葡萄牙语的支持;在多个文件中添加对葡萄牙语的支持,包括本地化字符串和 InfoPlist 配置;更新 UI 组件以适应新的语言设置,确保用户界面的一致性和可用性。 2025-07-15 14:35:05 +08:00
edwinQQQ
16f9041ba9 chore: 更新 .gitignore 文件,移除不必要的包文件类型;新增邮箱验证码登录流程和 OAuth/Ticket 认证系统 API 文档;更新勋章相关逻辑,优化用户信息获取失败处理;调整勋章展示逻辑,支持多等级高亮功能。 2025-07-14 17:37:10 +08:00
edwinQQQ
99db078b62 feat(勋章): 新增多等级高亮功能并优化勋章展示逻辑
refactor(勋章排行): 重构排行榜分页加载和刷新逻辑

fix(会话): 优化官方账号判断逻辑和跳转处理

style(UI): 调整勋章排行榜和游戏菜单UI布局

chore: 更新Podfile配置和bitcode框架列表
2025-07-03 19:35:17 +08:00
edwinQQQ
e32e5653fd change page item limit 2025-07-03 18:04:56 +08:00
edwinQQQ
8a096239da 新增 next-chat.mdc 文件以定义对话上下文的规则;在 Assets.xcassets 中添加多个图像资源及其对应的 Contents.json 文件;更新 AttachmentModel.h 中的枚举值以修正飘屏消息类型;在 SessionViewController.h 和 SessionViewController.m 中添加 userInfo 属性并调整相关逻辑;在 SessionListViewController.h 和 SessionListViewController.m 中优化会话列表的展示逻辑;在多个模型和视图文件中进行小幅调整以提升代码可读性和一致性。 2025-07-02 15:52:48 +08:00
edwinQQQ
f0dad8886f 更新 MedalsModel.h 和 MedalsModel.m,新增 expireDateString 方法以格式化过期时间;在 MedalsCollectionViewCell.m 和 MedalsCyclePagerCell.m 中优化 MP4 和图片的显示逻辑,增强降级处理;在 MedalsDetailView.m 中更新勋章显示逻辑,使用新的 URL 优先级;在 MedalsLevelIndicatorView.h 和 MedalsLevelIndicatorView.m 中新增重置等级指示器的方法;在 MedalsWearingControlCollectionViewCell.m 和 MedalsWearingListCollectionViewCell.m 中实现新的显示逻辑,支持 MP4 和图片的降级处理;在 NSString+Utils.h 和 NSString+Utils.m 中新增图片格式验证方法,提升代码健壮性和可读性。 2025-06-27 19:56:03 +08:00
edwinQQQ
0d6570a086 更新 .vscode/settings.json 文件,新增拼写检查词汇 "Defalut" 和 "MAXFLOAT";在 XPRoomSendTextView.m 中调整约束逻辑,确保 editTextFiled 和 imageButton 的布局正确;在 XPRoomMessageTableViewCell.m 中优化消息内容标签的约束,确保气泡和内容标签的布局一致性,增强代码可读性和健壮性。 2025-06-27 16:15:12 +08:00
edwinQQQ
f52af5d620 更新 YUMIHtmlUrl.h 和 YUMIHtmlUrl.m,新增首充相关的 URL 类型和路径;在 FeedBackViewController.h 和 FeedBackViewController.m 中,调整属性类型为强引用以增强内存管理;重构 FeedBackViewController 的视图设置逻辑,添加子视图的初始化方法以提高代码可读性;移除不再使用的 FeedBackViewController.xib 文件,优化项目结构;在 XPGiftBarView.h 和 XPGiftBarView.m 中,移除首充按钮相关代码,替换为新的首充视图逻辑;更新 XPWebViewController.h 和 XPWebViewController.m,调整 webview 属性为可写以支持动态更新;在 FirstRechargeManager.m 中,优化首充状态检查逻辑,确保在调试模式下始终显示首充信息。 2025-06-27 11:33:16 +08:00
edwinQQQ
86094da26f 添加首充管理功能,新增 FirstRechargeManager 和相关模型以支持首充监控;在多个模块中集成首充监控逻辑,确保在登录和页面加载时正确启动和停止监控;移除不再使用的首充相关代码,优化代码结构和可读性。 2025-06-26 11:08:21 +08:00
edwinQQQ
6f59274ca0 更新 XPRoomMessageParser.m,优化房间表情属性创建逻辑,简化代码结构;新增 createImageAttribute:size: 方法以支持自定义图像大小,增强代码可读性和灵活性;调整图像处理逻辑,确保在不同条件下正确展示表情图像,保持代码一致性。 2025-06-25 15:22:09 +08:00
edwinQQQ
e01148a3a9 更新 pi_area_info.json,新增突尼斯地区信息;更新 Assets.xcassets 中的 Contents.json,修复幸运数字图像的文件名;移除多个文件中的调试代码以优化勋章展示逻辑,保持代码结构一致性;在 XPRoomFaceTool.m 中改进表情文件加载路径和错误处理,增强代码健壮性;在 XPRoomFaceViewController.m 中添加消息发送错误日志;更新 XPRoomMessageContainerView.m 和 XPRoomMessageParser.m,调整索引类型以提高代码一致性。 2025-06-25 14:29:23 +08:00
edwinQQQ
1190e0f6d9 更新 XPMineUserInfoHeaderView.m,调整勋章显示逻辑,允许最多显示 10 个勋章;在 UserRoomCardViewController.m 中新增 UserRoomCardMedalCell 以支持勋章的展示,优化勋章区域的高度计算和更新逻辑,保持代码结构一致性。 2025-06-24 18:50:45 +08:00
edwinQQQ
c5d966317b 更新 XPMineUserInfoHeaderView.m,新增 UIScrollView 作为勋章展示的容器,调整勋章 StackView 的布局逻辑以适应 ScrollView,更新勋章显示方法以处理勋章的内容尺寸,保持代码结构一致性。 2025-06-24 17:10:11 +08:00
edwinQQQ
6573c0f6fe 更新 XPMineUserInfoHeaderView.m,新增 medalsStackView 和 medalMP4Views 属性以支持勋章展示逻辑;添加 medalsHeight 方法计算勋章高度;实现 setupMedalsArea 方法以配置勋章视图;更新 updateMedalsDisplay 方法以处理勋章的 MP4 和图片展示,保持代码结构一致性。同时,更新 .vscode/settings.json 文件以添加拼写检查词汇。 2025-06-24 16:45:26 +08:00
edwinQQQ
7810057542 更新 MedalsViewController.m,移除调试代码以优化勋章展示逻辑;在 XPMineUserInfoHeaderView.m 中新增 medalsStackView 和 userMedals 属性以支持用户勋章的显示;在 UserInfoModel.m 中更新 medalsPic 属性以增强模型的兼容性,保持代码结构一致性。 2025-06-24 15:42:56 +08:00
edwinQQQ
059305d3df 更新 MedalsViewController.m,新增其他用户勋章的显示逻辑,包括 MP4 播放和图片展示,优化相关方法以提升用户体验,保持代码结构一致性。同时,更新 MedalsWearingControlCollectionViewCell.m,移除调试背景颜色设置,保持代码整洁。 2025-06-24 15:01:33 +08:00
edwinQQQ
a15d6b34ed 更新 MedalsRankViewController.m,添加勋章排行视图的点击手势和回调处理,优化用户交互体验,保持代码结构一致性。 2025-06-23 19:09:45 +08:00
edwinQQQ
6d0122f3d1 更新 MedalsViewController.m,调整背景约束和底部勋章列表高度以适应不同显示类型,优化底部背景图像的拉伸处理,保持代码结构一致性。 2025-06-23 18:57:05 +08:00
edwinQQQ
286e68b5e3 更新 MedalsCyclePagerCell 和 MedalsViewController 以优化勋章展示和自动轮播功能,调整可见性管理逻辑,确保在视图切换时正确处理播放状态,保持代码结构一致性。 2025-06-23 18:43:17 +08:00
edwinQQQ
c0500397b6 删除不再使用的资源文件,包括多个图像集和其对应的 Contents.json 文件,更新 MedalsModel.h 和 MedalsModel.m 以支持使用勋章的类型,新增 MedalsCyclePagerCell 组件以优化勋章展示,保持代码结构一致性。 2025-06-23 16:36:39 +08:00
edwinQQQ
f62af24c48 更新勋章相关模型和视图,添加对使用勋章的支持,优化可见性管理和状态更新逻辑,保持代码结构一致性。 2025-06-23 15:55:11 +08:00
edwinQQQ
a72c010b56 新增勋章 VIP 控制相关的资源,包括 medals_control_vip4、medals_control_vip6、medals_control_vip7 和 medals_control_vip9 的 Contents.json 文件及其对应的 vip@3x.png 图片,保持代码结构一致性。 2025-06-23 14:29:08 +08:00
edwinQQQ
143ad115bc 新增勋章排行相关的 UI 组件,包括 MedalsRankViewController、MedalsRankTopView 和 MedalsRankListCell,更新 MedalsViewController 以支持展示其他用户的勋章信息,优化相关逻辑以提升用户体验,保持代码结构一致性。 2025-06-20 18:44:20 +08:00
edwinQQQ
4c76813273 新增勋章相关的 UI 组件,包括 MedalsWearingViewController、MedalsWearingControlCollectionViewCell 和 MedalsWearingListCollectionViewCell,更新相关逻辑以支持勋章的使用状态管理和展示,保持代码结构一致性。 2025-06-19 19:22:56 +08:00
edwinQQQ
da8d88347e 更新 Api+Medals.m 中的请求路径为 "medal/square",修改 MedalsViewController.m 中的导航栏标题以支持勋章广场,新增本地化字符串 "20.20.61_text_10" 以表示勋章广场,保持代码结构一致性。 2025-06-19 14:09:36 +08:00
edwinQQQ
06fecf1bf4 调整 MedalsDetailView.m 中的高度约束以适应内容,优化 MedalsLevelIndicatorView.m 中的图片视图创建和加载逻辑,新增缓存图片 URL 属性以支持延迟加载,确保代码结构一致性。 2025-06-19 11:49:25 +08:00
edwinQQQ
c978a8c355 优化 UIImage+Utils.m 中的灰度图像处理逻辑,调整 MedalsLevelIndicatorView.m 中的图片视图约束和间距,更新图像加载逻辑以使用裁剪后的图像,保持代码结构一致性。 2025-06-19 11:28:07 +08:00
edwinQQQ
a0fc15fefd 更新 MedalsCollectionViewCell 和 MedalsDetailView 以支持带图片的等级指示器,新增 MedalsLevelIndicatorType 枚举以区分指示器类型,优化手势识别功能,确保用户体验流畅,保持代码结构一致性。 2025-06-18 19:47:39 +08:00
edwinQQQ
c1b9dd3d9f 新增手势识别功能以处理背景和内容区域的点击事件,确保用户体验流畅。重写 removeFromSuperview 方法以停止 MP4 播放并释放资源,添加 dealloc 方法以确保资源正确释放,保持代码结构一致性。 2025-06-18 19:00:01 +08:00
edwinQQQ
887bb19056 新增 MedalsDetailView 和 MedalsLevelIndicatorView 组件,更新 MedalsCollectionViewCell 以支持 MP4 播放控制和可见性管理,优化相关逻辑以提升用户体验,保持代码结构一致性。 2025-06-18 18:31:13 +08:00
edwinQQQ
413b2c6944 新增等级指示器视图和容器视图,更新 MedalsCollectionViewCell 以集成等级指示器功能,支持等级选择回调。更新 .vscode/settings.json 文件以包含新组件 QGVAP 的配置,保持代码结构一致性。 2025-06-18 17:11:13 +08:00
edwinQQQ
89d9b57dea 更新 Podfile 以注释掉不再使用的 ZLCollectionViewFlowLayout 依赖,同时新增勋章相关功能,包括勋章使用、广场、排行和管理列表的 API 方法,新增 MedalsModel、MedalsPresenter 和 MedalsViewController 相关文件,保持代码结构一致性。新增勋章相关的 UI 组件和本地化字符串支持。 2025-06-18 16:28:13 +08:00
edwinQQQ
8c0276a208 优化数组操作,新增 NSMutableArray 的安全方法,包括安全移除、插入和替换对象,确保在索引超出范围时不发生崩溃。更新相关文件以使用新方法,保持代码结构一致性。 2025-06-16 17:30:20 +08:00
edwinQQQ
68ce148abb 新增勋章相关功能,包括勋章使用、广场、排行和管理列表的 API 方法,更新 Api+Mine.h 和 Api+Mine.m 文件以支持新功能。同时,新增 BaseModelVo.h 和 BaseModelVo.m 文件以定义勋章相关数据模型,并在 UserInfoModel.h 中引入 BaseModelVo。保持代码结构一致性。 2025-06-11 15:00:02 +08:00
edwinQQQ
4416e9f9c8 新增 VIP 和转账功能的 URL 路径,更新相关文件以使用 XPWebViewController 替代 VIPCenterViewController,保持代码结构一致性。调整公屏礼物消失的显示内容。 2025-06-11 11:27:00 +08:00
edwinQQQ
6e814f76cb 移除与 Agora 相关的 SDK 代码和引用,更新 Podfile 以删除不再使用的依赖项,调整房间初始化逻辑以使用 TRTC 引擎。保持代码结构一致性。 2025-06-10 16:07:59 +08:00
edwinQQQ
443e1a4332 新增分享功能的多个方法,包括提取 URL 和剩余字符串、强制关闭分享弹窗,以及分享内容的构建逻辑。更新 ShareProvider 类以支持新的初始化方法和不同分享类型的内容提供。保持代码结构一致性。 2025-06-09 16:39:32 +08:00
edwinQQQ
8dab721de9 在 SessionListViewController 中添加了对最近会话的日志输出。在 LuckyGiftWinningBannerView 和 BravoGiftBannerView 中引入了可配置的显示时间,注释掉了不再使用的代码。在 XPRoomFunctionContainerView 中优化了 hitTest 方法以增强用户交互体验,确保子视图的点击响应逻辑更加清晰。更新了 MicroView 中的表情加载逻辑,增加了对下载失败情况的处理。保持代码结构一致性。 2025-06-09 15:52:34 +08:00
edwinQQQ
5985b5701e 新增排行榜功能,更新 YUMIHtmlUrl.h 和 YUMIHtmlUrl.m 文件以支持新路径 kRankV2,调整 XPNewHomeViewController.m 中的相关逻辑以使用新排行榜链接,更新 XPMineUserInfoViewController.m 和 XPMineUserInfoHeaderView.m 文件以修复用户信息展示。保持代码结构一致性。 2025-06-03 11:00:10 +08:00
edwinQQQ
5ff62bafee 优化房间退出逻辑,更新退出事件处理,修复房间类型判断。更新分享功能以支持内容参数。调整游戏状态处理,重置当前游戏ID。 2025-05-29 11:50:00 +08:00
edwinQQQ
ca25ed14ac 新增搜索视图的 UITextFieldDelegate 实现,优化搜索逻辑,添加 isReturnKey 属性以区分回车触发的搜索。更新 GiftComboManager 和 HttpRequestHelper 中的错误处理逻辑,修复条件判断中的错误。移除不再使用的代码。版本号更新至1.0.28.2。 2025-05-27 16:02:11 +08:00
edwinQQQ
e5c8baad34 更新版本号至1.0.28.1,移除调试代码,新增隐私信息文件,优化房间消息处理逻辑,增加网络检测相关功能,调整部分接口参数,修复UI问题,更新本地化字符串,更新 SUD SDK 2025-05-26 15:24:31 +08:00
edwinQQQ
c765e78ec6 更新资源文件,替换部分图片文件名,调整图片内容,更新版本号至1.0.28。增加新的图片资源并删除不再使用的图片文件。 2025-05-23 14:43:39 +08:00
edwinQQQ
95e3e2601f 新增自定义麦位布局和房间麦位选择视图,优化房间用户界面,更新本地化字符串,调整部分UI组件约束。版本号更新至20.20.65。 2025-05-22 18:04:39 +08:00
edwinQQQ
0d01b1e6e9 更新事件中心模块,新增 HomeBannerInfoModel 相关功能,优化事件列表数据处理,调整时间选择器逻辑,修复部分 UI 问题,更新本地化字符串。版本号更新至20.20.64。 2025-05-22 11:50:53 +08:00
edwinQQQ
298a7e80c4 优化内购交易重试机制,增加交易重试次数记录,完善后台任务处理,更新相关API接口,调整UI组件约束,修复部分UI问题,更新本地化字符串。版本号更新至20.20.63。 2025-05-21 18:59:32 +08:00
edwinQQQ
b688962559 更新事件创建功能,新增金钱加载接口,优化事件时长选择器,调整UI组件约束,修复部分UI问题,更新本地化字符串。增加删除事件功能的提示确认。版本号更新至20.20.62。 2025-05-20 18:49:04 +08:00
edwinQQQ
353491f56f 更新活动中心模块,新增事件配置模型及相关API接口,优化事件列表展示,修复部分UI问题,更新本地化字符串,调整部分UI组件。删除不再使用的视图控制器。版本号更新至20.20.61。 2025-05-19 19:27:53 +08:00
edwinQQQ
a99e650d45 新增活动详情页相关功能,包括事件模型、API接口、UI组件及相关资源文件。更新版本号至20.20.60。 2025-05-15 16:24:43 +08:00
edwinQQQ
bd2e5e380f 增加选择房间 vc 2025-05-12 18:53:42 +08:00
edwinQQQ
2c5a1a289e 创建事件 create event UI 2025-05-12 17:37:58 +08:00
edwinQQQ
ffdfa121f9 新增 JoyPlay 游戏支持 2025-05-08 17:10:54 +08:00
AI Health Developer
07ebee2fc1 修正部分体验问题; 房间表情新增 api; 房间表情新增 svga 支持以及持久化缓存 2025-05-07 19:30:50 +08:00
AI Health Developer
69863a91ff feat(EventCenter): 添加活动中心模块
新增活动中心模块,包括官方活动、活动广场和我的活动三个页面。支持活动列表展示、倒计时功能及创建活动按钮。优化了页面切换和UI交互体验。
2025-05-06 16:45:34 +08:00
AI Health Developer
a9538c61ff 修正表情播放问题 2025-04-28 18:28:59 +08:00
AI Health Developer
41aae87dd6 修改 UI 需求和问题 2025-04-28 14:52:00 +08:00
AI Health Developer
ff365e8ae4 修正审核问题,适配后端修改 2025-04-25 15:54:30 +08:00
AI Health Developer
40311a1d85 完成 1.0.26 需求 2025-04-21 13:52:13 +08:00
AI Health Developer
2caa476640 temp save 2025-04-11 10:46:17 +08:00
AI Health Developer
faa6a2c4dc 修正 combo 金币显示问题 2025-04-11 10:45:49 +08:00
edwinQQQ
0275decc7e Finish 2.7.34 2025-04-02 11:04:07 +08:00
edwinQQQ
bc9a02802d boom 重构暂存 2025-03-25 15:22:01 +08:00
edwinQQQ
d9850e2c8b 需求暫存 2025-03-24 16:09:48 +08:00
737 changed files with 27776 additions and 13219 deletions

View File

@@ -0,0 +1,7 @@
---
description:
globs:
alwaysApply: false
---
本次对话的上下文已经太长了,我打算关掉并重新开一个新的会话。
你有什么想对你的继任者说的,以便它能更好的理解你当前的工作并顺利继续?

6
.gitignore vendored
View File

@@ -82,12 +82,6 @@ iOSInjectionProject/
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

40
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,40 @@
{
"cSpell.ignoreWords": [
"DJDKMIMOM",
"Strongify",
"Weakify",
"instancetype",
"nonatomic"
],
"cSpell.words": [
"autoreleasepool",
"Autoresizing",
"BGMP",
"Bugly",
"Commont",
"Contol",
"CPSVGA",
"Defalut",
"erban",
"exper",
"Headwear",
"HWDMP",
"Interitem",
"kindof",
"MAXFLOAT",
"MSRTL",
"NIMSDK",
"Nonnull",
"NSEC",
"NSURL",
"Offical",
"Procotol",
"QGVAP",
"Subview",
"subviews",
"Superview",
"Uids",
"XNDJTDD"
],
"C_Cpp.errorSquiggles": "disabled"
}

View File

@@ -0,0 +1,434 @@
# 邮箱验证码登录流程文档
## 概述
本文档详细描述了 YuMi iOS 应用中 `LoginTypesViewController``LoginDisplayType_email` 模式下的邮箱验证码登录流程。该流程实现了基于邮箱和验证码的用户认证机制。
## 系统架构
### 核心组件
- **LoginTypesViewController**: 登录类型控制器,负责 UI 展示和用户交互
- **LoginPresenter**: 登录业务逻辑处理器,负责与 API 交互
- **LoginInputItemView**: 输入组件,提供邮箱和验证码输入界面
- **Api+Login**: 登录相关 API 接口封装
- **AccountInfoStorage**: 账户信息本地存储管理
### 数据模型
#### LoginDisplayType 枚举
```objc
typedef NS_ENUM(NSUInteger, LoginDisplayType) {
LoginDisplayType_id, // ID 登录
LoginDisplayType_email, // 邮箱登录 ✓
LoginDisplayType_phoneNum, // 手机号登录
LoginDisplayType_email_forgetPassword, // 邮箱忘记密码
LoginDisplayType_phoneNum_forgetPassword, // 手机号忘记密码
};
```
#### LoginInputType 枚举
```objc
typedef NS_ENUM(NSUInteger, LoginInputType) {
LoginInputType_email, // 邮箱输入
LoginInputType_verificationCode, // 验证码输入
LoginInputType_login, // 登录按钮
// ... 其他类型
};
```
#### GetSmsType 验证码类型
```objc
typedef NS_ENUM(NSUInteger, GetSmsType) {
GetSmsType_Regist = 1, // 注册(邮箱登录使用此类型)
GetSmsType_Login = 2, // 登录
GetSmsType_Reset_Password = 3, // 重设密码
// ... 其他类型
};
```
## 登录流程详解
### 1. 界面初始化流程
#### 1.1 控制器初始化
```objc
// 在 LoginViewController 中点击邮箱登录按钮
- (void)didTapEntrcyButton:(UIButton *)sender {
if (sender.tag == LoginType_Email) {
LoginTypesViewController *vc = [[LoginTypesViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
[vc updateLoginType:LoginDisplayType_email]; // 设置为邮箱登录模式
}
}
```
#### 1.2 输入区域设置
```objc
- (void)setupEmailInputArea {
[self setupInpuArea:LoginInputType_email // 第一行:邮箱输入
second:LoginInputType_verificationCode // 第二行:验证码输入
third:LoginInputType_none // 第三行:无
action:LoginInputType_login // 操作按钮:登录
showForgetPassword:NO]; // 不显示忘记密码
}
```
#### 1.3 UI 组件配置
- **第一行输入框**: 邮箱地址输入
- 占位符: "请输入邮箱地址"
- 键盘类型: `UIKeyboardTypeEmailAddress`
- 回调: `handleFirstInputContentUpdate`
- **第二行输入框**: 验证码输入
- 占位符: "请输入验证码"
- 键盘类型: `UIKeyboardTypeDefault`
- 附带"获取验证码"按钮
- 回调: `handleSecondInputContentUpdate`
### 2. 验证码获取流程
#### 2.1 用户交互触发
```objc
// 用户点击"获取验证码"按钮
[self.secondLineInputView setHandleItemAction:^(LoginInputType inputType) {
if (inputType == LoginInputType_verificationCode) {
if (self.type == LoginDisplayType_email) {
[self handleTapGetMailVerificationCode];
}
}
}];
```
#### 2.2 邮箱验证码获取处理
```objc
- (void)handleTapGetMailVerificationCode {
NSString *email = [self.firstLineInputView inputContent];
// 邮箱地址验证
if (email.length == 0) {
[self.secondLineInputView endVerificationCountDown];
return;
}
// 调用 Presenter 发送验证码
[self.presenter sendMailVerificationCode:email type:GetSmsType_Regist];
}
```
#### 2.3 Presenter 层处理
```objc
- (void)sendMailVerificationCode:(NSString *)emailAddress type:(NSInteger)type {
// DES 加密邮箱地址
NSString *desEmail = [DESEncrypt encryptUseDES:emailAddress
key:KeyWithType(KeyType_PasswordEncode)];
@kWeakify(self);
[Api emailGetCode:[self createHttpCompletion:^(BaseModel *data) {
@kStrongify(self);
if ([[self getView] respondsToSelector:@selector(emailCodeSucess:type:)]) {
[[self getView] emailCodeSucess:@"" type:type];
}
} fail:^(NSInteger code, NSString *msg) {
@kStrongify(self);
if ([[self getView] respondsToSelector:@selector(emailCodeFailure)]) {
[[self getView] emailCodeFailure];
}
} showLoading:YES errorToast:YES]
emailAddress:desEmail
type:@(type)];
}
```
#### 2.4 API 接口调用
```objc
+ (void)emailGetCode:(HttpRequestHelperCompletion)completion
emailAddress:(NSString *)emailAddress
type:(NSNumber *)type {
[self makeRequest:@"email/getCode"
method:HttpRequestHelperMethodPOST
completion:completion, __FUNCTION__, emailAddress, type, nil];
}
```
**API 详情**:
- **接口路径**: `POST /email/getCode`
- **请求参数**:
- `emailAddress`: 邮箱地址DES 加密)
- `type`: 验证码类型1=注册)
#### 2.5 获取验证码成功处理
```objc
- (void)emailCodeSucess:(NSString *)message type:(GetSmsType)type {
[self showSuccessToast:YMLocalizedString(@"XPLoginPhoneViewController2")]; // "验证码已发送"
[self.secondLineInputView startVerificationCountDown]; // 开始倒计时
[self.secondLineInputView displayKeyboard]; // 显示键盘
}
```
#### 2.6 获取验证码失败处理
```objc
- (void)emailCodeFailure {
[self.secondLineInputView endVerificationCountDown]; // 结束倒计时
}
```
### 3. 邮箱登录流程
#### 3.1 登录按钮状态检查
```objc
- (void)checkActionButtonStatus {
switch (self.type) {
case LoginDisplayType_email: {
NSString *accountString = [self.firstLineInputView inputContent]; // 邮箱
NSString *codeString = [self.secondLineInputView inputContent]; // 验证码
// 只有当邮箱和验证码都不为空时才启用登录按钮
if (![NSString isEmpty:accountString] && ![NSString isEmpty:codeString]) {
self.bottomActionButton.enabled = YES;
} else {
self.bottomActionButton.enabled = NO;
}
}
break;
}
}
```
#### 3.2 登录按钮点击处理
```objc
- (void)didTapActionButton {
[self.view endEditing:true];
switch (self.type) {
case LoginDisplayType_email: {
// 调用 Presenter 进行邮箱登录
[self.presenter loginWithEmail:[self.firstLineInputView inputContent]
code:[self.secondLineInputView inputContent]];
}
break;
}
}
```
#### 3.3 Presenter 层登录处理
```objc
- (void)loginWithEmail:(NSString *)email code:(NSString *)code {
// DES 加密邮箱地址
NSString *desMail = [DESEncrypt encryptUseDES:email
key:KeyWithType(KeyType_PasswordEncode)];
@kWeakify(self);
[Api loginWithCode:[self createHttpCompletion:^(BaseModel *data) {
@kStrongify(self);
// 解析账户模型
AccountModel *accountModel = [AccountModel modelWithDictionary:data.data];
// 保存账户信息
if (accountModel && accountModel.access_token.length > 0) {
[[AccountInfoStorage instance] saveAccountInfo:accountModel];
}
// 通知登录成功
if ([[self getView] respondsToSelector:@selector(loginSuccess)]) {
[[self getView] loginSuccess];
}
} fail:^(NSInteger code, NSString *msg) {
@kStrongify(self);
[[self getView] loginFailWithMsg:msg];
} errorToast:NO]
email:desMail
code:code
client_secret:clinet_s // 客户端密钥
version:@"1"
client_id:@"erban-client"
grant_type:@"email"]; // 邮箱登录类型
}
```
#### 3.4 API 接口调用
```objc
+ (void)loginWithCode:(HttpRequestHelperCompletion)completion
email:(NSString *)email
code:(NSString *)code
client_secret:(NSString *)client_secret
version:(NSString *)version
client_id:(NSString *)client_id
grant_type:(NSString *)grant_type {
NSString *fang = [NSString stringFromBase64String:@"b2F1dGgvdG9rZW4="]; // oauth/token
[self makeRequest:fang
method:HttpRequestHelperMethodPOST
completion:completion, __FUNCTION__, email, code, client_secret,
version, client_id, grant_type, nil];
}
```
**API 详情**:
- **接口路径**: `POST /oauth/token`
- **请求参数**:
- `email`: 邮箱地址DES 加密)
- `code`: 验证码
- `client_secret`: 客户端密钥
- `version`: 版本号 "1"
- `client_id`: 客户端ID "erban-client"
- `grant_type`: 授权类型 "email"
#### 3.5 登录成功处理
```objc
- (void)loginSuccess {
[self showSuccessToast:YMLocalizedString(@"XPLoginPhoneViewController1")]; // "登录成功"
[PILoginManager loginWithVC:self isLoginPhone:NO]; // 执行登录后续处理
}
```
#### 3.6 登录失败处理
```objc
- (void)loginFailWithMsg:(NSString *)msg {
[self showSuccessToast:msg]; // 显示错误信息
}
```
## 数据流时序图
```mermaid
sequenceDiagram
participant User as 用户
participant VC as LoginTypesViewController
participant IV as LoginInputItemView
participant P as LoginPresenter
participant API as Api+Login
participant Storage as AccountInfoStorage
Note over User,Storage: 1. 初始化邮箱登录界面
User->>VC: 选择邮箱登录
VC->>VC: updateLoginType(LoginDisplayType_email)
VC->>VC: setupEmailInputArea()
VC->>IV: 创建邮箱输入框
VC->>IV: 创建验证码输入框
Note over User,Storage: 2. 获取邮箱验证码
User->>IV: 输入邮箱地址
User->>IV: 点击"获取验证码"
IV->>VC: handleTapGetMailVerificationCode
VC->>VC: 验证邮箱地址非空
VC->>P: sendMailVerificationCode(email, GetSmsType_Regist)
P->>P: DES加密邮箱地址
P->>API: emailGetCode(encryptedEmail, type=1)
API-->>P: 验证码发送结果
P-->>VC: emailCodeSucess / emailCodeFailure
VC->>IV: startVerificationCountDown / endVerificationCountDown
VC->>User: 显示成功/失败提示
Note over User,Storage: 3. 邮箱验证码登录
User->>IV: 输入验证码
IV->>VC: 输入内容变化回调
VC->>VC: checkActionButtonStatus()
VC->>User: 启用/禁用登录按钮
User->>VC: 点击登录按钮
VC->>VC: didTapActionButton()
VC->>P: loginWithEmail(email, code)
P->>P: DES加密邮箱地址
P->>API: loginWithCode(email, code, ...)
API-->>P: OAuth Token 响应
P->>P: 解析 AccountModel
P->>Storage: saveAccountInfo(accountModel)
P-->>VC: loginSuccess / loginFailWithMsg
VC->>User: 显示登录结果
VC->>User: 跳转到主界面
```
## 安全机制
### 1. 数据加密
- **邮箱地址加密**: 使用 DES 算法加密邮箱地址后传输
```objc
NSString *desEmail = [DESEncrypt encryptUseDES:email key:KeyWithType(KeyType_PasswordEncode)];
```
### 2. 输入验证
- **邮箱格式验证**: 通过 `UIKeyboardTypeEmailAddress` 键盘类型引导正确输入
- **非空验证**: 邮箱和验证码都必须非空才能执行登录
### 3. 验证码安全
- **时效性**: 验证码具有倒计时机制,防止重复获取
- **类型标识**: 使用 `GetSmsType_Regist = 1` 标识登录验证码
### 4. 网络安全
- **错误处理**: 完整的成功/失败回调机制
- **加载状态**: `showLoading:YES` 防止重复请求
- **错误提示**: `errorToast:YES` 显示网络错误
## 错误处理机制
### 1. 邮箱验证码获取错误
```objc
- (void)emailCodeFailure {
[self.secondLineInputView endVerificationCountDown]; // 停止倒计时
// 用户可以重新获取验证码
}
```
### 2. 登录失败处理
```objc
- (void)loginFailWithMsg:(NSString *)msg {
[self showSuccessToast:msg]; // 显示具体错误信息
// 用户可以重新尝试登录
}
```
### 3. 网络请求错误
- **自动重试**: 用户可以手动重新点击获取验证码或登录
- **错误提示**: 通过 Toast 显示具体错误信息
- **状态恢复**: 失败后恢复按钮可点击状态
## 本地化支持
### 关键文本资源
- `@"20.20.51_text_1"`: "邮箱登录"
- `@"20.20.51_text_4"`: "请输入邮箱地址"
- `@"20.20.51_text_7"`: "请输入验证码"
- `@"XPLoginPhoneViewController2"`: "验证码已发送"
- `@"XPLoginPhoneViewController1"`: "登录成功"
### 多语言支持
- 简体中文 (`zh-Hant.lproj`)
- 英文 (`en.lproj`)
- 阿拉伯语 (`ar.lproj`)
- 土耳其语 (`tr.lproj`)
## 依赖组件
### 外部框架
- **MASConstraintMaker**: 自动布局
- **ReactiveObjC**: 响应式编程(部分组件使用)
### 内部组件
- **YMLocalizedString**: 本地化字符串管理
- **DESEncrypt**: DES 加密工具
- **AccountInfoStorage**: 账户信息存储
- **HttpRequestHelper**: 网络请求管理
## 扩展和维护
### 新增功能建议
1. **邮箱格式验证**: 添加正则表达式验证邮箱格式
2. **验证码长度限制**: 限制验证码输入长度
3. **自动填充**: 支持系统邮箱自动填充
4. **记住邮箱**: 保存最近使用的邮箱地址
### 性能优化
1. **请求去重**: 防止短时间内重复请求验证码
2. **缓存机制**: 缓存验证码倒计时状态
3. **网络优化**: 添加请求超时和重试机制
### 代码维护
1. **常量管理**: 将硬编码字符串提取为常量
2. **错误码统一**: 统一管理API错误码
3. **日志记录**: 添加详细的操作日志
## 总结
邮箱验证码登录流程是一个完整的用户认证系统,包含了界面展示、验证码获取、用户登录、数据存储等完整环节。该流程具有良好的安全性、用户体验和错误处理机制,符合现代移动应用的认证标准。
通过本文档,开发人员可以全面了解邮箱登录的实现细节,便于后续的功能扩展和维护工作。

View File

@@ -0,0 +1,262 @@
# OAuth/Ticket 认证系统 API 文档
## 概述
本文档描述了 YuMi 应用中 OAuth 认证和 Ticket 会话管理的完整流程。系统采用两阶段认证机制:
1. **OAuth 阶段**:用户登录获取 `access_token`
2. **Ticket 阶段**:使用 `access_token` 获取业务会话 `ticket`
## 认证流程架构
### 核心组件
- **AccountInfoStorage**: 负责账户信息和 ticket 的本地存储
- **HttpRequestHelper**: 网络请求管理,自动添加认证头
- **Api+Login**: 登录相关 API 接口
- **Api+Main**: Ticket 获取相关 API 接口
### 认证数据模型
#### AccountModel
```objc
@interface AccountModel : PIBaseModel
@property (nonatomic, assign) NSString *uid; // 用户唯一标识
@property (nonatomic, copy) NSString *jti; // JWT ID
@property (nonatomic, copy) NSString *token_type; // Token 类型
@property (nonatomic, copy) NSString *refresh_token; // 刷新令牌
@property (nonatomic, copy) NSString *netEaseToken; // 网易云信令牌
@property (nonatomic, copy) NSString *access_token; // OAuth 访问令牌
@property (nonatomic, assign) NSNumber *expires_in; // 过期时间
@end
```
## API 接口详情
### 1. OAuth 登录接口
#### 1.1 手机验证码登录
```objc
+ (void)loginWithCode:(HttpRequestHelperCompletion)completion
phone:(NSString *)phone
code:(NSString *)code
client_secret:(NSString *)client_secret
version:(NSString *)version
client_id:(NSString *)client_id
grant_type:(NSString *)grant_type
phoneAreaCode:(NSString *)phoneAreaCode;
```
**接口路径**: `POST /oauth/token`
**请求参数**:
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| phone | String | 是 | 手机号DES加密 |
| code | String | 是 | 验证码 |
| client_secret | String | 是 | 客户端密钥,固定值:"uyzjdhds" |
| version | String | 是 | 版本号,固定值:"1" |
| client_id | String | 是 | 客户端ID固定值"erban-client" |
| grant_type | String | 是 | 授权类型,验证码登录为:"sms_code" |
| phoneAreaCode | String | 是 | 手机区号 |
**返回数据**: AccountModel 对象
#### 1.2 手机密码登录
```objc
+ (void)loginWithPassword:(HttpRequestHelperCompletion)completion
phone:(NSString *)phone
password:(NSString *)password
client_secret:(NSString *)client_secret
version:(NSString *)version
client_id:(NSString *)client_id
grant_type:(NSString *)grant_type;
```
**接口路径**: `POST /oauth/token`
**请求参数**:
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| phone | String | 是 | 手机号DES加密 |
| password | String | 是 | 密码DES加密 |
| client_secret | String | 是 | 客户端密钥 |
| version | String | 是 | 版本号 |
| client_id | String | 是 | 客户端ID |
| grant_type | String | 是 | 授权类型,密码登录为:"password" |
#### 1.3 第三方登录
```objc
+ (void)loginWithThirdPart:(HttpRequestHelperCompletion)completion
openid:(NSString *)openid
unionid:(NSString *)unionid
access_token:(NSString *)access_token
type:(NSString *)type;
```
**接口路径**: `POST /acc/third/login`
**请求参数**:
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| openid | String | 是 | 第三方平台用户唯一标识 |
| unionid | String | 是 | 第三方平台联合ID |
| access_token | String | 是 | 第三方平台访问令牌 |
| type | String | 是 | 第三方平台类型1:Apple, 2:Facebook, 3:Google等 |
### 2. Ticket 获取接口
#### 2.1 获取 Ticket
```objc
+ (void)requestTicket:(HttpRequestHelperCompletion)completion
access_token:(NSString *)accessToken
issue_type:(NSString *)issueType;
```
**接口路径**: `POST /oauth/ticket`
**请求参数**:
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| access_token | String | 是 | OAuth 登录获取的访问令牌 |
| issue_type | String | 是 | 签发类型,固定值:"multi" |
**返回数据**:
```json
{
"code": 200,
"data": {
"tickets": [
{
"ticket": "eyJhbGciOiJIUzI1NiJ9..."
}
]
}
}
```
### 3. HTTP 请求头配置
所有业务 API 请求都会自动添加以下请求头:
```objc
// 在 HttpRequestHelper 中自动配置
- (void)setupHeader {
AFHTTPSessionManager *client = [HttpRequestHelper requestManager];
// 用户ID头
if ([[AccountInfoStorage instance] getUid].length > 0) {
[client.requestSerializer setValue:[[AccountInfoStorage instance] getUid]
forHTTPHeaderField:@"pub_uid"];
}
// Ticket 认证头
if ([[AccountInfoStorage instance] getTicket].length > 0) {
[client.requestSerializer setValue:[[AccountInfoStorage instance] getTicket]
forHTTPHeaderField:@"pub_ticket"];
}
// 其他公共头
[client.requestSerializer setValue:[NSBundle uploadLanguageText]
forHTTPHeaderField:@"Accept-Language"];
[client.requestSerializer setValue:PI_App_Version
forHTTPHeaderField:@"App-Version"];
}
```
## 使用流程
### 完整登录流程示例
```objc
// 1. 用户登录获取 access_token
[Api loginWithCode:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
if (code == 200) {
// 保存账户信息
AccountModel *accountModel = [AccountModel modelWithDictionary:data.data];
[[AccountInfoStorage instance] saveAccountInfo:accountModel];
// 2. 使用 access_token 获取 ticket
[Api requestTicket:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
if (code == 200) {
NSArray *tickets = [data.data valueForKey:@"tickets"];
NSString *ticket = [tickets[0] valueForKey:@"ticket"];
// 保存 ticket
[[AccountInfoStorage instance] saveTicket:ticket];
// 3. 登录成功,可以进行业务操作
[self navigateToMainPage];
}
} access_token:accountModel.access_token issue_type:@"multi"];
}
} phone:encryptedPhone
code:verificationCode
client_secret:@"uyzjdhds"
version:@"1"
client_id:@"erban-client"
grant_type:@"sms_code"
phoneAreaCode:areaCode];
```
### 自动登录流程
```objc
- (void)autoLogin {
// 检查本地是否有账户信息
AccountModel *accountModel = [[AccountInfoStorage instance] getCurrentAccountInfo];
if (accountModel == nil || accountModel.access_token == nil) {
[self tokenInvalid]; // 跳转到登录页
return;
}
// 检查是否有有效的 ticket
if ([[AccountInfoStorage instance] getTicket].length > 0) {
[[self getView] autoLoginSuccess];
return;
}
// 使用 access_token 重新获取 ticket
[Api requestTicket:^(BaseModel * _Nonnull data) {
NSArray *tickets = [data.data valueForKey:@"tickets"];
NSString *ticket = [tickets[0] valueForKey:@"ticket"];
[[AccountInfoStorage instance] saveTicket:ticket];
[[self getView] autoLoginSuccess];
} fail:^(NSInteger code, NSString * _Nullable msg) {
[self logout]; // ticket 获取失败,重新登录
} access_token:accountModel.access_token issue_type:@"multi"];
}
```
## 错误处理
### 401 未授权错误
当接收到 401 状态码时,系统会自动处理:
```objc
// 在 HttpRequestHelper 中
if (response && response.statusCode == 401) {
failure(response.statusCode, YMLocalizedString(@"HttpRequestHelper7"));
// 通常需要重新登录
}
```
### Ticket 过期处理
- Ticket 过期时服务器返回 401 错误
- 客户端应该使用保存的 `access_token` 重新获取 ticket
- 如果 `access_token` 也过期,则需要用户重新登录
## 安全注意事项
1. **数据加密**: 敏感信息(手机号、密码)使用 DES 加密传输
2. **本地存储**:
- `access_token` 存储在文件系统中
- `ticket` 存储在内存中,应用重启需重新获取
3. **请求头**: 所有业务请求自动携带 `pub_uid``pub_ticket`
4. **错误处理**: 建立完善的 401 错误重试机制
## 相关文件
- `YuMi/Structure/MVP/Model/AccountInfoStorage.h/m` - 账户信息存储管理
- `YuMi/Modules/YMLogin/Api/Api+Login.h/m` - 登录相关接口
- `YuMi/Modules/YMTabbar/Api/Api+Main.h/m` - Ticket 获取接口
- `YuMi/Network/HttpRequestHelper.h/m` - 网络请求管理
- `YuMi/Structure/MVP/Model/AccountModel.h/m` - 账户数据模型

View File

@@ -9,7 +9,6 @@ target 'YuMi' do
pod 'FBSDKLoginKit'
pod 'FBSDKCoreKit'
pod 'FBSDKShareKit'
pod 'LineSDKSwift'
# 滑动标签栏
pod 'JXCategoryView'
pod 'JXPagingView/Pager'
@@ -45,7 +44,7 @@ target 'YuMi' do
#上传音乐
pod 'CocoaAsyncSocket',:modular_headers => true
#声网
pod 'AgoraRtcEngine_iOS'
pod 'SSKeychain'
pod 'Base64'
#pop动画
@@ -58,7 +57,6 @@ target 'YuMi' do
pod 'mob_linksdk_pro'
pod 'mob_sharesdk'
pod 'mob_sharesdk/ShareSDKPlatforms/Apple'
#pod 'mob_sharesdk/ShareSDKPlatforms/Line'
pod 'mob_sharesdk/ShareSDKExtension'
pod 'UMCommon'
@@ -76,6 +74,7 @@ post_install do |installer|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0'
config.build_settings['DEBUG_INFORMATION_FORMAT'] = 'dwarf-with-dsym'
config.build_settings['ENABLE_BITCODE'] = 'NO'
xcconfig_path = config.base_configuration_reference.real_path
xcconfig = File.read(xcconfig_path)
xcconfig_mod = xcconfig.gsub(/DT_TOOLCHAIN_DIR/, "TOOLCHAIN_DIR")

File diff suppressed because it is too large Load Diff

View File

@@ -27,6 +27,7 @@
#import <UserNotifications/UserNotifications.h>
#import <Bugly/Bugly.h>
#import <UIKit/UIDevice.h>
#import "YuMi-swift.h"
@@ -75,6 +76,7 @@ UIKIT_EXTERN NSString * adImageName;
/**
Bugly
*/
- (void) configBugly {
BuglyConfig *config = [[BuglyConfig alloc] init];
@@ -83,16 +85,15 @@ UIKIT_EXTERN NSString * adImageName;
#ifdef DEBUG
config.debugMode = NO;//YES; // debug
config.channel = [YYUtility getAppSource];
// config.blockMonitorEnable = YES; //
config.reportLogLevel = BuglyLogLevelSilent; // BuglyLogLevelVerbose; //
config.reportLogLevel = BuglyLogLevelWarn;// BuglyLogLevelSilent; // BuglyLogLevelVerbose; //
[Bugly startWithAppId:@"c937fd00f7" config:config];
#else
config.unexpectedTerminatingDetectionEnable = YES; // 退
config.debugMode = NO; // release
config.channel = [YYUtility getAppSource];;
config.blockMonitorEnable = NO; //
config.blockMonitorEnable = YES; //
config.reportLogLevel = BuglyLogLevelWarn; //
NSString *buylyKey = @"8627948559"; // isEnterprise == NO ? @"5334684d86" : @"d65df59a68";
NSString *buylyKey = @"8627948559";
[Bugly startWithAppId:buylyKey config:config];
#endif
}
@@ -121,7 +122,7 @@ UIKIT_EXTERN NSString * adImageName;
- (void)configShareSDK {
[PILineLoginManager registerLine];
// [PILineLoginManager registerLine];
[ShareSDK registPlatforms:^(SSDKRegister *platformsRegister) {
///faceBook
@@ -145,12 +146,18 @@ UIKIT_EXTERN NSString * adImageName;
NSArray * emojiArray = dic[@"data"];
NSMutableArray * array = [NSMutableArray array];
for (int i = 0; i < emojiArray.count; i++) {
NSDictionary * dic = [emojiArray objectAtIndex:i];
UIImage * image = [UIImage imageNamed:dic[@"file"]];
QEmotion * info = [[QEmotion alloc] init];
info.identifier = dic[@"id"];
NSDictionary * dic = [emojiArray xpSafeObjectAtIndex:i];
if (dic) {
info.displayName = dic[@"tag"];
info.identifier = dic[@"id"];
}
info.image = image;
info.displayName = dic[@"tag"];
[array addObject:info];
}
//

View File

@@ -78,6 +78,10 @@ void qg_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const
});
}];
if (@available(iOS 15, *)) {
[[UITableView appearance] setSectionHeaderTopPadding:0];
}
return YES;
}
@@ -144,16 +148,16 @@ void qg_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const
ATTrackingManagerAuthorizationStatus status = ATTrackingManager.trackingAuthorizationStatus;
switch (status) {
case ATTrackingManagerAuthorizationStatusDenied:
NSLog(@"用户拒绝IDFA");
// NSLog(@"用户拒绝IDFA");
break;
case ATTrackingManagerAuthorizationStatusAuthorized:
NSLog(@"用户允许IDFA");
// NSLog(@"用户允许IDFA");
break;
case ATTrackingManagerAuthorizationStatusNotDetermined: {
NSLog(@"用户未做选择或未弹窗IDFA");
// NSLog(@"用户未做选择或未弹窗IDFA");
//1app
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
NSLog(@"app追踪IDFA权限%lu",(unsigned long)status);
// NSLog(@"app追踪IDFA权限%lu",(unsigned long)status);
}];
}
break;
@@ -280,7 +284,7 @@ void qg_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const
error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
// NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
@@ -312,7 +316,7 @@ void qg_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
// NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@@ -9,7 +9,7 @@
"scale" : "2x"
},
{
"filename" : "椭圆 5@3x.png",
"filename" : "1@3x (1).png",
"idiom" : "universal",
"scale" : "3x"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -9,7 +9,7 @@
"scale" : "2x"
},
{
"filename" : "容器 8156@3x.png",
"filename" : "椭圆 6@3x.png",
"idiom" : "universal",
"scale" : "3x"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

View File

@@ -5,11 +5,11 @@
"scale" : "1x"
},
{
"filename" : "room_sailiing_enter.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "1@3x.png",
"idiom" : "universal",
"scale" : "3x"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 KiB

View File

@@ -1,7 +1,6 @@
{
"images" : [
{
"filename" : "room_menu_gift_first_recharge_ar.png",
"idiom" : "universal",
"scale" : "1x"
},
@@ -10,6 +9,7 @@
"scale" : "2x"
},
{
"filename" : "1@3x (2).png",
"idiom" : "universal",
"scale" : "3x"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@@ -5,12 +5,11 @@
"scale" : "1x"
},
{
"filename" : "room_sailing_bg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "room_sailing_bg@3x.png",
"filename" : "1@3x (1).png",
"idiom" : "universal",
"scale" : "3x"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -5,12 +5,11 @@
"scale" : "1x"
},
{
"filename" : "room_sailing_add@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "room_sailing_add@3x.png",
"filename" : "4cc1494e9e1f9153726ebc7fb2d5d12ef75635b53fe46-nx2IAX_fw480@3x.png",
"idiom" : "universal",
"scale" : "3x"
}

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: 185 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

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: 4.5 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "框-拷贝1@3x (2).png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "框-拷贝1@3x (1).png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 B

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: 199 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "切图 25@3x (1).png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "切图 68@3x (3).png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

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: 2.4 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 B

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "切图 68@3x (2).png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "切图 68@3x (1).png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "容器@3x (4).png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

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: 1.1 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

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: 6.0 KiB

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" : "切图 31@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

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

Some files were not shown because too many files have changed in this diff Show More