From d1d47336c2e8027a3eb2cd24af9c244a76f47f63 Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Mon, 19 Jan 2026 19:14:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=84=E7=90=86tabbar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- keyBoard.xcodeproj/project.pbxproj | 6 + keyBoard/Class/AiTalk/VC/KBAiMainVC.h | 2 +- keyBoard/Class/AiTalk/VC/KBAiMainVC.m | 66 +++---- keyBoard/Class/Base/V/KBCustomTabBar.h | 32 +++ keyBoard/Class/Base/V/KBCustomTabBar.m | 159 +++++++++++++++ .../Class/Base/VC/BaseNavigationController.m | 135 +++++++------ keyBoard/Class/Base/VC/BaseTabBarController.m | 183 +++++++----------- 7 files changed, 365 insertions(+), 218 deletions(-) create mode 100644 keyBoard/Class/Base/V/KBCustomTabBar.h create mode 100644 keyBoard/Class/Base/V/KBCustomTabBar.m diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index d91e7a6..57d99a3 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -194,6 +194,7 @@ 04C6EADD2EAF8CEB0089C901 /* KBToolBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 04C6EADC2EAF8CEB0089C901 /* KBToolBar.m */; }; 04D1F6B22EDFF10A00B12345 /* KBSkinInstallBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 04D1F6B12EDFF10A00B12345 /* KBSkinInstallBridge.m */; }; 04D1F6B32EDFF10A00B12345 /* KBSkinInstallBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 04D1F6B12EDFF10A00B12345 /* KBSkinInstallBridge.m */; }; + 04E0383E2F1A7C30002CA5A0 /* KBCustomTabBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E0383D2F1A7C30002CA5A0 /* KBCustomTabBar.m */; }; 04E161832F10E6470022C23B /* normal_hei_them.zip in Resources */ = {isa = PBXBuildFile; fileRef = 04E161812F10E6470022C23B /* normal_hei_them.zip */; }; 04E161842F10E6470022C23B /* normal_them.zip in Resources */ = {isa = PBXBuildFile; fileRef = 04E161822F10E6470022C23B /* normal_them.zip */; }; 04FC95672EB0546C007BD342 /* KBKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95652EB0546C007BD342 /* KBKey.m */; }; @@ -605,6 +606,8 @@ 04C6EAE12EAF940F0089C901 /* KBPermissionViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBPermissionViewController.m; sourceTree = ""; }; 04D1F6B02EDFF10A00B12345 /* KBSkinInstallBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinInstallBridge.h; sourceTree = ""; }; 04D1F6B12EDFF10A00B12345 /* KBSkinInstallBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinInstallBridge.m; sourceTree = ""; }; + 04E0383C2F1A7C30002CA5A0 /* KBCustomTabBar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBCustomTabBar.h; sourceTree = ""; }; + 04E0383D2F1A7C30002CA5A0 /* KBCustomTabBar.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBCustomTabBar.m; sourceTree = ""; }; 04E161812F10E6470022C23B /* normal_hei_them.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = normal_hei_them.zip; sourceTree = ""; }; 04E161822F10E6470022C23B /* normal_them.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = normal_them.zip; sourceTree = ""; }; 04FC953A2EAFAE56007BD342 /* KeyBoardPrefixHeader.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyBoardPrefixHeader.pch; sourceTree = ""; }; @@ -1637,6 +1640,8 @@ 04FC95D62EB1EA16007BD342 /* BaseTableView.m */, 048908FC2EC0CC2400FABA60 /* UIScrollView+KBEmptyView.h */, 048908FD2EC0CC2400FABA60 /* UIScrollView+KBEmptyView.m */, + 04E0383C2F1A7C30002CA5A0 /* KBCustomTabBar.h */, + 04E0383D2F1A7C30002CA5A0 /* KBCustomTabBar.m */, ); path = V; sourceTree = ""; @@ -2312,6 +2317,7 @@ 048908F52EC0496400FABA60 /* KBShopItemVC.m in Sources */, 04FC95F42EB339C1007BD342 /* AppleSignInManager.m in Sources */, 04C6EAC12EAF86530089C901 /* ViewController.m in Sources */, + 04E0383E2F1A7C30002CA5A0 /* KBCustomTabBar.m in Sources */, A1B2C4002EB4A0A100000004 /* KBAuthManager.m in Sources */, A1B2C4212EB4B7A100000001 /* KBKeyboardPermissionManager.m in Sources */, 0498BD682EE01180006CC1D5 /* KBEmailRegistVC.m in Sources */, diff --git a/keyBoard/Class/AiTalk/VC/KBAiMainVC.h b/keyBoard/Class/AiTalk/VC/KBAiMainVC.h index f9e8065..d1c4ea7 100644 --- a/keyBoard/Class/AiTalk/VC/KBAiMainVC.h +++ b/keyBoard/Class/AiTalk/VC/KBAiMainVC.h @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN /// AI 语音陪伴聊天主界面 -@interface KBAiMainVC : UIViewController +@interface KBAiMainVC : BaseViewController @end diff --git a/keyBoard/Class/AiTalk/VC/KBAiMainVC.m b/keyBoard/Class/AiTalk/VC/KBAiMainVC.m index 4b6dab9..624cbed 100644 --- a/keyBoard/Class/AiTalk/VC/KBAiMainVC.m +++ b/keyBoard/Class/AiTalk/VC/KBAiMainVC.m @@ -46,6 +46,11 @@ [self setupOrchestrator]; } +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + // TabBar 背景色由 BaseTabBarController 统一管理,这里不需要设置 +} + - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; @@ -56,55 +61,16 @@ - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; - // 设置黑色渐变层(从底部到顶部,黑色到透明) - if (!self.gradientLayer) { - self.gradientLayer = [CAGradientLayer layer]; - self.gradientLayer.startPoint = CGPointMake(0.53, 1); // 底部 - self.gradientLayer.endPoint = CGPointMake(0.54, 0); // 顶部 - // 渐变颜色:从黑色 24% 不透明度到透明 - self.gradientLayer.colors = @[ - (__bridge id)[UIColor colorWithRed:0 / 255.0 - green:0 / 255.0 - blue:0 / 255.0 - alpha:0.24] - .CGColor, - (__bridge id)[UIColor colorWithRed:3 / 255.0 - green:3 / 255.0 - blue:3 / 255.0 - alpha:0] - .CGColor - ]; - self.gradientLayer.locations = @[ @(0.7), @(1.0) ]; - [self.tabbarBackgroundView.layer addSublayer:self.gradientLayer]; + // 只更新 mask 的 frame(mask 已在 setupUI 中创建) + if (self.blurEffectView.layer.mask) { + self.blurEffectView.layer.mask.frame = self.blurEffectView.bounds; } - - // 更新黑色渐变层的 frame - self.gradientLayer.frame = self.tabbarBackgroundView.bounds; - - // 为 blurEffectView 添加透明度渐变 - // mask(从底部到中间不透明,从中间到顶部透明) - if (!self.blurEffectView.layer.mask) { - CAGradientLayer *maskLayer = [CAGradientLayer layer]; - maskLayer.startPoint = CGPointMake(0.5, 1); // 底部 - maskLayer.endPoint = CGPointMake(0.5, 0); // 顶部 - // 底部到中间保持不透明,从中间到顶部过渡透明 - maskLayer.colors = @[ - (__bridge id)[UIColor whiteColor].CGColor, // 底部:完全不透明 - (__bridge id)[UIColor whiteColor].CGColor, // 中间:完全不透明 - (__bridge id)[UIColor clearColor].CGColor // 顶部:完全透明 - ]; - maskLayer.locations = @[ @(0.0), @(0.5), @(1.0) ]; - self.blurEffectView.layer.mask = maskLayer; - } - - // 更新 mask 的 frame - self.blurEffectView.layer.mask.frame = self.blurEffectView.bounds; } #pragma mark - UI Setup - (void)setupUI { - self.view.backgroundColor = [UIColor systemBackgroundColor]; + self.view.backgroundColor = [UIColor whiteColor]; self.title = @"AI 助手"; // 安全区域 @@ -131,7 +97,19 @@ self.blurEffectView.translatesAutoresizingMaskIntoConstraints = NO; [self.tabbarBackgroundView addSubview:self.blurEffectView]; - // 渐变层将在 viewDidLayoutSubviews 中设置 + // 为 blurEffectView 创建透明度渐变 + // mask(从底部到中间不透明,从中间到顶部透明) + CAGradientLayer *maskLayer = [CAGradientLayer layer]; + maskLayer.startPoint = CGPointMake(0.5, 1); // 底部 + maskLayer.endPoint = CGPointMake(0.5, 0); // 顶部 + // 底部到中间保持不透明,从中间到顶部过渡透明 + maskLayer.colors = @[ + (__bridge id)[UIColor whiteColor].CGColor, // 底部:完全不透明 + (__bridge id)[UIColor whiteColor].CGColor, // 中间:完全不透明 + (__bridge id)[UIColor clearColor].CGColor // 顶部:完全透明 + ]; + maskLayer.locations = @[ @(0.0), @(0.5), @(1.0) ]; + self.blurEffectView.layer.mask = maskLayer; // 状态标签 self.statusLabel = [[UILabel alloc] init]; diff --git a/keyBoard/Class/Base/V/KBCustomTabBar.h b/keyBoard/Class/Base/V/KBCustomTabBar.h new file mode 100644 index 0000000..61a708c --- /dev/null +++ b/keyBoard/Class/Base/V/KBCustomTabBar.h @@ -0,0 +1,32 @@ +// +// KBCustomTabBar.h +// keyBoard +// +// Created by Assistant on 2026/01/16. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class KBCustomTabBar; + +@protocol KBCustomTabBarDelegate + +- (void)customTabBar:(KBCustomTabBar *)tabBar + didSelectItemAtIndex:(NSInteger)index; + +@end + +@interface KBCustomTabBar : UIView + +@property(nonatomic, weak) id delegate; +@property(nonatomic, assign) NSInteger selectedIndex; + +- (instancetype)initWithItems:(NSArray *)items; +- (void)setSelectedIndex:(NSInteger)selectedIndex animated:(BOOL)animated; +- (void)setTransparentBackground:(BOOL)transparent; + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Base/V/KBCustomTabBar.m b/keyBoard/Class/Base/V/KBCustomTabBar.m new file mode 100644 index 0000000..68d6dd4 --- /dev/null +++ b/keyBoard/Class/Base/V/KBCustomTabBar.m @@ -0,0 +1,159 @@ +// +// KBCustomTabBar.m +// keyBoard +// +// Created by Assistant on 2026/01/16. +// + +#import "KBCustomTabBar.h" +#import "Masonry.h" + +@interface KBCustomTabBar () + +@property(nonatomic, strong) NSArray *items; +@property(nonatomic, strong) NSMutableArray *buttons; +@property(nonatomic, strong) UIView *backgroundView; + +@end + +@implementation KBCustomTabBar + +- (instancetype)initWithItems:(NSArray *)items { + self = [super initWithFrame:CGRectZero]; + if (self) { + _items = items; + _selectedIndex = 0; + _buttons = [NSMutableArray array]; + [self setupUI]; + } + return self; +} + +- (void)setupUI { + // 背景视图 + self.backgroundView = [[UIView alloc] init]; + self.backgroundView.backgroundColor = [UIColor whiteColor]; + [self addSubview:self.backgroundView]; + [self.backgroundView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(self); + }]; + + // 创建按钮 + UIStackView *stackView = [[UIStackView alloc] init]; + stackView.axis = UILayoutConstraintAxisHorizontal; + stackView.distribution = UIStackViewDistributionFillEqually; + stackView.alignment = UIStackViewAlignmentFill; + [self addSubview:stackView]; + [stackView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(self); + }]; + + for (NSInteger i = 0; i < self.items.count; i++) { + UITabBarItem *item = self.items[i]; + UIButton *button = [self createButtonWithItem:item index:i]; + [stackView addArrangedSubview:button]; + [self.buttons addObject:button]; + } + + // 设置初始选中状态 + [self updateButtonStates]; +} + +- (UIButton *)createButtonWithItem:(UITabBarItem *)item index:(NSInteger)index { + UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; + button.tag = index; + + // 设置图片 + [button setImage:item.image forState:UIControlStateNormal]; + [button setImage:item.selectedImage forState:UIControlStateSelected]; + + // 设置标题 + [button setTitle:item.title forState:UIControlStateNormal]; + [button setTitleColor:[UIColor grayColor] forState:UIControlStateNormal]; + [button setTitleColor:[UIColor blackColor] forState:UIControlStateSelected]; + button.titleLabel.font = [UIFont systemFontOfSize:10]; + + // 布局:图片在上,文字在下 + button.imageView.contentMode = UIViewContentModeScaleAspectFit; + button.titleLabel.textAlignment = NSTextAlignmentCenter; + + // 调整图片和文字位置 + CGFloat spacing = 4.0; + button.imageEdgeInsets = UIEdgeInsetsMake(-spacing, 0, spacing, 0); + button.titleEdgeInsets = UIEdgeInsetsMake( + spacing, -button.imageView.frame.size.width, -spacing, 0); + button.contentEdgeInsets = UIEdgeInsetsMake(spacing, 0, spacing, 0); + + // 垂直排列 + [button setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter]; + [button + setContentHorizontalAlignment:UIControlContentHorizontalAlignmentCenter]; + + [button addTarget:self + action:@selector(buttonTapped:) + forControlEvents:UIControlEventTouchUpInside]; + + return button; +} + +- (void)buttonTapped:(UIButton *)button { + NSInteger index = button.tag; + if (index != self.selectedIndex) { + self.selectedIndex = index; + [self updateButtonStates]; + + if ([self.delegate respondsToSelector:@selector(customTabBar: + didSelectItemAtIndex:)]) { + [self.delegate customTabBar:self didSelectItemAtIndex:index]; + } + } +} + +- (void)setSelectedIndex:(NSInteger)selectedIndex animated:(BOOL)animated { + if (_selectedIndex != selectedIndex) { + _selectedIndex = selectedIndex; + [self updateButtonStates]; + } +} + +- (void)setSelectedIndex:(NSInteger)selectedIndex { + [self setSelectedIndex:selectedIndex animated:NO]; +} + +- (void)updateButtonStates { + for (NSInteger i = 0; i < self.buttons.count; i++) { + UIButton *button = self.buttons[i]; + button.selected = (i == self.selectedIndex); + } +} + +- (void)setTransparentBackground:(BOOL)transparent { + if (transparent) { + self.backgroundView.backgroundColor = [UIColor clearColor]; + } else { + self.backgroundView.backgroundColor = [UIColor whiteColor]; + } +} + +- (void)layoutSubviews { + [super layoutSubviews]; + + // 调整按钮的图片和文字布局 + for (UIButton *button in self.buttons) { + CGFloat imageWidth = button.imageView.frame.size.width; + CGFloat imageHeight = button.imageView.frame.size.height; + CGFloat titleWidth = button.titleLabel.frame.size.width; + CGFloat titleHeight = button.titleLabel.frame.size.height; + + CGFloat spacing = 4.0; + + // 图片在上 + button.imageEdgeInsets = + UIEdgeInsetsMake(-(titleHeight + spacing), 0, 0, -titleWidth); + // 文字在下 + button.titleEdgeInsets = + UIEdgeInsetsMake(0, -imageWidth, -(imageHeight + spacing), 0); + } +} + +@end diff --git a/keyBoard/Class/Base/VC/BaseNavigationController.m b/keyBoard/Class/Base/VC/BaseNavigationController.m index 7aeb5c5..13f3757 100644 --- a/keyBoard/Class/Base/VC/BaseNavigationController.m +++ b/keyBoard/Class/Base/VC/BaseNavigationController.m @@ -14,84 +14,97 @@ @implementation BaseNavigationController - (void)viewDidLoad { - [super viewDidLoad]; - // 统一返回箭头样式与颜色 - UIImage *backImg = [UIImage imageNamed:@"back_black_icon"]; - if (backImg) { - self.navigationBar.backIndicatorImage = backImg; - self.navigationBar.backIndicatorTransitionMaskImage = backImg; - } - self.navigationBar.tintColor = [UIColor blackColor]; // 箭头/按钮的着色 + [super viewDidLoad]; + // 统一返回箭头样式与颜色 + UIImage *backImg = [UIImage imageNamed:@"back_black_icon"]; + if (backImg) { + self.navigationBar.backIndicatorImage = backImg; + self.navigationBar.backIndicatorTransitionMaskImage = backImg; + } + self.navigationBar.tintColor = [UIColor blackColor]; // 箭头/按钮的着色 - if (@available(iOS 14.0, *)) { - self.navigationBar.topItem.backButtonDisplayMode = UINavigationItemBackButtonDisplayModeMinimal; - } + if (@available(iOS 14.0, *)) { + self.navigationBar.topItem.backButtonDisplayMode = + UINavigationItemBackButtonDisplayModeMinimal; + } - // 统一开启“边缘右滑返回”(interactivePopGestureRecognizer 在自定义导航栏/隐藏系统导航栏场景下可能失效) - if (self.interactivePopGestureRecognizer) { - self.interactivePopGestureRecognizer.delegate = self; - self.interactivePopGestureRecognizer.enabled = YES; - } + // 统一开启“边缘右滑返回”(interactivePopGestureRecognizer + // 在自定义导航栏/隐藏系统导航栏场景下可能失效) + if (self.interactivePopGestureRecognizer) { + self.interactivePopGestureRecognizer.delegate = self; + self.interactivePopGestureRecognizer.enabled = YES; + } } -- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{ - if (self.viewControllers.count > 0) { - viewController.hidesBottomBarWhenPushed = true; - UIViewController *prev = self.topViewController; - if (@available(iOS 14.0, *)) { - prev.navigationItem.backButtonDisplayMode = UINavigationItemBackButtonDisplayModeMinimal; - } else { - prev.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil]; - } +- (void)pushViewController:(UIViewController *)viewController + animated:(BOOL)animated { + if (self.viewControllers.count > 0) { + viewController.hidesBottomBarWhenPushed = true; + UIViewController *prev = self.topViewController; + if (@available(iOS 14.0, *)) { + prev.navigationItem.backButtonDisplayMode = + UINavigationItemBackButtonDisplayModeMinimal; + } else { + prev.navigationItem.backBarButtonItem = + [[UIBarButtonItem alloc] initWithTitle:@"" + style:UIBarButtonItemStylePlain + target:nil + action:nil]; } - [super pushViewController:viewController animated:animated]; + } + [super pushViewController:viewController animated:animated]; } #pragma mark - UIGestureRecognizerDelegate - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { - if (gestureRecognizer == self.interactivePopGestureRecognizer) { - return self.viewControllers.count > 1; - } - return YES; + if (gestureRecognizer == self.interactivePopGestureRecognizer) { + return self.viewControllers.count > 1; + } + return YES; } /// Push 一个 VC,若栈中已存在相同 class 的 VC,则先移除旧的再 push 新的。 -- (void)kb_pushViewControllerRemovingSameClass:(UIViewController *)viewController - animated:(BOOL)animated { - if (!viewController) { return; } +- (void)kb_pushViewControllerRemovingSameClass: + (UIViewController *)viewController + animated:(BOOL)animated { + if (!viewController) { + return; + } - NSMutableArray *stack = self.viewControllers.mutableCopy; - // 移除同 class 的旧 VC(通常只有一个,这里按需移除第一个) - UIViewController *toRemove = nil; - for (UIViewController *vc in stack) { - if ([vc isKindOfClass:[viewController class]]) { - toRemove = vc; - break; - } + NSMutableArray *stack = self.viewControllers.mutableCopy; + // 移除同 class 的旧 VC(通常只有一个,这里按需移除第一个) + UIViewController *toRemove = nil; + for (UIViewController *vc in stack) { + if ([vc isKindOfClass:[viewController class]]) { + toRemove = vc; + break; } - if (toRemove) { - [stack removeObject:toRemove]; + } + if (toRemove) { + [stack removeObject:toRemove]; + } + + // 追加新 VC + [stack addObject:viewController]; + + // 对前一个 VC 做统一的返回按钮样式处理,与 pushViewController: 保持一致 + if (stack.count > 1) { + viewController.hidesBottomBarWhenPushed = YES; + UIViewController *prev = stack[stack.count - 2]; + if (@available(iOS 14.0, *)) { + prev.navigationItem.backButtonDisplayMode = + UINavigationItemBackButtonDisplayModeMinimal; + } else { + prev.navigationItem.backBarButtonItem = + [[UIBarButtonItem alloc] initWithTitle:@"" + style:UIBarButtonItemStylePlain + target:nil + action:nil]; } + } - // 追加新 VC - [stack addObject:viewController]; - - // 对前一个 VC 做统一的返回按钮样式处理,与 pushViewController: 保持一致 - if (stack.count > 1) { - viewController.hidesBottomBarWhenPushed = YES; - UIViewController *prev = stack[stack.count - 2]; - if (@available(iOS 14.0, *)) { - prev.navigationItem.backButtonDisplayMode = UINavigationItemBackButtonDisplayModeMinimal; - } else { - prev.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" - style:UIBarButtonItemStylePlain - target:nil - action:nil]; - } - } - - [self setViewControllers:stack animated:animated]; + [self setViewControllers:stack animated:animated]; } @end diff --git a/keyBoard/Class/Base/VC/BaseTabBarController.m b/keyBoard/Class/Base/VC/BaseTabBarController.m index ba87094..086f09f 100644 --- a/keyBoard/Class/Base/VC/BaseTabBarController.m +++ b/keyBoard/Class/Base/VC/BaseTabBarController.m @@ -15,13 +15,12 @@ #import "BaseNavigationController.h" #import "KBAuthManager.h" -@interface BaseTabBarController () +#import "KBCustomTabBar.h" +#import "Masonry.h" +@interface BaseTabBarController () -/// 原始的 TabBar appearance(用于恢复) -@property(nonatomic, strong) - UITabBarAppearance *originalAppearance API_AVAILABLE(ios(13.0)); -@property(nonatomic, strong) - UITabBarAppearance *transparentAppearance API_AVAILABLE(ios(13.0)); +/// 自定义 TabBar +@property(nonatomic, strong) KBCustomTabBar *customTabBar; @end @@ -32,8 +31,6 @@ self.delegate = self; - [self setupTabbarAppearance]; - // 组装 4 个 Tab:首页 / 商城 / 社区 / 我的 // 图标位于 Assets.xcassets/Tabbar 下:tab_home/tab_home_selected 等 @@ -70,7 +67,10 @@ image:@"tab_my" selectedImg:@"tab_my_selected"]; - self.viewControllers = @[ navHome, navShop, aiMainVC, navMy ]; + self.viewControllers = @[ navHome, navShop, navCommunity, navMy ]; + + // 不隐藏系统 TabBar,而是将自定义视图添加到 TabBar 上 + [self setupCustomTabBarOnNativeTabBar]; // 测试储存Token // [[KBAuthManager shared] saveAccessToken:@"TEST" refreshToken:nil @@ -79,66 +79,60 @@ // [[KBAuthManager shared] signOut]; } -- (void)setupTabbarAppearance { - // 让 TabBar 不透明,子控制器 view 不再延伸到 TabBar 下 - self.tabBar.translucent = NO; - if (@available(iOS 15.0, *)) { - UITabBarAppearance *a = [UITabBarAppearance new]; - [a configureWithOpaqueBackground]; - a.backgroundColor = [UIColor whiteColor]; - // 设置选中标题为黑色(iOS 15+ 需通过 appearance) - NSDictionary *selAttr = - @{NSForegroundColorAttributeName : [UIColor blackColor]}; - a.stackedLayoutAppearance.selected.titleTextAttributes = selAttr; - a.inlineLayoutAppearance.selected.titleTextAttributes = selAttr; - a.compactInlineLayoutAppearance.selected.titleTextAttributes = selAttr; - self.tabBar.standardAppearance = a; - self.tabBar.scrollEdgeAppearance = a; +- (void)setupCustomTabBarOnNativeTabBar { + // 1. 持续隐藏原生 TabBar 的所有按钮 + [self hideNativeTabBarButtons]; - // 保存原始 appearance - self.originalAppearance = a; - - // 创建透明 appearance - UITabBarAppearance *transparentA = [UITabBarAppearance new]; - [transparentA configureWithTransparentBackground]; - transparentA.backgroundColor = [UIColor clearColor]; - transparentA.stackedLayoutAppearance.selected.titleTextAttributes = selAttr; - transparentA.inlineLayoutAppearance.selected.titleTextAttributes = selAttr; - transparentA.compactInlineLayoutAppearance.selected.titleTextAttributes = - selAttr; - self.transparentAppearance = transparentA; - } else if (@available(iOS 13.0, *)) { - UITabBarAppearance *a = [UITabBarAppearance new]; - [a configureWithOpaqueBackground]; - a.backgroundColor = [UIColor whiteColor]; - NSDictionary *selAttr = - @{NSForegroundColorAttributeName : [UIColor blackColor]}; - a.stackedLayoutAppearance.selected.titleTextAttributes = selAttr; - a.inlineLayoutAppearance.selected.titleTextAttributes = selAttr; - a.compactInlineLayoutAppearance.selected.titleTextAttributes = selAttr; - self.tabBar.standardAppearance = a; - self.tabBar.tintColor = [UIColor blackColor]; - - // 保存原始 appearance - self.originalAppearance = a; - - // 创建透明 appearance - UITabBarAppearance *transparentA = [UITabBarAppearance new]; - [transparentA configureWithTransparentBackground]; - transparentA.backgroundColor = [UIColor clearColor]; - transparentA.stackedLayoutAppearance.selected.titleTextAttributes = selAttr; - transparentA.inlineLayoutAppearance.selected.titleTextAttributes = selAttr; - transparentA.compactInlineLayoutAppearance.selected.titleTextAttributes = - selAttr; - self.transparentAppearance = transparentA; - } else { - // 老系统用 tintColor/appearance 做回退 - self.tabBar.tintColor = [UIColor blackColor]; - [[UITabBarItem appearance] setTitleTextAttributes:@{ - NSForegroundColorAttributeName : [UIColor blackColor] - } - forState:UIControlStateSelected]; + // 2. 创建自定义 TabBar + NSMutableArray *items = [NSMutableArray array]; + for (UIViewController *vc in self.viewControllers) { + [items addObject:vc.tabBarItem]; } + + self.customTabBar = [[KBCustomTabBar alloc] initWithItems:items]; + self.customTabBar.delegate = self; + + // 3. 设置 frame 覆盖整个 TabBar 区域 + self.customTabBar.frame = self.tabBar.bounds; + + // 4. 使用 autoresizingMask 自动适配尺寸变化 + self.customTabBar.autoresizingMask = + UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + + // 5. 将自定义 TabBar 添加到原生 TabBar 上 + [self.tabBar addSubview:self.customTabBar]; + + // 6. 禁用原生 TabBar 的交互,只让自定义视图响应 + self.tabBar.userInteractionEnabled = YES; + for (UIView *subview in self.tabBar.subviews) { + if (subview != self.customTabBar && + ![NSStringFromClass([subview class]) containsString:@"Background"]) { + subview.userInteractionEnabled = NO; + subview.hidden = YES; + } + } + + // 7. 设置初始背景色(默认选中索引0,白色背景) + [self.customTabBar setTransparentBackground:NO]; +} + +- (void)hideNativeTabBarButtons { + // 移除或隐藏原生 TabBar 的所有子视图(按钮等) + for (UIView *subview in self.tabBar.subviews) { + // 保留系统的 _UIBarBackground 背景视图,隐藏其他视图 + if (![NSStringFromClass([subview class]) containsString:@"Background"] && + subview != self.customTabBar) { + subview.hidden = YES; + subview.alpha = 0; + } + } +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + // 每次视图即将出现时,确保原生按钮被隐藏 + [self hideNativeTabBarButtons]; } // 统一构造 TabBarItem,原图渲染,避免被系统 Tint 着色 @@ -155,59 +149,24 @@ return item; } -#pragma mark - UITabBarControllerDelegate +#pragma mark - KBCustomTabBarDelegate -- (BOOL)tabBarController:(UITabBarController *)tabBarController - shouldSelectViewController:(UIViewController *)viewController { - // 已经是当前选中的 tab,直接允许(避免重复拦截) - if (tabBarController.selectedViewController == viewController) { - return YES; - } - - NSUInteger index = - [tabBarController.viewControllers indexOfObject:viewController]; - if (index == NSNotFound) { - return YES; - } - - // “我的” tab 默认为第 4 个(索引 3),未登录时先跳转登录页 +- (void)customTabBar:(KBCustomTabBar *)tabBar + didSelectItemAtIndex:(NSInteger)index { + // 登录检查 if ((index == 1 || index == 2) && ![KBUserSessionManager shared].isLoggedIn) { [[KBUserSessionManager shared] goLoginVC]; - return NO; + return; } - return YES; -} + // 切换 VC + self.selectedIndex = index; -- (void)tabBarController:(UITabBarController *)tabBarController - didSelectViewController:(UIViewController *)viewController { - NSUInteger index = - [tabBarController.viewControllers indexOfObject:viewController]; - - // 第三个 tab(索引2)是 KBAiMainVC,背景透明 + // 设置 TabBar 背景(索引2是 KBAiMainVC) if (index == 2) { - if (@available(iOS 13.0, *)) { - self.tabBar.standardAppearance = self.transparentAppearance; - if (@available(iOS 15.0, *)) { - self.tabBar.scrollEdgeAppearance = self.transparentAppearance; - } - } else { - // iOS 13 以下,直接设置背景色 - self.tabBar.barTintColor = [UIColor clearColor]; - self.tabBar.backgroundColor = [UIColor clearColor]; - } + [self.customTabBar setTransparentBackground:YES]; } else { - // 其他 tab,恢复原始背景 - if (@available(iOS 13.0, *)) { - self.tabBar.standardAppearance = self.originalAppearance; - if (@available(iOS 15.0, *)) { - self.tabBar.scrollEdgeAppearance = self.originalAppearance; - } - } else { - // iOS 13 以下,恢复白色背景 - self.tabBar.barTintColor = [UIColor whiteColor]; - self.tabBar.backgroundColor = [UIColor whiteColor]; - } + [self.customTabBar setTransparentBackground:NO]; } }