Swift学习笔记45-使用UIScene管理窗口的生命周期+半自定义TabBar
纯代码布局的具体操作步骤如果你想使用纯代码进行界面开发请按以下 3 个步骤操作1. 删除 Storyboard 文件在 Xcode 左侧导航栏中选中Main.storyboard或Main按Delete并选择Move to Trash移至废纸篓。2. 清除 Info.plist 中的 Main 引用因为系统默认会尝试从Main.storyboard加载初始界面需要删掉对应的键值打开Info.plist或在 Target 的Info选项卡下。展开Application Scene Manifest-Scene Configuration-Application Session Role-Item 0 (Default Configuration)。找到Storyboard Name这一项值为Main点击减号 将其删除。若存在在根节点下查找Main storyboard file base name如果存在也一并删除。3. 在SceneDelegate.swift中配置根视图控制器打开SceneDelegate.swift找到scene(_:willConnectTo:options:)方法修改为如下代码func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // 1. 确保 scene 可以转换为 UIWindowScene guard let windowScene (scene as? UIWindowScene) else { return } // 2. 使用 windowScene 初始化 UIWindow let window UIWindow(windowScene: windowScene) // 3. 设置你的根视图控制器例如 ViewController或者带有 NavigationController 的视图 let mainVC ViewController() let navVC UINavigationController(rootViewController: mainVC) window.rootViewController navVC // 4. 保存 window 并显示 self.window window window.makeKeyAndVisible() }完成上述配置后重新运行项目系统就会直接加载你用代码指定的ViewController不再依赖任何 Storyboard。------自定义Tabbar删掉该删的这里省略恢复NavigationController的手势操作了func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene (scene as? UIWindowScene) else { return } let window UIWindow(windowScene: windowScene) let homeTabbar CustomTabbar() let nav BaseNavController(rootViewController: homeTabbar) window.rootViewController nav self.window window window.makeKeyAndVisible() }UIScene获取安全距离有些不同现代 iOS 中状态栏高度直接等同于主窗口的顶部安全区域safeAreaInsets.topextension UIWindowScene { /// 顶部状态栏高度刘海屏通常为 44~59pt非刘海屏为 20pt static var topSafeArea: CGFloat { let scene UIApplication.shared.connectedScenes .first { $0.activationState .foregroundActive } as? UIWindowScene ?? UIApplication.shared.connectedScenes.first as? UIWindowScene let window scene?.windows.first(where: { $0.isKeyWindow }) ?? scene?.windows.first return window?.safeAreaInsets.top ?? 20.0 } }诸如此类extension UIWindowScene { static var current: UIWindowScene? { return UIApplication.shared.connectedScenes .first { $0.activationState .foregroundActive } as? UIWindowScene ?? UIApplication.shared.connectedScenes.first as? UIWindowScene } static var bottomSafeArea: CGFloat { let firstScene UIApplication.shared.connectedScenes .first { $0.activationState .foregroundActive } as? UIWindowScene ?? UIApplication.shared.connectedScenes.first as? UIWindowScene let window firstScene?.windows.first let bottom window?.safeAreaInsets.bottom return bottom ?? 0 } // 底部 static var statusHeight: CGFloat { let firstScene UIApplication.shared.connectedScenes .first { $0.activationState .foregroundActive } as? UIWindowScene ?? UIApplication.shared.connectedScenes.first as? UIWindowScene let window firstScene?.windows.first let status window?.safeAreaInsets.top return status ?? 20.0 } // 顶部 static var currentBounds: CGRect { return current?.coordinateSpace.bounds ?? .zero } // 窗口大小 }-----整体思路就是在for的时候把要更改的控件存起来然后点击的时候再取出来切换样式。let iconNames: [String] [house, doc.text, person] let iconNamesSelected: [String] [house.fill, doc.text.fill, person.fill] let textNames: [String] [首页, 订单, 我的] private var iconBtn: [UIButton] [] private var iconImg: [UIImageView] [] private var iconLbl: [UILabel] []隐藏掉原来的TabBar把TabBarController所控制的VC加进去class CustomTabbar: UITabBarController { let iconNames: [String] [house, doc.text, person] let iconNamesSelected: [String] [house.fill, doc.text.fill, person.fill] let textNames: [String] [首页, 订单, 我的] private var iconBtn: [UIButton] [] private var iconImg: [UIImageView] [] private var iconLbl: [UILabel] [] override func viewDidLoad() { super.viewDidLoad() self.tabBar.isHidden true let homeVC FirstVC() let ordersVC SecondVC() let meVC ThirdVC() self.viewControllers [homeVC, ordersVC, meVC] setupUI() } }自定义的底部栏要什么样式都可以自己写private func setupUI() { let backView UIView(frame: .init(x: 0, y: SCREENHEIGHT - 64 - bottomSafeArea, width: SCREENWIDTH, height: bottomSafeArea 64)) backView.backgroundColor .white let w SCREENWIDTH / CGFloat(iconNames.count) for i in 0..iconNames.count { let btn UIButton(type: .custom) btn.tag i btn.frame .init(x: (CGFloat(i) * w), y: 0, width: w, height: 64) // btn.setTitle(textNames[i], for: .normal) let imgX CGFloat((w - 24) / 2) let imgY CGFloat((64 - 24 - 20) / 2) let btnImg UIImageView(frame: .init(x: imgX, y: imgY, width: 24, height: 24)) btnImg.image UIImage(systemName: iconNames[i]) btnImg.tintColor .lightGray iconImg.append(btnImg) btn.addSubview(btnImg) let btnLbl UILabel(frame: .init(x: imgX, y: imgY 24, width: 24, height: 20)) btnLbl.text textNames[i] btnLbl.textColor .lightGray btnLbl.font .systemFont(ofSize: 12) btnLbl.textAlignment .center iconLbl.append(btnLbl) btn.addSubview(btnLbl) btn.addSubview(btnImg) btn.backgroundColor .clear btn.setTitleColor(.lightGray, for: .normal) btn.addTarget(self, action: #selector(tabButtonTapped(sender:)), for: .touchUpInside) iconBtn.append(btn) backView.addSubview(btn) } view.addSubview(backView) print(bottomSafeArea) }核心用tag把按钮绑定到系统的TabBarController.selectedIndex中让系统组件帮你管理选项卡切换。然后再刷新一下UIobjc private func tabButtonTapped(sender: UIButton) { let newIndex sender.tag if self.selectedIndex newIndex { return } let oldIndex self.selectedIndex self.selectedIndex newIndex iconImg[oldIndex].tintColor .lightGray iconImg[newIndex].tintColor .orange iconLbl[oldIndex].textColor .lightGray iconLbl[newIndex].textColor .orange UIView.animate(withDuration: 0.2) { self.iconBtn[oldIndex].transform .identity self.iconBtn[newIndex].transform CGAffineTransform(scaleX: 1.1, y: 1.1) } }

