Files
real-e-party-iOS/issues/scripts/4_tabbar_refactor_guide.md
edwinQQQ a35a711be6 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
2025-10-09 16:19:14 +08:00

16 KiB
Raw Permalink Blame History

TabBar 结构改动指南

📋 当前 TabBar 结构分析

基于 TabbarViewController.h/m 的分析,当前项目的 TabBar 包含以下模块:

现有结构(推测)

  1. 首页 (Home) - YMNewHome
  2. 消息 (Message) - YMMessage
  3. 动态 (Moments) - YMMonents
  4. 我的 (Mine) - YMMine

可能还有一个中间的"发布"按钮(常见于直播/社交应用)。


🎯 改造目标

通过调整 TabBar 的结构、顺序、样式,让 App 在使用体验和视觉上都和原项目有明显区别。


🔄 改造方案

方案A: 改变顺序

原顺序:首页 → 消息 → [发布] → 动态 → 我的

新顺序:动态 → 首页 → [发布] → 我的 → 消息

代码修改

// 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将其整合到首页或消息中。

新结构:首页 → 消息 → 我的 → 设置

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSArray *viewControllers = @[
        [self createHomeVC],            // 首页
        [self createMessageVC],         // 消息
        [self createMineVC],            // 我的
        [self createSettingsVC]         // 新增:设置
    ];
    
    self.viewControllers = viewControllers;
    
    // 移除中间的凸起按钮
    // self.customTabBar = nil;
}

方案C: 完全重新设计4个→6个

增加更多功能入口,显得是不同的产品定位。

新结构:发现 → 直播 → 短视频 → 消息 → 商城 → 我的

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSArray *viewControllers = @[
        [self createDiscoverVC],        // 发现(重组的首页)
        [self createLiveVC],            // 直播(原首页的一部分)
        [self createVideoVC],           // 短视频(新增或从动态拆分)
        [self createMessageVC],         // 消息
        [self createShopVC],            // 商城(新增或从我的拆分)
        [self createMineVC]             // 我的
    ];
    
    self.viewControllers = viewControllers;
}

🎨 视觉改造

1. TabBar 样式改变

方案1: 平面风格 → 卡片风格

// 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: 固定底部 → 浮动底部

- (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: 缩放动画

// 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: 上移 + 高亮

- (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: 移除凸起,改为平齐

// 移除原有的自定义中间按钮逻辑
// 使用普通的 TabBarItem

方案2: 改变凸起样式

// 原: 圆形凸起
// 新: 六边形凸起

- (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 文件

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 初始化逻辑

// 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需要全局搜索并更新

# 搜索所有 TabBar 跳转代码
grep -rn "selectedIndex" YuMi/Modules --include="*.m"

# 常见的模式
grep -rn "tabBarController.selectedIndex = " YuMi/Modules --include="*.m"

建议:创建一个枚举来管理 Tab index而不是硬编码数字

// TabbarViewController.h

typedef NS_ENUM(NSInteger, AppTabIndex) {
    AppTabIndexMoments = 0,
    AppTabIndexHome = 1,
    AppTabIndexPublish = 2,
    AppTabIndexMine = 3,
    AppTabIndexMessage = 4
};

// 使用
self.tabBarController.selectedIndex = AppTabIndexHome;

步骤4: 更新推送和深度链接

如果有推送通知或深度链接跳转到特定 Tab也需要更新

// 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. 图标位置变化

// 图标上移,文字下移
- (void)setupTabBarItemLayout {
    for (UIViewController *vc in self.viewControllers) {
        vc.tabBarItem.imageInsets = UIEdgeInsetsMake(-2, 0, 2, 0);
        vc.tabBarItem.titlePositionAdjustment = UIOffsetMake(0, 2);
    }
}

📱 响应式适配

确保 TabBar 在不同设备上都正常显示:

- (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. 添加震动反馈

#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. 添加粒子效果

- (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 动画图标

#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 审查中最直观的差异点之一!🎉