chore: Initial clean commit
- Removed YuMi/Library/ (138 MB, not tracked) - Removed YuMi/Resources/ (23 MB, not tracked) - Removed old version assets (566 files, not tracked) - Excluded Pods/, xcuserdata/ and other build artifacts - Clean repository optimized for company server deployment
This commit is contained in:
322
issues/scripts/1_rename_classes.sh
Executable file
322
issues/scripts/1_rename_classes.sh
Executable file
@@ -0,0 +1,322 @@
|
||||
#!/bin/bash
|
||||
# 类名批量替换脚本
|
||||
# 用法: ./1_rename_classes.sh
|
||||
|
||||
set -e # 遇到错误立即退出
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN}白牌项目 - 类名批量替换工具${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
|
||||
# 项目根目录
|
||||
PROJECT_ROOT="/Users/edwinqqq/Local/Company Projects/peko-ios"
|
||||
YUMI_DIR="$PROJECT_ROOT/YuMi"
|
||||
|
||||
# 备份提醒
|
||||
echo -e "${YELLOW}⚠️ 重要:此脚本会修改大量文件!${NC}"
|
||||
echo -e "${YELLOW}请确保你已经:${NC}"
|
||||
echo -e "${YELLOW}1. 提交了所有未保存的更改${NC}"
|
||||
echo -e "${YELLOW}2. 创建了新的 Git 分支${NC}"
|
||||
echo -e "${YELLOW}3. 做好了回滚准备${NC}"
|
||||
echo ""
|
||||
read -p "确认继续?(y/N): " confirm
|
||||
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
|
||||
echo "已取消操作"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 第一步:定义新前缀
|
||||
# ==========================================
|
||||
echo ""
|
||||
echo -e "${GREEN}[Step 1] 请输入新的类名前缀${NC}"
|
||||
echo ""
|
||||
|
||||
read -p "YM 替换为 (例如: LK): " NEW_PREFIX_YM
|
||||
read -p "XP 替换为 (例如: SL): " NEW_PREFIX_XP
|
||||
read -p "PI 替换为 (例如: MT): " NEW_PREFIX_PI
|
||||
read -p "Moli 替换为 (例如: Nova): " NEW_PREFIX_MOLI
|
||||
read -p "MS 替换为 (例如: AS): " NEW_PREFIX_MS
|
||||
|
||||
# 验证输入
|
||||
if [[ -z "$NEW_PREFIX_YM" || -z "$NEW_PREFIX_XP" || -z "$NEW_PREFIX_PI" || -z "$NEW_PREFIX_MOLI" || -z "$NEW_PREFIX_MS" ]]; then
|
||||
echo -e "${RED}❌ 错误:所有前缀都必须输入${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}替换映射:${NC}"
|
||||
echo " YM → $NEW_PREFIX_YM"
|
||||
echo " XP → $NEW_PREFIX_XP"
|
||||
echo " PI → $NEW_PREFIX_PI"
|
||||
echo " Moli → $NEW_PREFIX_MOLI"
|
||||
echo " MS → $NEW_PREFIX_MS"
|
||||
echo ""
|
||||
read -p "确认以上映射正确?(y/N): " confirm_mapping
|
||||
if [[ "$confirm_mapping" != "y" && "$confirm_mapping" != "Y" ]]; then
|
||||
echo "已取消操作"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 第二步:创建临时白名单(不应该被替换的字符串)
|
||||
# ==========================================
|
||||
echo ""
|
||||
echo -e "${GREEN}[Step 2] 扫描可能误伤的字符串常量...${NC}"
|
||||
|
||||
# 创建临时目录
|
||||
TEMP_DIR="$PROJECT_ROOT/issues/temp_rename"
|
||||
mkdir -p "$TEMP_DIR"
|
||||
|
||||
# 查找所有包含旧前缀的字符串常量
|
||||
grep -rn "\".*YM.*\"" "$YUMI_DIR" --include="*.m" --include="*.h" > "$TEMP_DIR/string_constants_YM.txt" 2>/dev/null || true
|
||||
grep -rn "\".*XP.*\"" "$YUMI_DIR" --include="*.m" --include="*.h" > "$TEMP_DIR/string_constants_XP.txt" 2>/dev/null || true
|
||||
grep -rn "\".*Moli.*\"" "$YUMI_DIR" --include="*.m" --include="*.h" > "$TEMP_DIR/string_constants_Moli.txt" 2>/dev/null || true
|
||||
|
||||
echo -e "${YELLOW}找到的字符串常量已保存到:${NC}"
|
||||
echo " $TEMP_DIR/string_constants_*.txt"
|
||||
echo -e "${YELLOW}请检查这些文件,确保没有不应该被替换的内容${NC}"
|
||||
echo ""
|
||||
read -p "检查完成后按回车继续..."
|
||||
|
||||
# ==========================================
|
||||
# 第三步:替换文件内容
|
||||
# ==========================================
|
||||
echo ""
|
||||
echo -e "${GREEN}[Step 3] 开始替换文件内容...${NC}"
|
||||
|
||||
# 函数:替换文件内容
|
||||
replace_in_files() {
|
||||
local old_prefix=$1
|
||||
local new_prefix=$2
|
||||
local file_count=0
|
||||
|
||||
echo " 替换 $old_prefix → $new_prefix ..."
|
||||
|
||||
# 查找所有 .h 和 .m 文件
|
||||
while IFS= read -r file; do
|
||||
# 跳过 Pods 目录
|
||||
if [[ "$file" == *"/Pods/"* ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# 执行替换(使用 Perl 的正则,更精确)
|
||||
# 只替换类名、方法名、属性名,不替换字符串字面量
|
||||
perl -i -pe "s/\b${old_prefix}([A-Z][a-zA-Z0-9]*)/${new_prefix}\1/g" "$file"
|
||||
((file_count++))
|
||||
|
||||
# 显示进度
|
||||
if ((file_count % 50 == 0)); then
|
||||
echo " 已处理 $file_count 个文件..."
|
||||
fi
|
||||
done < <(find "$YUMI_DIR" -type f \( -name "*.h" -o -name "*.m" \))
|
||||
|
||||
echo -e "${GREEN} ✓ 完成!共处理 $file_count 个文件${NC}"
|
||||
}
|
||||
|
||||
# 依次替换各个前缀
|
||||
replace_in_files "YM" "$NEW_PREFIX_YM"
|
||||
replace_in_files "XP" "$NEW_PREFIX_XP"
|
||||
replace_in_files "PI" "$NEW_PREFIX_PI"
|
||||
replace_in_files "Moli" "$NEW_PREFIX_MOLI"
|
||||
replace_in_files "MS" "$NEW_PREFIX_MS"
|
||||
|
||||
# 特殊处理:YUMI(全大写)
|
||||
echo " 替换 YUMI → ${NEW_PREFIX_YM}I ..."
|
||||
find "$YUMI_DIR" -type f \( -name "*.h" -o -name "*.m" \) -exec sed -i '' "s/YUMI/${NEW_PREFIX_YM}I/g" {} +
|
||||
|
||||
# ==========================================
|
||||
# 第四步:重命名文件
|
||||
# ==========================================
|
||||
echo ""
|
||||
echo -e "${GREEN}[Step 4] 重命名文件...${NC}"
|
||||
|
||||
rename_files() {
|
||||
local old_prefix=$1
|
||||
local new_prefix=$2
|
||||
local file_count=0
|
||||
|
||||
echo " 重命名 ${old_prefix}* → ${new_prefix}* ..."
|
||||
|
||||
# 查找所有以旧前缀开头的文件
|
||||
find "$YUMI_DIR" -type f \( -name "${old_prefix}*.h" -o -name "${old_prefix}*.m" \) | while read file; do
|
||||
# 跳过 Pods 目录
|
||||
if [[ "$file" == *"/Pods/"* ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
dir=$(dirname "$file")
|
||||
filename=$(basename "$file")
|
||||
newname=$(echo "$filename" | sed "s/^${old_prefix}/${new_prefix}/")
|
||||
|
||||
if [[ "$filename" != "$newname" ]]; then
|
||||
mv "$file" "$dir/$newname"
|
||||
((file_count++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${GREEN} ✓ 完成!共重命名 $file_count 个文件${NC}"
|
||||
}
|
||||
|
||||
rename_files "YM" "$NEW_PREFIX_YM"
|
||||
rename_files "XP" "$NEW_PREFIX_XP"
|
||||
rename_files "PI" "$NEW_PREFIX_PI"
|
||||
rename_files "Moli" "$NEW_PREFIX_MOLI"
|
||||
rename_files "MS" "$NEW_PREFIX_MS"
|
||||
|
||||
# ==========================================
|
||||
# 第五步:重命名文件夹
|
||||
# ==========================================
|
||||
echo ""
|
||||
echo -e "${GREEN}[Step 5] 重命名模块文件夹...${NC}"
|
||||
|
||||
cd "$YUMI_DIR/Modules"
|
||||
|
||||
# 重命名模块文件夹
|
||||
if [ -d "YMLogin" ]; then
|
||||
mv "YMLogin" "${NEW_PREFIX_YM}Login"
|
||||
echo " ✓ YMLogin → ${NEW_PREFIX_YM}Login"
|
||||
fi
|
||||
|
||||
if [ -d "YMMessage" ]; then
|
||||
mv "YMMessage" "${NEW_PREFIX_YM}Message"
|
||||
echo " ✓ YMMessage → ${NEW_PREFIX_YM}Message"
|
||||
fi
|
||||
|
||||
if [ -d "YMMine" ]; then
|
||||
mv "YMMine" "${NEW_PREFIX_YM}Mine"
|
||||
echo " ✓ YMMine → ${NEW_PREFIX_YM}Mine"
|
||||
fi
|
||||
|
||||
if [ -d "YMMonents" ]; then
|
||||
mv "YMMonents" "${NEW_PREFIX_YM}Monents"
|
||||
echo " ✓ YMMonents → ${NEW_PREFIX_YM}Monents"
|
||||
fi
|
||||
|
||||
if [ -d "YMNewHome" ]; then
|
||||
mv "YMNewHome" "${NEW_PREFIX_YM}NewHome"
|
||||
echo " ✓ YMNewHome → ${NEW_PREFIX_YM}NewHome"
|
||||
fi
|
||||
|
||||
if [ -d "YMRoom" ]; then
|
||||
mv "YMRoom" "${NEW_PREFIX_YM}Room"
|
||||
echo " ✓ YMRoom → ${NEW_PREFIX_YM}Room"
|
||||
fi
|
||||
|
||||
if [ -d "YMTabbar" ]; then
|
||||
mv "YMTabbar" "${NEW_PREFIX_YM}Tabbar"
|
||||
echo " ✓ YMTabbar → ${NEW_PREFIX_YM}Tabbar"
|
||||
fi
|
||||
|
||||
if [ -d "YMRTC" ]; then
|
||||
mv "YMRTC" "${NEW_PREFIX_YM}RTC"
|
||||
echo " ✓ YMRTC → ${NEW_PREFIX_YM}RTC"
|
||||
fi
|
||||
|
||||
if [ -d "YMWeb" ]; then
|
||||
mv "YMWeb" "${NEW_PREFIX_YM}Web"
|
||||
echo " ✓ YMWeb → ${NEW_PREFIX_YM}Web"
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 第六步:更新 Xcode 项目文件引用
|
||||
# ==========================================
|
||||
echo ""
|
||||
echo -e "${GREEN}[Step 6] 更新 Xcode 项目文件引用...${NC}"
|
||||
|
||||
XCODEPROJ="$PROJECT_ROOT/YuMi.xcodeproj/project.pbxproj"
|
||||
|
||||
if [ -f "$XCODEPROJ" ]; then
|
||||
# 备份原文件
|
||||
cp "$XCODEPROJ" "$XCODEPROJ.backup"
|
||||
|
||||
# 替换项目文件中的引用
|
||||
sed -i '' "s/YM/${NEW_PREFIX_YM}/g" "$XCODEPROJ"
|
||||
sed -i '' "s/XP/${NEW_PREFIX_XP}/g" "$XCODEPROJ"
|
||||
sed -i '' "s/PI/${NEW_PREFIX_PI}/g" "$XCODEPROJ"
|
||||
sed -i '' "s/Moli/${NEW_PREFIX_MOLI}/g" "$XCODEPROJ"
|
||||
sed -i '' "s/MS/${NEW_PREFIX_MS}/g" "$XCODEPROJ"
|
||||
sed -i '' "s/YUMI/${NEW_PREFIX_YM}I/g" "$XCODEPROJ"
|
||||
|
||||
echo -e "${GREEN} ✓ Xcode 项目文件已更新${NC}"
|
||||
else
|
||||
echo -e "${RED} ⚠️ 未找到 Xcode 项目文件${NC}"
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 第七步:生成替换报告
|
||||
# ==========================================
|
||||
echo ""
|
||||
echo -e "${GREEN}[Step 7] 生成替换报告...${NC}"
|
||||
|
||||
REPORT_FILE="$TEMP_DIR/rename_report.txt"
|
||||
|
||||
cat > "$REPORT_FILE" <<EOF
|
||||
========================================
|
||||
类名批量替换报告
|
||||
========================================
|
||||
执行时间: $(date)
|
||||
|
||||
替换映射:
|
||||
YM → $NEW_PREFIX_YM
|
||||
XP → $NEW_PREFIX_XP
|
||||
PI → $NEW_PREFIX_PI
|
||||
Moli → $NEW_PREFIX_MOLI
|
||||
MS → $NEW_PREFIX_MS
|
||||
|
||||
已重命名的模块文件夹:
|
||||
YMLogin → ${NEW_PREFIX_YM}Login
|
||||
YMMessage → ${NEW_PREFIX_YM}Message
|
||||
YMMine → ${NEW_PREFIX_YM}Mine
|
||||
YMMonents → ${NEW_PREFIX_YM}Monents
|
||||
YMNewHome → ${NEW_PREFIX_YM}NewHome
|
||||
YMRoom → ${NEW_PREFIX_YM}Room
|
||||
YMTabbar → ${NEW_PREFIX_YM}Tabbar
|
||||
YMRTC → ${NEW_PREFIX_YM}RTC
|
||||
YMWeb → ${NEW_PREFIX_YM}Web
|
||||
|
||||
注意事项:
|
||||
1. 请在 Xcode 中重新打开项目
|
||||
2. Clean Build Folder (Shift + Cmd + K)
|
||||
3. 尝试编译,查看是否有错误
|
||||
4. 检查 Podfile 是否需要更新
|
||||
5. 运行完整的功能测试
|
||||
|
||||
如果出现问题,可以通过 Git 回滚:
|
||||
git reset --hard HEAD
|
||||
git clean -fd
|
||||
|
||||
备份文件位置:
|
||||
$XCODEPROJ.backup
|
||||
========================================
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN} ✓ 报告已生成:$REPORT_FILE${NC}"
|
||||
cat "$REPORT_FILE"
|
||||
|
||||
# ==========================================
|
||||
# 完成
|
||||
# ==========================================
|
||||
echo ""
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN}✅ 类名批量替换完成!${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}下一步操作:${NC}"
|
||||
echo "1. 在 Xcode 中打开项目(可能需要关闭后重新打开)"
|
||||
echo "2. Product → Clean Build Folder"
|
||||
echo "3. 尝试编译项目,解决任何编译错误"
|
||||
echo "4. 运行 2_rename_assets.py 脚本替换图片资源"
|
||||
echo ""
|
||||
echo -e "${YELLOW}查看详细报告:${NC}"
|
||||
echo " cat $REPORT_FILE"
|
||||
echo ""
|
||||
|
||||
|
||||
|
336
issues/scripts/2_rename_assets.py
Executable file
336
issues/scripts/2_rename_assets.py
Executable file
@@ -0,0 +1,336 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
图片资源批量处理脚本
|
||||
用法: python3 2_rename_assets.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import hashlib
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Set
|
||||
import re
|
||||
|
||||
# 颜色输出
|
||||
class Colors:
|
||||
GREEN = '\033[92m'
|
||||
YELLOW = '\033[93m'
|
||||
RED = '\033[91m'
|
||||
BLUE = '\033[94m'
|
||||
END = '\033[0m'
|
||||
|
||||
# 配置
|
||||
PROJECT_ROOT = "/Users/edwinqqq/Local/Company Projects/peko-ios"
|
||||
ASSETS_DIR = os.path.join(PROJECT_ROOT, "YuMi/Assets.xcassets")
|
||||
SOURCE_DIR = os.path.join(PROJECT_ROOT, "YuMi")
|
||||
BACKUP_DIR = os.path.join(PROJECT_ROOT, "issues/temp_rename/assets_backup")
|
||||
MAPPING_FILE = os.path.join(PROJECT_ROOT, "issues/temp_rename/asset_name_mapping.json")
|
||||
|
||||
# 存储映射关系
|
||||
asset_mapping: Dict[str, str] = {}
|
||||
|
||||
def print_header(text):
|
||||
"""打印标题"""
|
||||
print(f"\n{Colors.GREEN}{'='*50}{Colors.END}")
|
||||
print(f"{Colors.GREEN}{text}{Colors.END}")
|
||||
print(f"{Colors.GREEN}{'='*50}{Colors.END}\n")
|
||||
|
||||
def print_info(text):
|
||||
"""打印信息"""
|
||||
print(f"{Colors.BLUE}ℹ️ {text}{Colors.END}")
|
||||
|
||||
def print_warning(text):
|
||||
"""打印警告"""
|
||||
print(f"{Colors.YELLOW}⚠️ {text}{Colors.END}")
|
||||
|
||||
def print_error(text):
|
||||
"""打印错误"""
|
||||
print(f"{Colors.RED}❌ {text}{Colors.END}")
|
||||
|
||||
def print_success(text):
|
||||
"""打印成功"""
|
||||
print(f"{Colors.GREEN}✓ {text}{Colors.END}")
|
||||
|
||||
def generate_new_name(old_name: str, index: int) -> str:
|
||||
"""
|
||||
生成新的资源名称
|
||||
策略:使用语义化的随机名称,而不是完全随机
|
||||
"""
|
||||
# 提取原名称中的关键信息
|
||||
parts = old_name.lower().split('_')
|
||||
|
||||
# 常见的UI元素映射
|
||||
element_map = {
|
||||
'icon': ['symbol', 'mark', 'sign', 'emblem'],
|
||||
'button': ['btn', 'action', 'control', 'press'],
|
||||
'background': ['bg', 'backdrop', 'layer', 'base'],
|
||||
'avatar': ['profile', 'face', 'user', 'photo'],
|
||||
'banner': ['header', 'top', 'promo', 'ad'],
|
||||
'gift': ['present', 'reward', 'prize', 'bonus'],
|
||||
'room': ['space', 'zone', 'area', 'hall'],
|
||||
'home': ['main', 'index', 'start', 'feed'],
|
||||
'message': ['msg', 'chat', 'talk', 'dialog'],
|
||||
'mine': ['profile', 'me', 'personal', 'user'],
|
||||
'login': ['signin', 'auth', 'entry', 'access'],
|
||||
'arrow': ['pointer', 'indicator', 'direction', 'nav'],
|
||||
'close': ['dismiss', 'exit', 'cancel', 'remove'],
|
||||
'back': ['return', 'previous', 'prev', 'retreat'],
|
||||
'next': ['forward', 'proceed', 'continue', 'advance'],
|
||||
'play': ['start', 'run', 'launch', 'begin'],
|
||||
'pause': ['stop', 'halt', 'freeze', 'wait'],
|
||||
'more': ['additional', 'extra', 'plus', 'expand'],
|
||||
}
|
||||
|
||||
# 尝试替换关键词
|
||||
new_parts = []
|
||||
for part in parts:
|
||||
replaced = False
|
||||
for key, alternatives in element_map.items():
|
||||
if key in part:
|
||||
# 使用 index 作为种子选择一个替代词
|
||||
alt_index = (index + hash(part)) % len(alternatives)
|
||||
new_part = part.replace(key, alternatives[alt_index])
|
||||
new_parts.append(new_part)
|
||||
replaced = True
|
||||
break
|
||||
if not replaced:
|
||||
# 保留数字和常见后缀
|
||||
if part.isdigit() or part in ['selected', 'normal', 'disabled', 'active', 'inactive']:
|
||||
new_parts.append(part)
|
||||
else:
|
||||
# 使用简单的字符替换
|
||||
new_part = ''.join([chr((ord(c) - 97 + 13) % 26 + 97) if c.isalpha() else c for c in part])
|
||||
new_parts.append(new_part)
|
||||
|
||||
new_name = '_'.join(new_parts)
|
||||
|
||||
# 如果生成的名称和原名称一样,添加一个后缀
|
||||
if new_name == old_name:
|
||||
new_name = f"asset_{hashlib.md5(old_name.encode()).hexdigest()[:8]}"
|
||||
|
||||
return new_name
|
||||
|
||||
def backup_assets():
|
||||
"""备份原始资源"""
|
||||
print_header("第1步:备份原始资源")
|
||||
|
||||
if os.path.exists(BACKUP_DIR):
|
||||
print_warning("备份目录已存在,将覆盖...")
|
||||
shutil.rmtree(BACKUP_DIR)
|
||||
|
||||
print_info(f"正在备份 {ASSETS_DIR} 到 {BACKUP_DIR} ...")
|
||||
shutil.copytree(ASSETS_DIR, BACKUP_DIR)
|
||||
print_success(f"备份完成!")
|
||||
|
||||
def scan_imagesets() -> List[str]:
|
||||
"""扫描所有 .imageset 目录"""
|
||||
print_header("第2步:扫描图片资源")
|
||||
|
||||
imagesets = []
|
||||
for root, dirs, files in os.walk(ASSETS_DIR):
|
||||
for dir_name in dirs:
|
||||
if dir_name.endswith('.imageset'):
|
||||
full_path = os.path.join(root, dir_name)
|
||||
imagesets.append(full_path)
|
||||
|
||||
print_success(f"找到 {len(imagesets)} 个图片资源")
|
||||
return imagesets
|
||||
|
||||
def rename_imageset(imageset_path: str, index: int) -> tuple:
|
||||
"""
|
||||
重命名单个 imageset
|
||||
返回 (old_name, new_name)
|
||||
"""
|
||||
dir_name = os.path.basename(imageset_path)
|
||||
old_name = dir_name.replace('.imageset', '')
|
||||
|
||||
# 跳过 AppIcon
|
||||
if 'AppIcon' in old_name:
|
||||
print_info(f"跳过 AppIcon: {old_name}")
|
||||
return (old_name, old_name)
|
||||
|
||||
# 生成新名称
|
||||
new_name = generate_new_name(old_name, index)
|
||||
new_dir_name = f"{new_name}.imageset"
|
||||
new_path = os.path.join(os.path.dirname(imageset_path), new_dir_name)
|
||||
|
||||
# 重命名目录
|
||||
try:
|
||||
os.rename(imageset_path, new_path)
|
||||
|
||||
# 更新 Contents.json
|
||||
contents_json = os.path.join(new_path, 'Contents.json')
|
||||
if os.path.exists(contents_json):
|
||||
with open(contents_json, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# 更新图片文件名引用
|
||||
for image in data.get('images', []):
|
||||
if 'filename' in image:
|
||||
old_filename = image['filename']
|
||||
if old_name in old_filename:
|
||||
new_filename = old_filename.replace(old_name, new_name)
|
||||
# 重命名实际的图片文件
|
||||
old_file_path = os.path.join(new_path, old_filename)
|
||||
new_file_path = os.path.join(new_path, new_filename)
|
||||
if os.path.exists(old_file_path):
|
||||
os.rename(old_file_path, new_file_path)
|
||||
image['filename'] = new_filename
|
||||
|
||||
# 写回 Contents.json
|
||||
with open(contents_json, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
return (old_name, new_name)
|
||||
|
||||
except Exception as e:
|
||||
print_error(f"重命名失败 {old_name}: {str(e)}")
|
||||
return (old_name, old_name)
|
||||
|
||||
def rename_all_imagesets():
|
||||
"""批量重命名所有图片资源"""
|
||||
print_header("第3步:批量重命名图片资源")
|
||||
|
||||
imagesets = scan_imagesets()
|
||||
total = len(imagesets)
|
||||
success_count = 0
|
||||
|
||||
for index, imageset_path in enumerate(imagesets, 1):
|
||||
old_name, new_name = rename_imageset(imageset_path, index)
|
||||
|
||||
if old_name != new_name:
|
||||
asset_mapping[old_name] = new_name
|
||||
success_count += 1
|
||||
|
||||
# 显示进度
|
||||
if index % 100 == 0:
|
||||
print_info(f"进度: {index}/{total} ({index*100//total}%)")
|
||||
|
||||
print_success(f"完成!成功重命名 {success_count} 个资源")
|
||||
|
||||
# 保存映射关系
|
||||
os.makedirs(os.path.dirname(MAPPING_FILE), exist_ok=True)
|
||||
with open(MAPPING_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(asset_mapping, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print_success(f"映射关系已保存到: {MAPPING_FILE}")
|
||||
|
||||
def update_code_references():
|
||||
"""更新代码中的图片资源引用"""
|
||||
print_header("第4步:更新代码引用")
|
||||
|
||||
print_warning("这一步需要手动处理,因为图片引用方式多样")
|
||||
print_info("常见的引用方式:")
|
||||
print(" 1. [UIImage imageNamed:@\"old_name\"]")
|
||||
print(" 2. UIImage(named: \"old_name\")")
|
||||
print(" 3. @\"old_name\"")
|
||||
print("")
|
||||
|
||||
# 生成替换脚本
|
||||
replace_script = os.path.join(PROJECT_ROOT, "issues/temp_rename/replace_image_refs.sh")
|
||||
|
||||
with open(replace_script, 'w') as f:
|
||||
f.write("#!/bin/bash\n")
|
||||
f.write("# 自动生成的图片引用替换脚本\n\n")
|
||||
f.write(f"SOURCE_DIR=\"{SOURCE_DIR}\"\n\n")
|
||||
|
||||
for old_name, new_name in asset_mapping.items():
|
||||
# 转义特殊字符
|
||||
old_escaped = old_name.replace('"', '\\"')
|
||||
new_escaped = new_name.replace('"', '\\"')
|
||||
|
||||
f.write(f"# {old_name} → {new_name}\n")
|
||||
f.write(f"find \"$SOURCE_DIR\" -type f \\( -name \"*.m\" -o -name \"*.h\" -o -name \"*.swift\" \\) -exec sed -i '' 's/@\"{old_escaped}\"/@\"{new_escaped}\"/g' {{}} +\n")
|
||||
f.write(f"find \"$SOURCE_DIR\" -type f \\( -name \"*.m\" -o -name \"*.h\" -o -name \"*.swift\" \\) -exec sed -i '' 's/\"{old_escaped}\"/\"{new_escaped}\"/g' {{}} +\n\n")
|
||||
|
||||
os.chmod(replace_script, 0o755)
|
||||
print_success(f"替换脚本已生成: {replace_script}")
|
||||
print_warning("请手动执行该脚本进行代码替换")
|
||||
|
||||
def generate_report():
|
||||
"""生成替换报告"""
|
||||
print_header("第5步:生成报告")
|
||||
|
||||
report_file = os.path.join(PROJECT_ROOT, "issues/temp_rename/assets_rename_report.txt")
|
||||
|
||||
with open(report_file, 'w', encoding='utf-8') as f:
|
||||
f.write("=" * 60 + "\n")
|
||||
f.write("图片资源批量重命名报告\n")
|
||||
f.write("=" * 60 + "\n\n")
|
||||
f.write(f"执行时间: {os.popen('date').read()}\n")
|
||||
f.write(f"总共处理: {len(asset_mapping)} 个资源\n\n")
|
||||
f.write("=" * 60 + "\n")
|
||||
f.write("重命名映射(前50个):\n")
|
||||
f.write("=" * 60 + "\n\n")
|
||||
|
||||
count = 0
|
||||
for old_name, new_name in asset_mapping.items():
|
||||
f.write(f"{old_name:40} → {new_name}\n")
|
||||
count += 1
|
||||
if count >= 50:
|
||||
f.write(f"\n... 还有 {len(asset_mapping) - 50} 个资源\n")
|
||||
break
|
||||
|
||||
f.write("\n" + "=" * 60 + "\n")
|
||||
f.write("下一步操作:\n")
|
||||
f.write("=" * 60 + "\n\n")
|
||||
f.write("1. 执行生成的替换脚本更新代码引用\n")
|
||||
f.write("2. 在 Xcode 中清理并重新构建项目\n")
|
||||
f.write("3. 运行应用,检查所有图片是否正常显示\n")
|
||||
f.write("4. 使用 UI 测试验证关键页面\n\n")
|
||||
f.write("备份位置:\n")
|
||||
f.write(f" {BACKUP_DIR}\n\n")
|
||||
f.write("映射文件:\n")
|
||||
f.write(f" {MAPPING_FILE}\n\n")
|
||||
f.write("替换脚本:\n")
|
||||
f.write(f" {os.path.join(PROJECT_ROOT, 'issues/temp_rename/replace_image_refs.sh')}\n\n")
|
||||
|
||||
print_success(f"报告已生成: {report_file}")
|
||||
|
||||
# 打印简要统计
|
||||
print("")
|
||||
print(f" 📊 总共重命名: {len(asset_mapping)} 个资源")
|
||||
print(f" 📁 备份位置: {BACKUP_DIR}")
|
||||
print(f" 📄 映射文件: {MAPPING_FILE}")
|
||||
print("")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print_header("白牌项目 - 图片资源批量重命名工具")
|
||||
|
||||
# 确认操作
|
||||
print_warning("此脚本将重命名所有图片资源!")
|
||||
print_warning("请确保:")
|
||||
print(" 1. 已经运行了类名替换脚本")
|
||||
print(" 2. 已经提交了所有更改")
|
||||
print(" 3. 已经创建了新的 Git 分支")
|
||||
print("")
|
||||
|
||||
confirm = input("确认继续?(y/N): ")
|
||||
if confirm.lower() != 'y':
|
||||
print("已取消操作")
|
||||
return
|
||||
|
||||
try:
|
||||
# 执行步骤
|
||||
backup_assets()
|
||||
rename_all_imagesets()
|
||||
update_code_references()
|
||||
generate_report()
|
||||
|
||||
print_header("✅ 图片资源批量重命名完成!")
|
||||
print_warning("记得执行生成的替换脚本更新代码引用!")
|
||||
|
||||
except Exception as e:
|
||||
print_error(f"执行过程中出错: {str(e)}")
|
||||
print_warning("你可以从备份恢复:")
|
||||
print(f" rm -rf {ASSETS_DIR}")
|
||||
print(f" cp -r {BACKUP_DIR} {ASSETS_DIR}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
402
issues/scripts/3_theme_color_example.md
Normal file
402
issues/scripts/3_theme_color_example.md
Normal file
@@ -0,0 +1,402 @@
|
||||
# 主题颜色替换方案
|
||||
|
||||
## 📋 当前颜色系统分析
|
||||
|
||||
基于 `DJDKMIMOMColor.h/m` 的分析,当前项目使用的主要颜色:
|
||||
|
||||
### 主色调
|
||||
- **主题色**: `0x9682FF` (紫色)
|
||||
- **强调色**: `0x248CFE` (蓝色)
|
||||
- **强调色1**: `0xBF36FF` (亮紫)
|
||||
- **强调色2**: `0xFB486A` (粉红)
|
||||
|
||||
### 页面颜色
|
||||
- **背景色**: `0xF3F5FA` (浅灰)
|
||||
- **Cell背景**: `0xFFFFFF` (白色)
|
||||
- **正文**: `0x333333` (深灰)
|
||||
- **二级文字**: `0x666666` (中灰)
|
||||
- **三级文字**: `0x999999` (浅灰)
|
||||
|
||||
### 按钮渐变
|
||||
- **确认按钮渐变**: `0xFFA936` → `0x9CB3FF` → `0xFFCB47`
|
||||
- **取消按钮渐变**: `0xF7DDBF` → `0xF7E8C4`
|
||||
|
||||
---
|
||||
|
||||
## 🎨 新配色方案建议
|
||||
|
||||
### 方案A: 活力橙色系(适合年轻社交)
|
||||
|
||||
```objc
|
||||
// 主题色系
|
||||
#define THEME_PRIMARY_COLOR 0xFF6B35 // 活力橙
|
||||
#define THEME_EMPHASIZE_COLOR 0x00A8E8 // 天空蓝
|
||||
#define THEME_EMPHASIZE_COLOR_1 0xFF9F1C // 金橙
|
||||
#define THEME_EMPHASIZE_COLOR_2 0xE71D36 // 玫红
|
||||
|
||||
// 背景色系
|
||||
#define THEME_BG_COLOR 0xF8F9FA // 极浅灰
|
||||
#define THEME_CELL_BG_COLOR 0xFFFFFF // 白色
|
||||
|
||||
// 按钮渐变
|
||||
#define CONFIRM_BTN_START 0xFF6B35 // 橙色
|
||||
#define CONFIRM_BTN_MIDDLE 0xFFB347 // 浅橙
|
||||
#define CONFIRM_BTN_END 0xFFC85B // 金橙
|
||||
```
|
||||
|
||||
### 方案B: 科技蓝绿系(适合专业/科技感)
|
||||
|
||||
```objc
|
||||
// 主题色系
|
||||
#define THEME_PRIMARY_COLOR 0x00D9FF // 科技蓝
|
||||
#define THEME_EMPHASIZE_COLOR 0x00FFB3 // 荧光绿
|
||||
#define THEME_EMPHASIZE_COLOR_1 0x667EEA // 深蓝紫
|
||||
#define THEME_EMPHASIZE_COLOR_2 0xFF6AC1 // 亮粉
|
||||
|
||||
// 背景色系
|
||||
#define THEME_BG_COLOR 0x0D1117 // 深色背景
|
||||
#define THEME_CELL_BG_COLOR 0x161B22 // 卡片深色
|
||||
|
||||
// 按钮渐变
|
||||
#define CONFIRM_BTN_START 0x667EEA // 深蓝
|
||||
#define CONFIRM_BTN_MIDDLE 0x00D9FF // 天蓝
|
||||
#define CONFIRM_BTN_END 0x00FFB3 // 荧光绿
|
||||
```
|
||||
|
||||
### 方案C: 温暖粉紫系(适合女性/浪漫风)
|
||||
|
||||
```objc
|
||||
// 主题色系
|
||||
#define THEME_PRIMARY_COLOR 0xE91E63 // 玫瑰粉
|
||||
#define THEME_EMPHASIZE_COLOR 0x9C27B0 // 深紫
|
||||
#define THEME_EMPHASIZE_COLOR_1 0xF06292 // 浅粉
|
||||
#define THEME_EMPHASIZE_COLOR_2 0xFF4081 // 亮粉
|
||||
|
||||
// 背景色系
|
||||
#define THEME_BG_COLOR 0xFFF0F5 // 淡粉背景
|
||||
#define THEME_CELL_BG_COLOR 0xFFFFFF // 白色
|
||||
|
||||
// 按钮渐变
|
||||
#define CONFIRM_BTN_START 0xE91E63 // 玫瑰粉
|
||||
#define CONFIRM_BTN_MIDDLE 0xF06292 // 浅粉
|
||||
#define CONFIRM_BTN_END 0xFF80AB // 亮粉
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠 实施步骤
|
||||
|
||||
### 步骤1: 创建新的颜色类
|
||||
|
||||
创建新文件 `AppThemeColor.h` 和 `AppThemeColor.m`,替代 `DJDKMIMOMColor`
|
||||
|
||||
```objc
|
||||
// AppThemeColor.h
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AppThemeColor : NSObject
|
||||
|
||||
// 主题色
|
||||
+ (UIColor *)primaryColor;
|
||||
+ (UIColor *)accentColor;
|
||||
+ (UIColor *)highlightColor;
|
||||
+ (UIColor *)warningColor;
|
||||
|
||||
// 背景色
|
||||
+ (UIColor *)backgroundColor;
|
||||
+ (UIColor *)cardBackgroundColor;
|
||||
|
||||
// 文字色
|
||||
+ (UIColor *)primaryTextColor;
|
||||
+ (UIColor *)secondaryTextColor;
|
||||
+ (UIColor *)tertiaryTextColor;
|
||||
|
||||
// 分割线
|
||||
+ (UIColor *)separatorColor;
|
||||
|
||||
// 按钮渐变
|
||||
+ (UIColor *)buttonGradientStartColor;
|
||||
+ (UIColor *)buttonGradientMiddleColor;
|
||||
+ (UIColor *)buttonGradientEndColor;
|
||||
|
||||
// TabBar
|
||||
+ (UIColor *)tabBarNormalColor;
|
||||
+ (UIColor *)tabBarSelectedColor;
|
||||
+ (UIColor *)tabBarBackgroundColor;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
```
|
||||
|
||||
```objc
|
||||
// AppThemeColor.m
|
||||
#import "AppThemeColor.h"
|
||||
|
||||
@implementation AppThemeColor
|
||||
|
||||
// 选择方案A: 活力橙色系
|
||||
#define PRIMARY_COLOR 0xFF6B35
|
||||
#define ACCENT_COLOR 0x00A8E8
|
||||
#define HIGHLIGHT_COLOR 0xFF9F1C
|
||||
#define WARNING_COLOR 0xE71D36
|
||||
|
||||
#define BG_COLOR 0xF8F9FA
|
||||
#define CARD_BG_COLOR 0xFFFFFF
|
||||
|
||||
#define PRIMARY_TEXT 0x212529
|
||||
#define SECONDARY_TEXT 0x6C757D
|
||||
#define TERTIARY_TEXT 0xADB5BD
|
||||
|
||||
#define SEPARATOR 0xDEE2E6
|
||||
|
||||
#define BTN_GRADIENT_START 0xFF6B35
|
||||
#define BTN_GRADIENT_MIDDLE 0xFFB347
|
||||
#define BTN_GRADIENT_END 0xFFC85B
|
||||
|
||||
#define TABBAR_NORMAL 0x6C757D
|
||||
#define TABBAR_SELECTED 0xFF6B35
|
||||
#define TABBAR_BG 0xFFFFFF
|
||||
|
||||
// 辅助宏
|
||||
#define ColorFromHex(hex) [UIColor colorWithRed:((float)((hex & 0xFF0000) >> 16))/255.0 \
|
||||
green:((float)((hex & 0xFF00) >> 8))/255.0 \
|
||||
blue:((float)(hex & 0xFF))/255.0 \
|
||||
alpha:1.0]
|
||||
|
||||
+ (UIColor *)primaryColor {
|
||||
return ColorFromHex(PRIMARY_COLOR);
|
||||
}
|
||||
|
||||
+ (UIColor *)accentColor {
|
||||
return ColorFromHex(ACCENT_COLOR);
|
||||
}
|
||||
|
||||
+ (UIColor *)highlightColor {
|
||||
return ColorFromHex(HIGHLIGHT_COLOR);
|
||||
}
|
||||
|
||||
+ (UIColor *)warningColor {
|
||||
return ColorFromHex(WARNING_COLOR);
|
||||
}
|
||||
|
||||
+ (UIColor *)backgroundColor {
|
||||
return ColorFromHex(BG_COLOR);
|
||||
}
|
||||
|
||||
+ (UIColor *)cardBackgroundColor {
|
||||
return ColorFromHex(CARD_BG_COLOR);
|
||||
}
|
||||
|
||||
+ (UIColor *)primaryTextColor {
|
||||
return ColorFromHex(PRIMARY_TEXT);
|
||||
}
|
||||
|
||||
+ (UIColor *)secondaryTextColor {
|
||||
return ColorFromHex(SECONDARY_TEXT);
|
||||
}
|
||||
|
||||
+ (UIColor *)tertiaryTextColor {
|
||||
return ColorFromHex(TERTIARY_TEXT);
|
||||
}
|
||||
|
||||
+ (UIColor *)separatorColor {
|
||||
return ColorFromHex(SEPARATOR);
|
||||
}
|
||||
|
||||
+ (UIColor *)buttonGradientStartColor {
|
||||
return ColorFromHex(BTN_GRADIENT_START);
|
||||
}
|
||||
|
||||
+ (UIColor *)buttonGradientMiddleColor {
|
||||
return ColorFromHex(BTN_GRADIENT_MIDDLE);
|
||||
}
|
||||
|
||||
+ (UIColor *)buttonGradientEndColor {
|
||||
return ColorFromHex(BTN_GRADIENT_END);
|
||||
}
|
||||
|
||||
+ (UIColor *)tabBarNormalColor {
|
||||
return ColorFromHex(TABBAR_NORMAL);
|
||||
}
|
||||
|
||||
+ (UIColor *)tabBarSelectedColor {
|
||||
return ColorFromHex(TABBAR_SELECTED);
|
||||
}
|
||||
|
||||
+ (UIColor *)tabBarBackgroundColor {
|
||||
return ColorFromHex(TABBAR_BG);
|
||||
}
|
||||
|
||||
@end
|
||||
```
|
||||
|
||||
### 步骤2: 全局替换颜色引用
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 替换所有对旧颜色类的引用
|
||||
|
||||
SOURCE_DIR="/Users/edwinqqq/Local/Company Projects/peko-ios/YuMi"
|
||||
|
||||
# 1. 替换 import 语句
|
||||
find "$SOURCE_DIR" -type f \( -name "*.m" -o -name "*.h" \) \
|
||||
-exec sed -i '' 's/#import "DJDKMIMOMColor.h"/#import "AppThemeColor.h"/g' {} +
|
||||
|
||||
# 2. 替换方法调用
|
||||
find "$SOURCE_DIR" -type f \( -name "*.m" -o -name "*.h" \) \
|
||||
-exec sed -i '' 's/\[DJDKMIMOMColor appMainColor\]/[AppThemeColor primaryColor]/g' {} +
|
||||
|
||||
find "$SOURCE_DIR" -type f \( -name "*.m" -o -name "*.h" \) \
|
||||
-exec sed -i '' 's/\[DJDKMIMOMColor appEmphasizeColor\]/[AppThemeColor accentColor]/g' {} +
|
||||
|
||||
find "$SOURCE_DIR" -type f \( -name "*.m" -o -name "*.h" \) \
|
||||
-exec sed -i '' 's/\[DJDKMIMOMColor appBackgroundColor\]/[AppThemeColor backgroundColor]/g' {} +
|
||||
|
||||
find "$SOURCE_DIR" -type f \( -name "*.m" -o -name "*.h" \) \
|
||||
-exec sed -i '' 's/\[DJDKMIMOMColor mainTextColor\]/[AppThemeColor primaryTextColor]/g' {} +
|
||||
|
||||
find "$SOURCE_DIR" -type f \( -name "*.m" -o -name "*.h" \) \
|
||||
-exec sed -i '' 's/\[DJDKMIMOMColor secondTextColor\]/[AppThemeColor secondaryTextColor]/g' {} +
|
||||
|
||||
# ... 其他颜色方法的替换
|
||||
```
|
||||
|
||||
### 步骤3: 替换硬编码的颜色值
|
||||
|
||||
```bash
|
||||
# 查找所有硬编码的颜色值(hex)
|
||||
grep -rn "0x9682FF" "$SOURCE_DIR" --include="*.m" --include="*.h"
|
||||
grep -rn "UIColorFromRGB(0x" "$SOURCE_DIR" --include="*.m" --include="*.h" | head -50
|
||||
|
||||
# 手动替换这些硬编码值为新的颜色类方法
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 关键替换点
|
||||
|
||||
### TabBar 颜色
|
||||
```objc
|
||||
// 旧代码
|
||||
self.tabBarItem.imageInsets = UIEdgeInsetsMake(0, 0, 0, 0);
|
||||
self.tabBarItem.image = [[UIImage imageNamed:@"icon_home"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
|
||||
self.tabBarItem.selectedImage = [[UIImage imageNamed:@"icon_home_selected"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
|
||||
|
||||
// 新代码(添加颜色渲染)
|
||||
UIImage *normalImage = [[UIImage imageNamed:@"icon_home"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
UIImage *selectedImage = [[UIImage imageNamed:@"icon_home_selected"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
|
||||
self.tabBarItem.image = normalImage;
|
||||
self.tabBarItem.selectedImage = selectedImage;
|
||||
|
||||
// 在 TabBar 设置中
|
||||
[self.tabBar setTintColor:[AppThemeColor tabBarSelectedColor]];
|
||||
[self.tabBar setUnselectedItemTintColor:[AppThemeColor tabBarNormalColor]];
|
||||
```
|
||||
|
||||
### 渐变按钮
|
||||
```objc
|
||||
// 旧代码
|
||||
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
|
||||
gradientLayer.colors = @[(id)[DJDKMIMOMColor confirmButtonGradientStartColor].CGColor,
|
||||
(id)[DJDKMIMOMColor confirmButtonGradientEndColor].CGColor];
|
||||
|
||||
// 新代码
|
||||
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
|
||||
gradientLayer.colors = @[(id)[AppThemeColor buttonGradientStartColor].CGColor,
|
||||
(id)[AppThemeColor buttonGradientMiddleColor].CGColor,
|
||||
(id)[AppThemeColor buttonGradientEndColor].CGColor];
|
||||
gradientLayer.locations = @[@0.0, @0.5, @1.0];
|
||||
```
|
||||
|
||||
### 导航栏颜色
|
||||
```objc
|
||||
// 在 BaseViewController 中
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
// 设置导航栏颜色
|
||||
self.navigationController.navigationBar.barTintColor = [AppThemeColor primaryColor];
|
||||
self.navigationController.navigationBar.tintColor = [UIColor whiteColor];
|
||||
[self.navigationController.navigationBar setTitleTextAttributes:@{
|
||||
NSForegroundColorAttributeName: [UIColor whiteColor],
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:18]
|
||||
}];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 检查清单
|
||||
|
||||
完成颜色替换后,检查以下页面:
|
||||
|
||||
- [ ] 启动页背景色
|
||||
- [ ] 登录页主题色、按钮渐变
|
||||
- [ ] TabBar 选中/未选中颜色
|
||||
- [ ] 导航栏背景色和按钮颜色
|
||||
- [ ] 首页列表背景、卡片背景
|
||||
- [ ] 房间页面背景、控制按钮
|
||||
- [ ] 消息列表颜色
|
||||
- [ ] 个人中心页面主题色
|
||||
- [ ] 弹窗背景和按钮
|
||||
- [ ] 所有渐变效果
|
||||
|
||||
---
|
||||
|
||||
## 🎨 配色测试工具
|
||||
|
||||
使用在线工具预览配色效果:
|
||||
- [Coolors.co](https://coolors.co/) - 配色方案生成器
|
||||
- [Adobe Color](https://color.adobe.com/) - 配色轮盘
|
||||
- [Paletton](https://paletton.com/) - 配色组合工具
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **渐变方向**:改变渐变的起点和终点,让视觉效果完全不同
|
||||
```objc
|
||||
// 旧: 水平渐变
|
||||
gradientLayer.startPoint = CGPointMake(0, 0.5);
|
||||
gradientLayer.endPoint = CGPointMake(1, 0.5);
|
||||
|
||||
// 新: 斜向渐变
|
||||
gradientLayer.startPoint = CGPointMake(0, 0);
|
||||
gradientLayer.endPoint = CGPointMake(1, 1);
|
||||
```
|
||||
|
||||
2. **透明度变化**:适当调整颜色透明度
|
||||
```objc
|
||||
[[AppThemeColor primaryColor] colorWithAlphaComponent:0.8]
|
||||
```
|
||||
|
||||
3. **深色模式适配**(可选):
|
||||
```objc
|
||||
if (@available(iOS 13.0, *)) {
|
||||
return [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
|
||||
if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
|
||||
return ColorFromHex(DARK_MODE_COLOR);
|
||||
}
|
||||
return ColorFromHex(LIGHT_MODE_COLOR);
|
||||
}];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
1. 选择一个配色方案(A/B/C)
|
||||
2. 创建 `AppThemeColor.h/m` 文件
|
||||
3. 运行替换脚本
|
||||
4. 在 Xcode 中编译并查看效果
|
||||
5. 微调颜色值直到满意
|
||||
|
||||
**预计时间**:2-3天
|
||||
|
||||
|
579
issues/scripts/4_tabbar_refactor_guide.md
Normal file
579
issues/scripts/4_tabbar_refactor_guide.md
Normal file
@@ -0,0 +1,579 @@
|
||||
# TabBar 结构改动指南
|
||||
|
||||
## 📋 当前 TabBar 结构分析
|
||||
|
||||
基于 `TabbarViewController.h/m` 的分析,当前项目的 TabBar 包含以下模块:
|
||||
|
||||
### 现有结构(推测)
|
||||
1. **首页** (Home) - `YMNewHome`
|
||||
2. **消息** (Message) - `YMMessage`
|
||||
3. **动态** (Moments) - `YMMonents`
|
||||
4. **我的** (Mine) - `YMMine`
|
||||
|
||||
可能还有一个中间的"发布"按钮(常见于直播/社交应用)。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 改造目标
|
||||
|
||||
通过调整 TabBar 的结构、顺序、样式,让 App 在使用体验和视觉上都和原项目有明显区别。
|
||||
|
||||
---
|
||||
|
||||
## 🔄 改造方案
|
||||
|
||||
### 方案A: 改变顺序
|
||||
|
||||
**原顺序**:首页 → 消息 → [发布] → 动态 → 我的
|
||||
|
||||
**新顺序**:动态 → 首页 → [发布] → 我的 → 消息
|
||||
|
||||
**代码修改**:
|
||||
```objc
|
||||
// TabbarViewController.m
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
// 创建各个 ViewController(顺序改变)
|
||||
NSArray *viewControllers = @[
|
||||
[self createMomentsVC], // 动态(原本第3个,现在第1个)
|
||||
[self createHomeVC], // 首页(原本第1个,现在第2个)
|
||||
[self createBlankVC], // 占位(发布按钮)
|
||||
[self createMineVC], // 我的(原本第4个,现在第4个)
|
||||
[self createMessageVC] // 消息(原本第2个,现在第5个)
|
||||
];
|
||||
|
||||
self.viewControllers = viewControllers;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 方案B: 改变数量(5个→4个)
|
||||
|
||||
移除"动态"Tab,将其整合到首页或消息中。
|
||||
|
||||
**新结构**:首页 → 消息 → 我的 → 设置
|
||||
|
||||
```objc
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
NSArray *viewControllers = @[
|
||||
[self createHomeVC], // 首页
|
||||
[self createMessageVC], // 消息
|
||||
[self createMineVC], // 我的
|
||||
[self createSettingsVC] // 新增:设置
|
||||
];
|
||||
|
||||
self.viewControllers = viewControllers;
|
||||
|
||||
// 移除中间的凸起按钮
|
||||
// self.customTabBar = nil;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 方案C: 完全重新设计(4个→6个)
|
||||
|
||||
增加更多功能入口,显得是不同的产品定位。
|
||||
|
||||
**新结构**:发现 → 直播 → 短视频 → 消息 → 商城 → 我的
|
||||
|
||||
```objc
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
NSArray *viewControllers = @[
|
||||
[self createDiscoverVC], // 发现(重组的首页)
|
||||
[self createLiveVC], // 直播(原首页的一部分)
|
||||
[self createVideoVC], // 短视频(新增或从动态拆分)
|
||||
[self createMessageVC], // 消息
|
||||
[self createShopVC], // 商城(新增或从我的拆分)
|
||||
[self createMineVC] // 我的
|
||||
];
|
||||
|
||||
self.viewControllers = viewControllers;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 视觉改造
|
||||
|
||||
### 1. TabBar 样式改变
|
||||
|
||||
#### 方案1: 平面风格 → 卡片风格
|
||||
|
||||
```objc
|
||||
// TabbarViewController.m
|
||||
|
||||
- (void)setupTabBarAppearance {
|
||||
// 创建卡片样式的 TabBar
|
||||
self.tabBar.backgroundColor = [UIColor whiteColor];
|
||||
self.tabBar.layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
self.tabBar.layer.shadowOffset = CGSizeMake(0, -3);
|
||||
self.tabBar.layer.shadowOpacity = 0.1;
|
||||
self.tabBar.layer.shadowRadius = 10;
|
||||
|
||||
// 圆角
|
||||
self.tabBar.layer.cornerRadius = 20;
|
||||
self.tabBar.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner;
|
||||
|
||||
// 内边距
|
||||
self.tabBar.frame = CGRectMake(0,
|
||||
self.tabBar.frame.origin.y,
|
||||
self.tabBar.frame.size.width,
|
||||
self.tabBar.frame.size.height + 20);
|
||||
}
|
||||
```
|
||||
|
||||
#### 方案2: 固定底部 → 浮动底部
|
||||
|
||||
```objc
|
||||
- (void)setupFloatingTabBar {
|
||||
// 让 TabBar 浮动在内容之上
|
||||
self.tabBar.backgroundColor = [UIColor clearColor];
|
||||
self.tabBar.backgroundImage = [UIImage new];
|
||||
self.tabBar.shadowImage = [UIImage new];
|
||||
|
||||
// 创建自定义背景
|
||||
UIView *customTabBar = [[UIView alloc] initWithFrame:CGRectMake(20, -10,
|
||||
self.tabBar.bounds.size.width - 40,
|
||||
self.tabBar.bounds.size.height - 10)];
|
||||
customTabBar.backgroundColor = [UIColor whiteColor];
|
||||
customTabBar.layer.cornerRadius = 25;
|
||||
customTabBar.layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
customTabBar.layer.shadowOffset = CGSizeMake(0, -2);
|
||||
customTabBar.layer.shadowOpacity = 0.15;
|
||||
customTabBar.layer.shadowRadius = 15;
|
||||
|
||||
[self.tabBar insertSubview:customTabBar atIndex:0];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 选中动画改变
|
||||
|
||||
#### 方案1: 缩放动画
|
||||
|
||||
```objc
|
||||
// XPTabBar.m (或新的 TabBar 类)
|
||||
|
||||
- (void)setSelectedItem:(UITabBarItem *)selectedItem {
|
||||
[super setSelectedItem:selectedItem];
|
||||
|
||||
// 找到对应的按钮
|
||||
NSInteger index = [self.items indexOfObject:selectedItem];
|
||||
if (index != NSNotFound) {
|
||||
UIView *tabBarButton = self.subviews[index + 1]; // +1 是因为第一个是背景
|
||||
|
||||
// 缩放动画
|
||||
[UIView animateWithDuration:0.3
|
||||
delay:0
|
||||
usingSpringWithDamping:0.5
|
||||
initialSpringVelocity:0.5
|
||||
options:UIViewAnimationOptionCurveEaseInOut
|
||||
animations:^{
|
||||
tabBarButton.transform = CGAffineTransformMakeScale(1.2, 1.2);
|
||||
} completion:^(BOOL finished) {
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
tabBarButton.transform = CGAffineTransformIdentity;
|
||||
}];
|
||||
}];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 方案2: 上移 + 高亮
|
||||
|
||||
```objc
|
||||
- (void)animateSelectionForButton:(UIView *)button {
|
||||
// 所有按钮复位
|
||||
for (UIView *view in self.subviews) {
|
||||
if ([view isKindOfClass:NSClassFromString(@"UITabBarButton")]) {
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
view.transform = CGAffineTransformIdentity;
|
||||
view.alpha = 0.6;
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// 选中按钮上移 + 高亮
|
||||
[UIView animateWithDuration:0.3
|
||||
delay:0
|
||||
usingSpringWithDamping:0.7
|
||||
initialSpringVelocity:0.3
|
||||
options:UIViewAnimationOptionCurveEaseOut
|
||||
animations:^{
|
||||
button.transform = CGAffineTransformMakeTranslation(0, -8);
|
||||
button.alpha = 1.0;
|
||||
} completion:nil];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 中间按钮改造
|
||||
|
||||
如果有中间凸起的发布按钮:
|
||||
|
||||
#### 方案1: 移除凸起,改为平齐
|
||||
|
||||
```objc
|
||||
// 移除原有的自定义中间按钮逻辑
|
||||
// 使用普通的 TabBarItem
|
||||
```
|
||||
|
||||
#### 方案2: 改变凸起样式
|
||||
|
||||
```objc
|
||||
// 原: 圆形凸起
|
||||
// 新: 六边形凸起
|
||||
|
||||
- (void)setupCenterButton {
|
||||
// 创建六边形路径
|
||||
UIBezierPath *hexagonPath = [UIBezierPath bezierPath];
|
||||
CGFloat radius = 30;
|
||||
CGFloat centerX = self.bounds.size.width / 2;
|
||||
CGFloat centerY = 0;
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
CGFloat angle = M_PI / 3 * i;
|
||||
CGFloat x = centerX + radius * cos(angle);
|
||||
CGFloat y = centerY + radius * sin(angle);
|
||||
|
||||
if (i == 0) {
|
||||
[hexagonPath moveToPoint:CGPointMake(x, y)];
|
||||
} else {
|
||||
[hexagonPath addLineToPoint:CGPointMake(x, y)];
|
||||
}
|
||||
}
|
||||
[hexagonPath closePath];
|
||||
|
||||
// 创建形状层
|
||||
CAShapeLayer *hexagonLayer = [CAShapeLayer layer];
|
||||
hexagonLayer.path = hexagonPath.CGPath;
|
||||
hexagonLayer.fillColor = [AppThemeColor primaryColor].CGColor;
|
||||
|
||||
self.centerButton.layer.mask = hexagonLayer;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 具体实施步骤
|
||||
|
||||
### 步骤1: 备份原 TabBar 文件
|
||||
|
||||
```bash
|
||||
cd "/Users/edwinqqq/Local/Company Projects/peko-ios/YuMi/Modules/YMTabbar"
|
||||
cp View/TabbarViewController.m View/TabbarViewController.m.backup
|
||||
cp View/XPTabBar.m View/XPTabBar.m.backup
|
||||
```
|
||||
|
||||
### 步骤2: 修改 TabBar 初始化逻辑
|
||||
|
||||
```objc
|
||||
// TabbarViewController.m
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
// 1. 创建新的 ViewController 顺序
|
||||
[self setupViewControllers];
|
||||
|
||||
// 2. 设置新的 TabBar 样式
|
||||
[self setupTabBarAppearance];
|
||||
|
||||
// 3. 配置 TabBarItem
|
||||
[self setupTabBarItems];
|
||||
|
||||
// 4. 设置选中动画
|
||||
[self setupSelectionAnimation];
|
||||
}
|
||||
|
||||
- (void)setupViewControllers {
|
||||
// 根据选择的方案A/B/C调整
|
||||
// 方案A: 改变顺序
|
||||
NSArray *viewControllers = @[
|
||||
[self createMomentsVC],
|
||||
[self createHomeVC],
|
||||
[self createBlankVC],
|
||||
[self createMineVC],
|
||||
[self createMessageVC]
|
||||
];
|
||||
|
||||
self.viewControllers = viewControllers;
|
||||
}
|
||||
|
||||
- (void)setupTabBarAppearance {
|
||||
// 应用新的视觉样式
|
||||
self.tabBar.tintColor = [AppThemeColor tabBarSelectedColor];
|
||||
self.tabBar.unselectedItemTintColor = [AppThemeColor tabBarNormalColor];
|
||||
self.tabBar.backgroundColor = [AppThemeColor tabBarBackgroundColor];
|
||||
|
||||
// 添加阴影或其他装饰
|
||||
[self setupFloatingTabBar]; // 或 setupTabBarShadow
|
||||
}
|
||||
|
||||
- (void)setupTabBarItems {
|
||||
// 更新所有 Tab 的图标和文字
|
||||
NSArray *titles = @[@"动态", @"首页", @"", @"我的", @"消息"];
|
||||
NSArray *normalIcons = @[@"tab_moments_normal", @"tab_home_normal", @"",
|
||||
@"tab_mine_normal", @"tab_message_normal"];
|
||||
NSArray *selectedIcons = @[@"tab_moments_selected", @"tab_home_selected", @"",
|
||||
@"tab_mine_selected", @"tab_message_selected"];
|
||||
|
||||
for (int i = 0; i < self.viewControllers.count; i++) {
|
||||
UIViewController *vc = self.viewControllers[i];
|
||||
vc.tabBarItem.title = titles[i];
|
||||
vc.tabBarItem.image = [[UIImage imageNamed:normalIcons[i]] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
|
||||
vc.tabBarItem.selectedImage = [[UIImage imageNamed:selectedIcons[i]] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
|
||||
|
||||
// 设置标题样式
|
||||
[vc.tabBarItem setTitleTextAttributes:@{
|
||||
NSForegroundColorAttributeName: [AppThemeColor tabBarNormalColor],
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:10]
|
||||
} forState:UIControlStateNormal];
|
||||
|
||||
[vc.tabBarItem setTitleTextAttributes:@{
|
||||
NSForegroundColorAttributeName: [AppThemeColor tabBarSelectedColor],
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:10]
|
||||
} forState:UIControlStateSelected];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤3: 处理业务逻辑中的 Tab 跳转
|
||||
|
||||
很多地方可能硬编码了 Tab 的 index,需要全局搜索并更新:
|
||||
|
||||
```bash
|
||||
# 搜索所有 TabBar 跳转代码
|
||||
grep -rn "selectedIndex" YuMi/Modules --include="*.m"
|
||||
|
||||
# 常见的模式
|
||||
grep -rn "tabBarController.selectedIndex = " YuMi/Modules --include="*.m"
|
||||
```
|
||||
|
||||
**建议**:创建一个枚举来管理 Tab index,而不是硬编码数字
|
||||
|
||||
```objc
|
||||
// TabbarViewController.h
|
||||
|
||||
typedef NS_ENUM(NSInteger, AppTabIndex) {
|
||||
AppTabIndexMoments = 0,
|
||||
AppTabIndexHome = 1,
|
||||
AppTabIndexPublish = 2,
|
||||
AppTabIndexMine = 3,
|
||||
AppTabIndexMessage = 4
|
||||
};
|
||||
|
||||
// 使用
|
||||
self.tabBarController.selectedIndex = AppTabIndexHome;
|
||||
```
|
||||
|
||||
### 步骤4: 更新推送和深度链接
|
||||
|
||||
如果有推送通知或深度链接跳转到特定 Tab,也需要更新:
|
||||
|
||||
```objc
|
||||
// AppDelegate.m 或 深度链接处理
|
||||
|
||||
- (void)handleNotification:(NSDictionary *)userInfo {
|
||||
NSString *targetTab = userInfo[@"target_tab"];
|
||||
|
||||
TabbarViewController *tabVC = (TabbarViewController *)self.window.rootViewController;
|
||||
|
||||
if ([targetTab isEqualToString:@"message"]) {
|
||||
tabVC.selectedIndex = AppTabIndexMessage; // 更新为新的 index
|
||||
}
|
||||
// ... 其他 Tab
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 图标设计建议
|
||||
|
||||
### 1. 扁平风格 → 3D 风格
|
||||
|
||||
或者反过来,如果原来是 3D,改为扁平。
|
||||
|
||||
### 2. 线条图标 → 填充图标
|
||||
|
||||
```
|
||||
原: ☐ ☐ ☐ ☐ (线框)
|
||||
新: ■ ■ ■ ■ (填充)
|
||||
```
|
||||
|
||||
### 3. 图标位置变化
|
||||
|
||||
```objc
|
||||
// 图标上移,文字下移
|
||||
- (void)setupTabBarItemLayout {
|
||||
for (UIViewController *vc in self.viewControllers) {
|
||||
vc.tabBarItem.imageInsets = UIEdgeInsetsMake(-2, 0, 2, 0);
|
||||
vc.tabBarItem.titlePositionAdjustment = UIOffsetMake(0, 2);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📱 响应式适配
|
||||
|
||||
确保 TabBar 在不同设备上都正常显示:
|
||||
|
||||
```objc
|
||||
- (void)setupTabBarForDevice {
|
||||
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
|
||||
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
|
||||
|
||||
// iPad 适配
|
||||
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
||||
self.tabBar.itemWidth = 100;
|
||||
self.tabBar.itemSpacing = 20;
|
||||
}
|
||||
|
||||
// iPhone SE 等小屏适配
|
||||
if (screenWidth <= 375) {
|
||||
// 减小图标和文字
|
||||
for (UIViewController *vc in self.viewControllers) {
|
||||
[vc.tabBarItem setTitleTextAttributes:@{
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:9]
|
||||
} forState:UIControlStateNormal];
|
||||
}
|
||||
}
|
||||
|
||||
// 刘海屏适配
|
||||
if (@available(iOS 11.0, *)) {
|
||||
CGFloat bottomSafeArea = self.view.safeAreaInsets.bottom;
|
||||
if (bottomSafeArea > 0) {
|
||||
// 有刘海
|
||||
self.tabBar.frame = CGRectMake(0,
|
||||
screenHeight - 83 - bottomSafeArea,
|
||||
screenWidth,
|
||||
83 + bottomSafeArea);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 测试清单
|
||||
|
||||
完成 TabBar 改造后,测试以下内容:
|
||||
|
||||
- [ ] 所有 Tab 都能正常切换
|
||||
- [ ] Tab 图标和文字显示正确
|
||||
- [ ] 选中状态颜色正确
|
||||
- [ ] 选中动画流畅
|
||||
- [ ] 推送通知跳转正确
|
||||
- [ ] 深度链接跳转正确
|
||||
- [ ] 小红点显示正确(未读消息等)
|
||||
- [ ] 旋转屏幕后 TabBar 正常
|
||||
- [ ] iPad 上显示正常
|
||||
- [ ] 深色模式下(如果支持)显示正常
|
||||
|
||||
---
|
||||
|
||||
## 🎯 差异化效果评估
|
||||
|
||||
### 原 TabBar
|
||||
- 5个固定 Tab
|
||||
- 中间凸起发布按钮
|
||||
- 紫色主题
|
||||
- 扁平风格图标
|
||||
- 无动画
|
||||
|
||||
### 新 TabBar(方案A)
|
||||
- 5个 Tab,顺序改变
|
||||
- 中间按钮改为六边形
|
||||
- 橙色主题
|
||||
- 填充风格图标
|
||||
- 弹性选中动画
|
||||
- 浮动卡片样式
|
||||
|
||||
**差异度**: 约 80%
|
||||
|
||||
---
|
||||
|
||||
## 🚀 高级技巧
|
||||
|
||||
### 1. 添加震动反馈
|
||||
|
||||
```objc
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item {
|
||||
// 触觉反馈
|
||||
if (@available(iOS 10.0, *)) {
|
||||
UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
|
||||
[generator impactOccurred];
|
||||
} else {
|
||||
AudioServicesPlaySystemSound(1519); // Peek feedback
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 添加粒子效果
|
||||
|
||||
```objc
|
||||
- (void)addParticleEffectToButton:(UIView *)button {
|
||||
CAEmitterLayer *emitterLayer = [CAEmitterLayer layer];
|
||||
emitterLayer.emitterPosition = CGPointMake(button.bounds.size.width/2,
|
||||
button.bounds.size.height/2);
|
||||
|
||||
CAEmitterCell *cell = [CAEmitterCell emitterCell];
|
||||
cell.birthRate = 10;
|
||||
cell.lifetime = 1.0;
|
||||
cell.velocity = 50;
|
||||
cell.scale = 0.05;
|
||||
cell.contents = (id)[[UIImage imageNamed:@"particle"] CGImage];
|
||||
|
||||
emitterLayer.emitterCells = @[cell];
|
||||
[button.layer addSublayer:emitterLayer];
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Lottie 动画图标
|
||||
|
||||
```objc
|
||||
#import <Lottie/Lottie.h>
|
||||
|
||||
- (void)setupLottieTabBarItems {
|
||||
for (int i = 0; i < self.viewControllers.count; i++) {
|
||||
LOTAnimationView *animationView = [LOTAnimationView animationNamed:@"tab_icon_animation"];
|
||||
animationView.frame = CGRectMake(0, 0, 30, 30);
|
||||
|
||||
// 添加到 TabBarItem
|
||||
UIViewController *vc = self.viewControllers[i];
|
||||
// ... 自定义逻辑
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 总结
|
||||
|
||||
通过以上改造,你的 TabBar 将:
|
||||
|
||||
1. ✅ 结构完全不同(顺序/数量)
|
||||
2. ✅ 视觉完全不同(颜色/样式/图标)
|
||||
3. ✅ 交互完全不同(动画/反馈)
|
||||
4. ✅ 代码明显不同(类名/逻辑)
|
||||
|
||||
**预计改造时间**: 2-3天
|
||||
|
||||
**差异化程度**: 80%+
|
||||
|
||||
这将是 App Store 审查中最直观的差异点之一!🎉
|
||||
|
||||
|
404
issues/scripts/README.md
Normal file
404
issues/scripts/README.md
Normal file
@@ -0,0 +1,404 @@
|
||||
# 白牌项目改造工具包 - 快速执行指南
|
||||
|
||||
## 🎯 目标
|
||||
在1个月内将 YuMi 项目改造成全新的白牌项目,规避 App Store 审查。
|
||||
|
||||
---
|
||||
|
||||
## 📦 工具清单
|
||||
|
||||
本目录包含以下工具:
|
||||
|
||||
1. **1_rename_classes.sh** - 类名批量替换脚本
|
||||
2. **2_rename_assets.py** - 图片资源批量重命名
|
||||
3. **3_theme_color_example.md** - 主题颜色替换方案
|
||||
4. **white-label-refactor-plan.md** - 完整的30天改造计划
|
||||
|
||||
---
|
||||
|
||||
## ⚡️ 快速开始(3步走)
|
||||
|
||||
### 前置准备
|
||||
|
||||
```bash
|
||||
# 1. 确保在正确的分支
|
||||
cd "/Users/edwinqqq/Local/Company Projects/peko-ios"
|
||||
git status
|
||||
|
||||
# 2. 创建新分支
|
||||
git checkout -b white-label-refactor
|
||||
|
||||
# 3. 提交所有未保存的更改
|
||||
git add .
|
||||
git commit -m "开始白牌改造前的备份"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 执行步骤
|
||||
|
||||
### Week 1: 标识层改造
|
||||
|
||||
#### Day 1-5: 类名和文件重命名
|
||||
|
||||
```bash
|
||||
# 1. 给脚本添加执行权限
|
||||
cd issues/scripts
|
||||
chmod +x 1_rename_classes.sh
|
||||
|
||||
# 2. 运行类名替换脚本(交互式)
|
||||
./1_rename_classes.sh
|
||||
|
||||
# 输入示例:
|
||||
# YM → LK
|
||||
# XP → SL
|
||||
# PI → MT
|
||||
# Moli → Nova
|
||||
# MS → AS
|
||||
```
|
||||
|
||||
**预期结果**:
|
||||
- ✅ 所有类名前缀已替换
|
||||
- ✅ 文件名已重命名
|
||||
- ✅ 模块文件夹已重命名
|
||||
- ✅ Xcode 项目文件已更新
|
||||
|
||||
**验证步骤**:
|
||||
```bash
|
||||
# 1. 在 Xcode 中重新打开项目
|
||||
open YuMi.xcworkspace
|
||||
|
||||
# 2. Clean Build Folder
|
||||
# Xcode: Product → Clean Build Folder (⇧⌘K)
|
||||
|
||||
# 3. 尝试编译
|
||||
# Xcode: Product → Build (⌘B)
|
||||
|
||||
# 4. 检查编译错误并修复
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Day 6-7: 图片资源替换
|
||||
|
||||
```bash
|
||||
# 1. 确保 Python 3 已安装
|
||||
python3 --version
|
||||
|
||||
# 2. 运行图片资源重命名脚本
|
||||
python3 2_rename_assets.py
|
||||
|
||||
# 3. 执行生成的代码替换脚本
|
||||
cd ../temp_rename
|
||||
chmod +x replace_image_refs.sh
|
||||
./replace_image_refs.sh
|
||||
```
|
||||
|
||||
**预期结果**:
|
||||
- ✅ 2971个图片资源已重命名
|
||||
- ✅ Contents.json 已更新
|
||||
- ✅ 代码中的图片引用已替换
|
||||
- ✅ 生成了映射文件备查
|
||||
|
||||
**验证步骤**:
|
||||
```bash
|
||||
# 1. 编译项目
|
||||
# Xcode: Product → Build
|
||||
|
||||
# 2. 运行应用
|
||||
# Xcode: Product → Run (⌘R)
|
||||
|
||||
# 3. 检查关键页面的图片是否正常显示
|
||||
# - 登录页
|
||||
# - 首页
|
||||
# - TabBar
|
||||
# - 房间页
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Week 2: 视觉层改造
|
||||
|
||||
#### Day 8-10: 主题颜色替换
|
||||
|
||||
```bash
|
||||
# 1. 阅读配色方案文档
|
||||
cat 3_theme_color_example.md
|
||||
|
||||
# 2. 选择一个配色方案(A/B/C)
|
||||
|
||||
# 3. 手动创建新的颜色类
|
||||
# 在 Xcode 中:
|
||||
# YuMi/CustomUI/ → New File → Cocoa Touch Class
|
||||
# 类名: AppThemeColor
|
||||
# 父类: NSObject
|
||||
```
|
||||
|
||||
**实施步骤**:
|
||||
1. 复制 `3_theme_color_example.md` 中的示例代码
|
||||
2. 创建 `AppThemeColor.h` 和 `AppThemeColor.m`
|
||||
3. 运行文档中的替换脚本
|
||||
4. 手动调整硬编码的颜色值
|
||||
|
||||
**验证步骤**:
|
||||
```bash
|
||||
# 运行应用,检查所有页面的颜色
|
||||
# 重点检查:
|
||||
# - TabBar 颜色
|
||||
# - 导航栏颜色
|
||||
# - 按钮渐变
|
||||
# - 背景色
|
||||
# - 文字颜色
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Day 11-12: TabBar 结构调整
|
||||
|
||||
**手动操作**(在 Xcode 中):
|
||||
|
||||
1. 打开 `TabbarViewController.m`
|
||||
2. 找到 TabBar 初始化代码
|
||||
3. 调整 Tab 顺序,例如:
|
||||
```objc
|
||||
// 旧顺序: 首页 → 消息 → 动态 → 我的
|
||||
// 新顺序: 动态 → 首页 → 消息 → 我的
|
||||
|
||||
// 或者改变 Tab 数量
|
||||
// 5个 Tab → 4个 Tab(移除某个功能)
|
||||
```
|
||||
4. 更换所有 TabBar 图标
|
||||
5. 修改 TabBar 选中/未选中动画
|
||||
|
||||
---
|
||||
|
||||
### Week 3: 代码结构层改造
|
||||
|
||||
#### Day 13-15: 核心页面 UI 重构
|
||||
|
||||
**优先级**:
|
||||
1. 登录页(必须改)
|
||||
2. 首页(必须改)
|
||||
3. 房间页(可选择性改)
|
||||
4. 个人中心(必须改)
|
||||
|
||||
**策略**:
|
||||
- 不改业务逻辑,只改 UI 布局
|
||||
- 使用 Interface Builder → 纯代码(或反之)
|
||||
- 改变控件位置和排列方式
|
||||
|
||||
---
|
||||
|
||||
#### Day 16-19: 拆分超大类
|
||||
|
||||
重点处理 `XPRoomViewController.m`(4605行):
|
||||
|
||||
```objc
|
||||
// 创建 Category 拆分
|
||||
// XPRoomViewController+Gift.h/m - 礼物相关
|
||||
// XPRoomViewController+PK.h/m - PK相关
|
||||
// XPRoomViewController+Game.h/m - 游戏相关
|
||||
// XPRoomViewController+Chat.h/m - 聊天相关
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Week 4: 测试和提审
|
||||
|
||||
#### Day 20-23: 功能测试
|
||||
|
||||
```bash
|
||||
# 测试检查清单
|
||||
- [ ] 登录/注册(所有方式)
|
||||
- [ ] 首页浏览和筛选
|
||||
- [ ] 进入房间
|
||||
- [ ] 发送消息
|
||||
- [ ] 发送礼物
|
||||
- [ ] 充值流程(测试环境)
|
||||
- [ ] 个人中心设置
|
||||
- [ ] 第三方登录(Facebook/Google/Apple)
|
||||
- [ ] 推送通知
|
||||
- [ ] 分享功能
|
||||
- [ ] 深度链接
|
||||
```
|
||||
|
||||
#### Day 24-26: 最终调整
|
||||
|
||||
1. 修复所有测试中发现的 bug
|
||||
2. 优化性能(内存泄漏、卡顿)
|
||||
3. 更新应用截图(5-10张)
|
||||
4. 撰写新的应用描述
|
||||
|
||||
#### Day 27-30: 提审准备
|
||||
|
||||
```bash
|
||||
# 1. 更新 Bundle ID
|
||||
# 在 Xcode: Targets → General → Bundle Identifier
|
||||
|
||||
# 2. 更新 App 名称
|
||||
# 在 Info.plist: CFBundleDisplayName
|
||||
|
||||
# 3. 准备新的 App Store 截图
|
||||
|
||||
# 4. 撰写应用描述(完全不同的文案)
|
||||
|
||||
# 5. 提交审核
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 进度追踪
|
||||
|
||||
使用以下命令查看当前进度:
|
||||
|
||||
```bash
|
||||
# 检查已重命名的类
|
||||
find YuMi -name "*.m" | grep -v Pods | wc -l
|
||||
|
||||
# 检查颜色替换进度
|
||||
grep -r "DJDKMIMOMColor" YuMi --include="*.m" | wc -l
|
||||
grep -r "AppThemeColor" YuMi --include="*.m" | wc -l
|
||||
|
||||
# 检查图片资源
|
||||
ls -l YuMi/Assets.xcassets/**/*.imageset | wc -l
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 常见问题
|
||||
|
||||
### Q1: 编译错误 "Use of undeclared identifier"
|
||||
|
||||
**原因**:类名替换不完整
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 搜索旧的类名
|
||||
grep -r "YMSomeClass" YuMi --include="*.m" --include="*.h"
|
||||
|
||||
# 手动替换
|
||||
```
|
||||
|
||||
### Q2: 图片不显示
|
||||
|
||||
**原因**:图片名称替换遗漏
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 1. 检查映射文件
|
||||
cat issues/temp_rename/asset_name_mapping.json
|
||||
|
||||
# 2. 搜索旧的图片名
|
||||
grep -r "old_image_name" YuMi --include="*.m"
|
||||
|
||||
# 3. 手动替换
|
||||
```
|
||||
|
||||
### Q3: 颜色显示异常
|
||||
|
||||
**原因**:硬编码的颜色值未替换
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 搜索硬编码的 hex 颜色
|
||||
grep -r "0x9682FF" YuMi --include="*.m"
|
||||
grep -r "UIColorFromRGB" YuMi --include="*.m" | grep "0x"
|
||||
|
||||
# 手动替换为 [AppThemeColor xxx]
|
||||
```
|
||||
|
||||
### Q4: TabBar 跳转失败
|
||||
|
||||
**原因**:代码中硬编码了 Tab index
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 搜索 selectedIndex
|
||||
grep -r "selectedIndex" YuMi --include="*.m"
|
||||
|
||||
# 更新所有硬编码的 index
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🆘 回滚方案
|
||||
|
||||
如果出现严重问题,可以快速回滚:
|
||||
|
||||
```bash
|
||||
# 方法1: Git 回滚
|
||||
git reset --hard HEAD
|
||||
git clean -fd
|
||||
|
||||
# 方法2: 恢复备份
|
||||
# 类名替换备份: issues/temp_rename/
|
||||
# 图片资源备份: issues/temp_rename/assets_backup/
|
||||
# Xcode 项目备份: YuMi.xcodeproj/project.pbxproj.backup
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如果遇到无法解决的问题:
|
||||
|
||||
1. 查看详细日志
|
||||
```bash
|
||||
cat issues/temp_rename/rename_report.txt
|
||||
cat issues/temp_rename/assets_rename_report.txt
|
||||
```
|
||||
|
||||
2. 检查 Git 提交记录
|
||||
```bash
|
||||
git log --oneline
|
||||
git diff
|
||||
```
|
||||
|
||||
3. 分步骤回滚测试
|
||||
|
||||
---
|
||||
|
||||
## ✅ 最终检查清单
|
||||
|
||||
提审前必须确认:
|
||||
|
||||
- [ ] 所有编译警告已解决
|
||||
- [ ] 核心功能测试通过
|
||||
- [ ] 没有明显的崩溃
|
||||
- [ ] UI 和原项目有显著差异(至少70%)
|
||||
- [ ] Bundle ID 已更改
|
||||
- [ ] App 名称已更改
|
||||
- [ ] 所有图片资源已替换
|
||||
- [ ] 主题色已完全不同
|
||||
- [ ] 准备好新的应用截图和描述
|
||||
- [ ] 第三方 SDK 配置正确
|
||||
- [ ] 测试环境和生产环境切换正确
|
||||
|
||||
---
|
||||
|
||||
## 📈 成功标准
|
||||
|
||||
**技术层面**:
|
||||
- 代码相似度 < 30%
|
||||
- 资源文件完全不同
|
||||
- UI 视觉差异 > 70%
|
||||
|
||||
**审查层面**:
|
||||
- App Store 截图无相似性
|
||||
- 应用描述和关键词不重复
|
||||
- 功能展示有差异化
|
||||
|
||||
---
|
||||
|
||||
## 🎉 完成后
|
||||
|
||||
恭喜!你已经完成了白牌项目的改造。
|
||||
|
||||
**建议**:
|
||||
1. 保留完整的映射文档,便于后续维护
|
||||
2. 定期同步原项目的 bug 修复
|
||||
3. 使用 Git submodule 或 monorepo 管理多个白牌版本
|
||||
|
||||
**祝你成功通过 App Store 审核!** 🚀
|
||||
|
||||
|
319
issues/white-label-refactor-plan.md
Normal file
319
issues/white-label-refactor-plan.md
Normal file
@@ -0,0 +1,319 @@
|
||||
# 白牌项目一个月改造计划
|
||||
|
||||
## 项目背景
|
||||
- **目标**:基于现有 YuMi 项目创建白牌版本,规避 App Store 审查
|
||||
- **时间**:1个月
|
||||
- **约束**:保留大部分基建代码和 SDK,功能可适当裁剪
|
||||
- **核心策略**:三层剥离法(标识层 100% + 视觉层 80% + 代码结构层 30%)
|
||||
|
||||
---
|
||||
|
||||
## Week 1: 标识层改造(100%)- 不可逆的关键改动
|
||||
|
||||
### Day 1-2: 项目基础标识
|
||||
- [ ] 创建新的 Xcode Project(新 Bundle ID、新证书)
|
||||
- [ ] 全局替换类名前缀(5个前缀)
|
||||
- `YM` → `新前缀1`(如 `LK`)
|
||||
- `XP` → `新前缀2`(如 `SL`)
|
||||
- `PI` → `新前缀3`(如 `MT`)
|
||||
- `Moli` → `新前缀4`(如 `Nova`)
|
||||
- `MS` → `新前缀5`(如 `AS`)
|
||||
- [ ] 替换所有宏定义前缀
|
||||
- `YUMI` → `新宏前缀`
|
||||
- [ ] 修改全局常量文件
|
||||
- `YUMIConstant.h/m` → 重命名 + 内容修改
|
||||
- `YUMIHtmlUrl.h/m` → 重命名 + 内容修改
|
||||
|
||||
### Day 3: 文件和模块重命名
|
||||
- [ ] 重命名核心模块文件夹
|
||||
- `YMLogin` → `新名字`
|
||||
- `YMMessage` → `新名字`
|
||||
- `YMMine` → `新名字`
|
||||
- `YMMonents` → `新名字`
|
||||
- `YMNewHome` → `新名字`
|
||||
- `YMRoom` → `新名字`
|
||||
- `YMTabbar` → `新名字`
|
||||
- [ ] 重命名 Pod 项目名
|
||||
- `YuMi.podspec` → 新名字
|
||||
- [ ] 修改 Podfile 引用
|
||||
|
||||
### Day 4-5: 字符串常量清理
|
||||
- [ ] 扫描所有硬编码字符串(grep "YuMi|Moli|YUMI")
|
||||
- [ ] 替换 UserDefaults/Keychain 的 key 前缀
|
||||
- [ ] 替换所有中文文案为新的品牌名(可选保留,用新马甲名)
|
||||
- [ ] 修改多语言文件(.strings)
|
||||
|
||||
---
|
||||
|
||||
## Week 2: 视觉层改造(80%)- UI/UX 大改
|
||||
|
||||
### Day 6-7: 资源文件全量替换
|
||||
- [ ] **图片资源批量处理**(2971个文件)
|
||||
- 编写脚本批量重命名所有图片
|
||||
- 更换核心 icon(登录、TabBar、导航栏按钮等)
|
||||
- 调整配色方案(主题色、渐变色)
|
||||
- 工具:使用 Python/Shell 脚本 + ImageMagick
|
||||
- [ ] **SVGA 动画资源**
|
||||
- 重命名所有 .svga 文件
|
||||
- 替换关键动画(登录动画、礼物动画)
|
||||
- [ ] **App Icon 和启动图**
|
||||
- 全新设计 AppIcon
|
||||
- 替换 LaunchScreen
|
||||
|
||||
### Day 8-9: UI 组件库改造
|
||||
- [ ] 颜色系统重构
|
||||
- 修改 `DJDKMIMOMColor.h/m` 为新的色值
|
||||
- 建立新的 UIColor 扩展类(不要用原类名)
|
||||
- [ ] 通用 UI 组件重写
|
||||
- `MoliAvatar` → 新的头像组件(改渲染逻辑)
|
||||
- `SexAgeLabel` → 新样式
|
||||
- `MoliMoneyLabel` → 新样式
|
||||
- 所有渐变效果改变方向或色值
|
||||
- [ ] 字体系统(可选)
|
||||
- 替换默认字体为另一种商业字体
|
||||
|
||||
### Day 10-12: TabBar 和导航栏重构
|
||||
- [ ] **TabBar 结构调整**
|
||||
- 更改 Tab 数量(5个→4个?或6个?)
|
||||
- 更改 Tab 顺序(如:首页/消息/我的 → 消息/首页/我的)
|
||||
- 完全重写 `XPTabBar.h/m` 布局逻辑
|
||||
- 新的选中/未选中动画效果
|
||||
- [ ] **导航栏样式**
|
||||
- `BaseViewController` 的导航栏改为新样式
|
||||
- 所有返回按钮、右侧按钮图标更换
|
||||
- 导航栏背景色/透明度调整
|
||||
|
||||
---
|
||||
|
||||
## Week 3: 代码结构层改造(30%)- 关键差异化
|
||||
|
||||
### Day 13-15: 核心页面 UI 重构
|
||||
- [ ] **登录页面**(`YMLogin` 模块)
|
||||
- 完全重写 UI 布局(从竖向改横向?)
|
||||
- 登录方式顺序调整
|
||||
- 新的视觉风格
|
||||
- [ ] **首页**(`YMNewHome` 模块)
|
||||
- 房间列表布局改变(瀑布流→网格?)
|
||||
- 筛选器位置和样式调整
|
||||
- Banner 尺寸和位置变化
|
||||
- [ ] **房间页**(`YMRoom` 模块)
|
||||
- ⚠️ 这是最大的模块(1059个文件)
|
||||
- **策略**:只改 UI 层,不动业务逻辑
|
||||
- 拆分 `XPRoomViewController.m`(4605行)
|
||||
- 提取礼物模块为独立 ViewController
|
||||
- 提取 PK 模块为独立 ViewController
|
||||
- 提取底部控制栏为独立 View
|
||||
|
||||
### Day 16-17: 消息和动态模块
|
||||
- [ ] **消息列表**(`YMMessage` 模块)
|
||||
- Cell 样式完全重写
|
||||
- 时间显示格式改变
|
||||
- 头像位置调整
|
||||
- [ ] **动态/朋友圈**(`YMMonents` 模块)
|
||||
- 布局方式改变(卡片式→列表式?)
|
||||
- 交互手势调整(点赞动画、评论入口)
|
||||
|
||||
### Day 18-19: 个人中心模块
|
||||
- [ ] **我的页面**(`YMMine` 模块)
|
||||
- 顶部个人信息卡片重新设计
|
||||
- 设置项图标和顺序调整
|
||||
- 等级/财富展示方式改变
|
||||
|
||||
---
|
||||
|
||||
## Week 4: 功能裁剪和测试
|
||||
|
||||
### Day 20-21: 功能裁剪(可选)
|
||||
根据实际情况,可以移除以下模块以降低相似度:
|
||||
- [ ] 移除小游戏模块(如果有独特的游戏)
|
||||
- [ ] 移除特定的礼物动画
|
||||
- [ ] 简化某些复杂的 H5 页面
|
||||
- [ ] 移除品牌相关的活动页面
|
||||
|
||||
### Day 22-23: 网络层包装
|
||||
- [ ] 在 `HttpRequestHelper` 外包一层新 API
|
||||
- [ ] 更改请求头字段名(User-Agent, Custom-Headers)
|
||||
- [ ] 参数加密方式微调(如果可以服务端配合)
|
||||
|
||||
### Day 24-28: 全面测试
|
||||
- [ ] 核心功能冒烟测试
|
||||
- 登录/注册
|
||||
- 首页浏览
|
||||
- 进入房间
|
||||
- 发送消息
|
||||
- 充值流程(测试环境)
|
||||
- [ ] UI 自动化测试(截图对比)
|
||||
- [ ] 崩溃率监控(集成 Bugly/Firebase)
|
||||
- [ ] 内存泄漏检测
|
||||
|
||||
### Day 29-30: 提审准备
|
||||
- [ ] 准备新的 App Store 截图(5-10张)
|
||||
- [ ] 撰写新的应用描述(完全不同的文案)
|
||||
- [ ] 准备审核说明(强调差异化功能)
|
||||
- [ ] 最终检查清单
|
||||
|
||||
---
|
||||
|
||||
## 关键技术方案
|
||||
|
||||
### 1. 批量类名替换脚本
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# rename_classes.sh
|
||||
|
||||
OLD_PREFIX="YM"
|
||||
NEW_PREFIX="LK"
|
||||
|
||||
# 查找所有 .h 和 .m 文件
|
||||
find ./YuMi -type f \( -name "*.h" -o -name "*.m" \) -exec sed -i '' "s/${OLD_PREFIX}/${NEW_PREFIX}/g" {} +
|
||||
|
||||
# 重命名文件
|
||||
find ./YuMi -type f -name "${OLD_PREFIX}*" | while read file; do
|
||||
newname=$(echo "$file" | sed "s/${OLD_PREFIX}/${NEW_PREFIX}/g")
|
||||
mv "$file" "$newname"
|
||||
done
|
||||
```
|
||||
|
||||
### 2. 图片资源批量处理
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# rename_images.py
|
||||
|
||||
import os
|
||||
import hashlib
|
||||
from PIL import Image
|
||||
|
||||
def rename_images(root_dir):
|
||||
for dirpath, dirnames, filenames in os.walk(root_dir):
|
||||
for filename in filenames:
|
||||
if filename.endswith(('.png', '.jpg', '.jpeg')):
|
||||
old_path = os.path.join(dirpath, filename)
|
||||
# 生成新的随机名称(基于内容hash)
|
||||
new_name = hashlib.md5(open(old_path,'rb').read()).hexdigest()[:16]
|
||||
new_path = os.path.join(dirpath, new_name + os.path.splitext(filename)[1])
|
||||
os.rename(old_path, new_path)
|
||||
print(f"Renamed: {filename} -> {new_name}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
rename_images("./YuMi/Assets.xcassets")
|
||||
```
|
||||
|
||||
### 3. 拆分超大类工具(XPRoomViewController)
|
||||
使用 Category 方式拆分,不改变主类:
|
||||
```objc
|
||||
// XPRoomViewController+Gift.h
|
||||
@interface XPRoomViewController (Gift)
|
||||
- (void)setupGiftUI;
|
||||
- (void)handleGiftAction;
|
||||
@end
|
||||
|
||||
// XPRoomViewController+PK.h
|
||||
@interface XPRoomViewController (PK)
|
||||
- (void)setupPKUI;
|
||||
- (void)handlePKAction;
|
||||
@end
|
||||
```
|
||||
|
||||
### 4. 颜色系统替换
|
||||
```objc
|
||||
// 新建 AppTheme.h
|
||||
@interface AppTheme : NSObject
|
||||
+ (UIColor *)primaryColor; // 主色调
|
||||
+ (UIColor *)secondaryColor; // 辅助色
|
||||
+ (UIColor *)accentColor; // 强调色
|
||||
@end
|
||||
|
||||
// 全局替换所有硬编码颜色
|
||||
// 旧: [UIColor colorWithHex:0xFF6B9D]
|
||||
// 新: [AppTheme primaryColor]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 风险控制
|
||||
|
||||
### 高风险点
|
||||
1. **类名替换可能误伤字符串**
|
||||
- 解决:先 grep 找出所有字符串常量,手动检查
|
||||
- 工具:`grep -r "\".*YM.*\"" ./YuMi`
|
||||
|
||||
2. **图片批量重命名导致找不到资源**
|
||||
- 解决:先做映射表,然后用脚本全局替换代码中的引用
|
||||
- 保留原图副本,出错可回滚
|
||||
|
||||
3. **第三方 SDK 回调失败**
|
||||
- 原因:某些 SDK 使用类名字符串反射
|
||||
- 解决:保留涉及 SDK 的类名不变,或在映射表中特殊处理
|
||||
|
||||
4. **TabBar 结构改变导致通知/跳转失败**
|
||||
- 原因:很多业务逻辑硬编码了 Tab index
|
||||
- 解决:全局搜索 `selectedIndex` 和 TabBar 相关跳转
|
||||
|
||||
### 测试检查清单
|
||||
- [ ] 所有第三方登录(Facebook/Google/Apple)
|
||||
- [ ] 支付流程(内购/第三方支付)
|
||||
- [ ] IM 消息收发
|
||||
- [ ] 直播推流和拉流
|
||||
- [ ] 深度链接(Deep Link)
|
||||
- [ ] 推送通知
|
||||
- [ ] 分享功能
|
||||
|
||||
---
|
||||
|
||||
## 成功标准
|
||||
|
||||
### App Store 审查通过标准
|
||||
1. ✅ 二进制代码相似度 < 30%(类名、方法名完全不同)
|
||||
2. ✅ 所有截图视觉差异明显(配色、布局、icon)
|
||||
3. ✅ Bundle ID 和签名完全不同
|
||||
4. ✅ 应用描述和关键词无重叠
|
||||
5. ✅ 核心功能可正常使用
|
||||
|
||||
### 技术债务评估
|
||||
- 预计技术债务:中等
|
||||
- 后续维护:需要同步维护两套代码(建议用 Git 分支管理)
|
||||
- 代码质量:不会比原项目更差(因为会顺便重构)
|
||||
|
||||
---
|
||||
|
||||
## 备选方案
|
||||
|
||||
如果 30 天时间不够,可以采用 **最小可行方案**(MVP):
|
||||
|
||||
### 最小改造清单(15天)
|
||||
1. 类名全局替换(2天)
|
||||
2. 核心图片替换(50个关键icon)(2天)
|
||||
3. 主题色全局替换(1天)
|
||||
4. TabBar 重新设计(2天)
|
||||
5. 登录页重写(2天)
|
||||
6. 首页重写(3天)
|
||||
7. 测试和提审(3天)
|
||||
|
||||
这个方案可以达到约 50-60% 的差异化,适合时间极度紧张的情况。
|
||||
|
||||
---
|
||||
|
||||
## 附录:工具清单
|
||||
|
||||
### 开发工具
|
||||
- Xcode Refactor 功能(批量重命名)
|
||||
- AppCode(IntelliJ 的 OC IDE,重构能力更强)
|
||||
- Shell 脚本(批量文件操作)
|
||||
- Python 脚本(图片处理、文本替换)
|
||||
|
||||
### 设计工具
|
||||
- Sketch/Figma(重新设计 UI)
|
||||
- ImageMagick(批量图片处理)
|
||||
- Lottie/SVGA Player(动画资源处理)
|
||||
|
||||
### 测试工具
|
||||
- Charles/Proxyman(抓包测试网络)
|
||||
- Instruments(性能和内存测试)
|
||||
- Xcode UI Testing(自动化测试)
|
||||
|
||||
---
|
||||
|
||||
**制定人**: Linus Mode AI
|
||||
**创建时间**: 2025-10-09
|
||||
**预计完成时间**: 2025-11-09(30天)
|
||||
|
Reference in New Issue
Block a user