相关新闻

uni-app x微信小程序分享功能完全指南:从转发到朋友圈避坑

uni-app x微信小程序分享功能完全指南:从转发到朋友圈避坑

做微信小程序分享功能,我见过太多人把代码一贴就以为完事,结果真机一测就露馅:右上角转发点了提示“当前页面未设置分享”,朋友圈入口干脆不显示。说白了,分享不是“写个方法”那么简单,而是要把微信小程序…

2026/10/5 7:30:55 阅读更多 →
AI-Native SDLC实战:Claude Code、CLAUDE.md与MCP协同开发指南

AI-Native SDLC实战:Claude Code、CLAUDE.md与MCP协同开发指南

1. 从“写代码”到“指挥AI写代码”:AI-Native SDLC到底改变了什么这两年但凡在一线写代码的人,应该都有一个共同的体感:工具链的迭代速度已经快到让人有点喘不过气。以前我们聊的是“用哪个IDE”“装哪些插件”,现在聊的是“你的…

2026/10/4 3:50:57 阅读更多 →
Android以太网开关底层原理:从UI点击到PHY寄存器写入全链路解析

Android以太网开关底层原理:从UI点击到PHY寄存器写入全链路解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 8:09:05 阅读更多 →

最新新闻

Java零基础入门:周末大总结

Java零基础入门:周末大总结

import java.util.Scanner; public class SummaryTry { public static void main(String[] args) { 综合编程题 11:简易学生成绩管理系统(控制台版)】 结合本周所有知识点,完成以下需求: 使用 Scanner 输入3名学生的姓…

2026/10/5 9:23:55 阅读更多 →
AI辅助芯片选型:从痛点拆解到实战工作流

AI辅助芯片选型:从痛点拆解到实战工作流

芯片选型的AI工具,现在其实是个“看着热闹、用着别扭”的领域。真干过硬件的人都知道,上午还在为选一颗合适的LDO翻三个分销商网站,下午就可能因为某颗MCU的交期变成52周而推翻整版方案。最近AI工具的声量很大,但能正经回答“帮我…

2026/10/5 9:23:55 阅读更多 →
自注意力+对抗生成网络:深度子空间聚类实战解析

自注意力+对抗生成网络:深度子空间聚类实战解析

简介:一份关于基于自注意力对抗的深度子空间聚类研究的Word文档,面向机器学习、计算机视觉等领域的研究者和学习者,系统阐述如何融合自注意力机制与对抗网络来提升高维数据聚类的鲁棒性。文档从聚类基础与经典算法讲起,涵盖稀疏子…

2026/10/5 9:23:55 阅读更多 →
分数阶时滞神经网络稳定性分析:Caputo导数、LMI判据与数值验证

分数阶时滞神经网络稳定性分析:Caputo导数、LMI判据与数值验证

简介:这份PDF文献《含有离散时滞及分布时滞分数阶神经网络的渐近稳定性分析》面向从事神经网络、深度学习与数据建模方向的研究生、教师及工程技术人员,聚焦分数阶神经网络在同时含离散时滞与分布时滞情形下的渐近稳定性难题。文章在Caputo导数意义下构造…

2026/10/5 9:23:55 阅读更多 →
从爬虫到可视化:豆瓣读书Top250全栈数据项目实战

从爬虫到可视化:豆瓣读书Top250全栈数据项目实战

做这个豆瓣读书 Top250 全栈数据项目,起因其实特别朴素,就是想给简历里添一个能从头讲到尾的实战项目。python爬虫负责采数据,pandas做数据清洗,MySQL落库,Java后端提供接口,前端再用ECharts把图书数据渲染…

2026/10/5 9:23:55 阅读更多 →
Google AX 开源:用声明式 YAML 编排十亿级 AI Agent 任务

Google AX 开源:用声明式 YAML 编排十亿级 AI Agent 任务

1. 从“一天一个开源项目”聊起:为什么 AX 值得单独写一篇做 Agent 开发这两年,我最大的感受就是:写一个能跑的 Agent 不难,难的是让一千个、一万个甚至更多 Agent 稳定地跑起来、跑对、跑完还能查账。单机跑个 ReAct 循环&#x…

2026/10/5 9:22:54 阅读更多 →

日新闻

马斯克杀回智能体战场,Grok 4.5万亿参数撑腰,Cursor接手数字白领项目:用TaoToken统一Key跑通多模型Agent工作流

马斯克杀回智能体战场,Grok 4.5万亿参数撑腰,Cursor接手数字白领项目:用TaoToken统一Key跑通多模型Agent工作流

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 0:00:22 阅读更多 →
AI编程工具插件机制详解:plugin.json配置与加载失败排查指南

AI编程工具插件机制详解:plugin.json配置与加载失败排查指南

1. 从“plugins”这个词说起:它到底在解决什么问题如果你最近在折腾 AI 编程工具,尤其是 Cursor、Codex CLI、Claude Code 这类带 CLI 的编辑器或命令行助手,那你大概率绕不开一个词——plugins。这个词本身不新鲜,从浏览器到 IDE…

2026/10/5 0:00:23 阅读更多 →
第26课:OpenClaw|日志审计与问题诊断:把日志链路改到 TaoToken 的排查清单

第26课:OpenClaw|日志审计与问题诊断:把日志链路改到 TaoToken 的排查清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 0:00:23 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 5:06:42 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 1:10:22 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 3:06:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 11:40:45 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 9:43:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 20:14:29 阅读更多 →