[{"content":"前言 你有没有遇到过这种场景：\n用户 A 分享了一个邀请链接给用户 B。用户 B 在手机上点击链接，跳转到 App Store 下载了 App。安装打开后——邀请码呢？\n传统方案要么依赖复杂的 Deep Link 配置（Universal Link / App Links），要么需要接入 Firebase Dynamic Links 这类第三方服务。前者配置繁琐，后者有被 deprecate 的风险。\n这就是我开源 share-installs 的原因。\n什么是 share-installs？ share-installs 是一个开源、自托管的 邀请码 \u0026amp; 延迟深度链接归因系统。\n核心能力只有一句话：用户点击邀请链接并安装 App 后，首次启动时邀请码会自动识别并回填——即使点击时 App 尚未安装。\n项目地址：github.com/ceeyang/share-installs\n⭐ 核心功能：浏览器指纹 → 后端匹配 → App 启动自动回填 🖥️ 三端 SDK：Web（TypeScript）+ iOS（Swift）+ Android（Kotlin） 🐳 一键部署：Docker Compose / Kubernetes / 裸机 🔐 自托管：数据存在你自己的服务器上 工作原理 整个流程分为四个阶段：\n[阶段 1] 用户 A 分享邀请链接 │ ▼ [阶段 2] 用户 B 点击链接 → 你的落地页 │ JS SDK 上报浏览器指纹到后端 ▼ [阶段 3] 跳转 App Store / Play Store │ ▼ [阶段 4] 用户 B 安装并打开 App │ 移动 SDK 上报设备指纹 ▼ 后端匹配指纹 → 返回邀请码 │ ▼ App 自动填入邀请码 ✓ 关键技术点：\n指纹匹配：浏览器和设备的特征组合（时区、语言、屏幕尺寸、硬件参数等）被视为同一用户的指纹 时间窗口：默认 72 小时内，点击事件可被匹配 置信度阈值：模糊匹配默认 0.75，可配置 职责边界： share-installs 只负责指纹收集和匹配。邀请码的生成、跳转商店、使用次数统计等业务逻辑由你的系统处理。\n快速启动 Docker 一键启动（推荐） git clone https://github.com/ceeyang/share-installs.git cd share-installs # 使用默认配置启动全部服务 docker compose up --build # 验证 curl http://localhost:6066/api/health # {\u0026#34;status\u0026#34;:\u0026#34;ok\u0026#34;,\u0026#34;version\u0026#34;:\u0026#34;1.0.0\u0026#34;,\u0026#34;timestamp\u0026#34;:\u0026#34;...\u0026#34;} 本地调试模式（Docker 基础设施 + 本地代码热重载） 最适合有断点调试需求的日常开发：\n# 1. 启动数据库 + 缓存 docker compose up db redis -d # 2. 安装依赖 cd backend \u0026amp;\u0026amp; npm install # 3. 初始化数据库 npm run db:generate npx prisma migrate dev --name init # 4. 启动热重载开发服务器 npm run dev # 服务运行在 http://localhost:6066 环境变量快速参考 变量 默认值 说明 MULTI_TENANT false 自部署模式 / SaaS 模式 ADMIN_SECRET 空（不鉴权） 项目管理端点保护密钥 FINGERPRINT_MATCH_THRESHOLD 0.75 匹配最低置信度 FINGERPRINT_MATCH_TTL_HOURS 72 匹配时间窗口（小时） SDK 集成 Web 端（邀请落地页） import { ShareInstallsSDK } from \u0026#39;@share-installs/js-sdk\u0026#39;; const sdk = new ShareInstallsSDK({ apiBaseUrl: \u0026#39;https://你的域名/api\u0026#39;, }); // 从 URL 提取邀请码 const inviteCode = location.pathname.split(\u0026#39;/\u0026#39;).pop()!; // 上报指纹 await sdk.trackClick(inviteCode, { customData: { campaign: \u0026#39;summer2024\u0026#39; }, }); // 然后由你的代码跳转 App Store / Play Store iOS（Swift） import ShareInstallsSDK // 在 AppDelegate 或 @main App 中初始化 ShareInstallsSDK.configure(with: ShareInstallsConfiguration( apiBaseURL: URL(string: \u0026#34;https://你的域名/api\u0026#34;)! )) // 首次启动后 resolve if let result = try await ShareInstallsSDK.shared.resolveDeferred() { // result.inviteCode — 邀请码 // result.customData — 落地页传入的自定义数据 applyInviteCode(result.inviteCode) } Android（Kotlin） // 在 Application.onCreate 中初始化 ShareInstallsSDK.configure( context = applicationContext, configuration = ShareInstallsConfiguration( apiBaseUrl = \u0026#34;https://你的域名/api\u0026#34; ) ) // 首次启动后 resolve val result = ShareInstallsSDK.instance.resolveDeferred() if (result != null) { applyInviteCode(result.inviteCode) } 部署选项 方式 适用场景 复杂度 Docker Compose 测试 \u0026amp; 自托管 ⭐ Docker Dev 日常开发调试 ⭐⭐ Docker Compose（生产） 小型生产部署 ⭐⭐ 裸机（Node.js + PG + Redis） 已有基础设施 ⭐⭐⭐ Kubernetes + Terraform（AWS） 生产环境 ⭐⭐⭐⭐ 项目结构一览 share-installs/ ├── backend/ # Express + Prisma + Redis REST API ├── sdk/ │ ├── ios/ # Swift SDK（SPM + CocoaPods） │ ├── android/ # Kotlin SDK │ └── js/ # TypeScript Web SDK（npm） ├── landing/ # Next.js 邀请落地页（可选） ├── dashboard/ # 管理仪表盘 ├── docs/ # 架构 \u0026amp; 集成文档 ├── infrastructure/ │ ├── k8s/ # Kubernetes manifests │ └── terraform/ # AWS 基础设施（EKS + RDS） ├── test/ # 内存测试服务器 \u0026amp; 测试页面 ├── docker-compose.yml └── docker-compose.dev.yml 后端技术栈：\n运行时：Node.js + TypeScript 框架：Express ORM：Prisma 缓存：Redis（用于指纹匹配加速 + 限流） 数据库：PostgreSQL 适用场景 社交裂变：App 用户邀请好友注册，自动归因 活动推广：通过邀请链接分发优惠券码 内测分发：控制邀请名额，追踪安装来源 渠道归因：区分不同推广渠道的下载来源 与其他方案对比 share-installs Firebase Dynamic Links Branch.io AppsFlyer 开源 ✅ ❌ ❌ ❌ 自托管 ✅ ❌ ❌ ❌ 免费 ✅ ✅（有额度） ❌ ❌ 多端 SDK Web/iOS/Android Web/iOS/Android Web/iOS/Android Web/iOS/Android 部署复杂度 低（Docker） 零（托管） 零（托管） 零（托管） 选择建议： 如果你的数据合规要求严格、不想依赖第三方服务、或者想完全控制归因逻辑，share-installs 是一个轻量且可扩展的选择。\n结语 share-installs 是一个小而美的项目——它只做一件事，但把它做扎实了。从指纹采集、存储匹配、到多端 SDK，整个链路完整闭环。\n项目是完全开源的（Apache 2.0 协议），欢迎 Star、Issue 和 PR：\n👉 https://github.com/ceeyang/share-installs\n如果你有类似的场景需要处理邀请归因，不妨试试自部署一个，5 分钟就能跑起来。\n","permalink":"https://ceeyang.com/posts/2026-06-01-share-installs-guide/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e你有没有遇到过这种场景：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e用户 A 分享了一个邀请链接给用户 B。用户 B 在手机上点击链接，跳转到 App Store 下载了 App。安装打开后——\u003cstrong\u003e邀请码呢？\u003c/strong\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e传统方案要么依赖复杂的 Deep Link 配置（Universal Link / App Links），要么需要接入 Firebase Dynamic Links 这类第三方服务。前者配置繁琐，后者有被 deprecate 的风险。\u003c/p\u003e\n\u003cp\u003e这就是我开源 \u003cstrong\u003eshare-installs\u003c/strong\u003e 的原因。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"什么是-share-installs\"\u003e什么是 share-installs？\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eshare-installs\u003c/strong\u003e 是一个开源、自托管的 \u003cstrong\u003e邀请码 \u0026amp; 延迟深度链接归因系统\u003c/strong\u003e。\u003c/p\u003e\n\u003cp\u003e核心能力只有一句话：\u003cstrong\u003e用户点击邀请链接并安装 App 后，首次启动时邀请码会自动识别并回填——即使点击时 App 尚未安装。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e项目地址：\u003ca href=\"https://github.com/ceeyang/share-installs\"\u003egithub.com/ceeyang/share-installs\u003c/a\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e⭐ 核心功能：浏览器指纹 → 后端匹配 → App 启动自动回填\n🖥️ 三端 SDK：Web（TypeScript）+ iOS（Swift）+ Android（Kotlin）\n🐳 一键部署：Docker Compose / Kubernetes / 裸机\n🔐 自托管：数据存在你自己的服务器上\n\u003c/code\u003e\u003c/pre\u003e\u003chr\u003e\n\u003ch2 id=\"工作原理\"\u003e工作原理\u003c/h2\u003e\n\u003cp\u003e整个流程分为四个阶段：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e[阶段 1] 用户 A 分享邀请链接\n         │\n         ▼\n[阶段 2] 用户 B 点击链接 → 你的落地页\n         │  JS SDK 上报浏览器指纹到后端\n         ▼\n[阶段 3] 跳转 App Store / Play Store\n         │\n         ▼\n[阶段 4] 用户 B 安装并打开 App\n         │  移动 SDK 上报设备指纹\n         ▼\n         后端匹配指纹 → 返回邀请码\n         │\n         ▼\n         App 自动填入邀请码 ✓\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003e关键技术点：\u003c/strong\u003e\u003c/p\u003e","title":"开源邀请归因系统 share-installs：自托管延迟深度链接匹配方案"},{"content":"2021国庆旅行计划 到成都南约50分钟，入站等需要提前一个半小时，总约 1.5 小时\n到成都东约70分钟，入站等需要提前一个半小时，总约 2 小时\n到犀浦站约100分钟，入站等需要提前一个半小时，总约 2.5 小时\n成都南到峨眉山无票\n成都东到峨眉山 1.5 个小时\n成都东 11：00 ~ 12：23，需 9 点出门 成都东 12：11 ~ 13：34，需 10 点出门 * 成都东 13：15 ~ 14：18，需 11 点出门 * 成都东 13：50 ~ 15：17，需 11：30 点出门 犀浦到都江堰 30 分钟\n峨眉山计划： 报国寺-清音寺/万年寺-雷洞坪-索道-金顶-日出/云海 09:00 ~ 11:00 点出门前往成都东站 11:00 ~ 12:23 成都东出发 到 达峨眉山站 12:30 ~ 13:00 午饭时间 13:00 ~ 14:00 乘坐公共交通到达报国寺车站 14:00 ~ 14:30 报国寺游玩儿 万年寺路线 14:30 ~ 15:30 观光车到达万年寺 15:30 ~ 16:00 万年寺,万年索道 14:20 ~ 16:30 到达雷洞坪, 2.0小时 16:30 ~ 17:30 入住酒店,雷洞坪购物街, 1.0 小时 17:30 ~ 19:30 晚饭时间, 1.0 小时 19:30 ~ 晚上早点休息, 次日需凌晨 4:30 起床看日出 清音寺路线 14:30 ~ 15:00 观光车到达清音寺, 0.5 小时 15:00 ~ 15:30 清音寺,一线天观光, 0.5 小时 15:30 ~ 17:30 到达雷洞坪, 2.0小时 17:30 ~ 18:30 入住酒店,雷洞坪购物街, 1.0 小时 18:30 ~ 19:30 晚饭时间, 1.0 小时 19:30 ~ 晚上早点休息, 次日需凌晨 4:30 起床看日出 04:30 ~ 05:30 起床早餐 05:30 ~ 06:30 到达接引殿, 1.0 小时 06:30 ~ 07:30 缆车排队预计 1 小时, 缆车 20 分钟, 下缆车走路 10 分钟到金顶 07:30 ~ 09:00 看日出,观云海 08:30 ~ 09:30 乘坐缆车返回接引殿 09:30 ~ 10:30 返回雷洞坪, 回酒店提行李 10:30 ~ 12:00 观光车到达峨眉山出口 12:00 ~ 开始返程 共计 1.5 天\n优点: 可观日出,看云海 缺点: 第二天需要早起, 时间稍微会有点儿赶, 峨眉山其他景区来不及逛 青城山计划: 青城山-都江堰 09:00 ~ 11:30 点出门前往犀浦高铁站 11:30 ~ 12:00 犀浦出发 到 青城山 12:00 ~ 13:00 午饭时间 13:00 ~ 15:00 青城山前山 2.0 小时 15:00 ~ 16:00 前往都江堰 16:00 ~ 19:00 都江堰风景区 3.0 小时 19:00 ~ 20:00 吃完饭,领略都江堰夜景 20:00 ~ 入住酒店到次日 次日返程, 泡温泉, 睡懒觉 次日中午十二点之前到家 20:00 ~ 开始返程 21:00 ~ 21:36 余票三张 21:36 ~ 23:20 到家, 1.5 小时 次日返回需要 1.5 天, 时间充裕, 可以睡懒觉, 泡温泉 当日返回需要 1.0 天, 时间稍微比较赶, 第二天可以在附近逛逛或者好好休息 成都市区计划: 宽窄巷子-武侯祠-锦里-音乐剧 逛街/买衣服/小吃 时间 目的地 耗时 09:00 ~ 10:00 出发前往宽窄巷子 1.0 小时 10:00 ~ 12:00 宽窄巷子 2.0 小时 12:00 ~ 14:00 武侯祠 2.0 小时 14:00 ~ 16:30 锦里 2.5 小时 16:30 ~ 19:30 到达牛王庙,吃晚饭 1.0 小时 19:30 ~ 21:00 音乐会结束 1.5 小时 21:00 ~ 22:00 到家 1.0 小时 ","permalink":"https://ceeyang.com/posts/2021-09-28-2021%E5%9B%BD%E5%BA%86%E6%97%85%E8%A1%8C%E6%94%BB%E7%95%A5/","summary":"\u003ch2 id=\"2021国庆旅行计划\"\u003e2021国庆旅行计划\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e到成都南约50分钟，入站等需要提前一个半小时，总约 1.5 小时\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e到成都东约70分钟，入站等需要提前一个半小时，总约 2 小时\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e到犀浦站约100分钟，入站等需要提前一个半小时，总约 2.5 小时\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e成都南到峨眉山无票\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e成都东到峨眉山 1.5 个小时\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e成都东 11：00 ~ 12：23，需 9 点出门\u003c/li\u003e\n\u003cli\u003e成都东 12：11 ~ 13：34，需 10 点出门 *\u003c/li\u003e\n\u003cli\u003e成都东 13：15 ~ 14：18，需 11 点出门 *\u003c/li\u003e\n\u003cli\u003e成都东 13：50 ~ 15：17，需 11：30 点出门\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e犀浦到都江堰 30 分钟\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"峨眉山计划\"\u003e峨眉山计划：\u003c/h3\u003e\n\u003cpre\u003e\u003ccode\u003e报国寺-清音寺/万年寺-雷洞坪-索道-金顶-日出/云海\n\u003c/code\u003e\u003c/pre\u003e\n\u003col\u003e\n\u003cli\u003e09:00 ~ 11:00 点出门前往成都东站\u003c/li\u003e\n\u003cli\u003e11:00 ~ 12:23 成都东出发 到 达峨眉山站\u003c/li\u003e\n\u003cli\u003e12:30 ~ 13:00 午饭时间\u003c/li\u003e\n\u003cli\u003e13:00 ~ 14:00 乘坐公共交通到达报国寺车站\u003c/li\u003e\n\u003cli\u003e14:00 ~ 14:30 报国寺游玩儿\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e万年寺路线\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cul\u003e\n\u003cli\u003e14:30 ~ 15:30 观光车到达万年寺\u003c/li\u003e\n\u003cli\u003e15:30 ~ 16:00 万年寺,万年索道\u003c/li\u003e\n\u003cli\u003e14:20 ~ 16:30 到达雷洞坪, 2.0小时\u003c/li\u003e\n\u003cli\u003e16:30 ~ 17:30 入住酒店,雷洞坪购物街, 1.0 小时\u003c/li\u003e\n\u003cli\u003e17:30 ~ 19:30 晚饭时间, 1.0 小时\u003c/li\u003e\n\u003cli\u003e19:30 ~ 晚上早点休息, 次日需凌晨 4:30 起床看日出\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/blockquote\u003e\n\u003cul\u003e\n\u003cli\u003e清音寺路线\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cul\u003e\n\u003cli\u003e14:30 ~ 15:00 观光车到达清音寺, 0.5 小时\u003c/li\u003e\n\u003cli\u003e15:00 ~ 15:30 清音寺,一线天观光, 0.5 小时\u003c/li\u003e\n\u003cli\u003e15:30 ~ 17:30 到达雷洞坪, 2.0小时\u003c/li\u003e\n\u003cli\u003e17:30 ~ 18:30 入住酒店,雷洞坪购物街, 1.0 小时\u003c/li\u003e\n\u003cli\u003e18:30 ~ 19:30 晚饭时间, 1.0 小时\u003c/li\u003e\n\u003cli\u003e19:30 ~ 晚上早点休息, 次日需凌晨 4:30 起床看日出\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/blockquote\u003e\n\u003col start=\"12\"\u003e\n\u003cli\u003e04:30 ~ 05:30 起床早餐\u003c/li\u003e\n\u003cli\u003e05:30 ~ 06:30 到达接引殿,  1.0 小时\u003c/li\u003e\n\u003cli\u003e06:30 ~ 07:30 缆车排队预计 1 小时, 缆车 20 分钟, 下缆车走路 10 分钟到金顶\u003c/li\u003e\n\u003cli\u003e07:30 ~ 09:00 看日出,观云海\u003c/li\u003e\n\u003cli\u003e08:30 ~ 09:30 乘坐缆车返回接引殿\u003c/li\u003e\n\u003cli\u003e09:30 ~ 10:30 返回雷洞坪, 回酒店提行李\u003c/li\u003e\n\u003cli\u003e10:30 ~ 12:00 观光车到达峨眉山出口\u003c/li\u003e\n\u003cli\u003e12:00 ~ 开始返程\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e共计 1.5 天\u003c/p\u003e","title":"2021国庆旅游攻略"},{"content":"自定义 swift 控制台输出 // MARK: - Debug Print - public func print\u0026lt;T\u0026gt;(_ message: T, fileName: String = #file, methodName: String = #function, line: Int = #line) { #if DEBUG let printInfo = \u0026#34;\u0026#34;\u0026#34; \\n ================================================================================================================================================= 【File Name】: \\(fileName.split(\u0026#34;/\u0026#34;).last ?? \u0026#34;\u0026#34;) 【FunctionName】: \\(methodName) 【Fuction Line】: \\(line) 【Print Time】: \\(Date()) 【 Message 】: \\(message) ================================================================================================================================================= \\n \u0026#34;\u0026#34;\u0026#34; print(printInfo, separator: \u0026#34;\u0026#34;, terminator: \u0026#34;\u0026#34;) #endif } ","permalink":"https://ceeyang.com/posts/2019-08-10-swift-debug-print/","summary":"\u003ch2 id=\"自定义-swift-控制台输出\"\u003e自定义 swift 控制台输出\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-swift\" data-lang=\"swift\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// \u003c/span\u003e\u003cspan class=\"cs\"\u003eMARK:\u003c/span\u003e\u003cspan class=\"c1\"\u003e - Debug Print -\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eT\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"kc\"\u003e_\u003c/span\u003e \u003cspan class=\"n\"\u003emessage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eT\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003efileName\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eString\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003e#file\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003emethodName\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eString\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003e#function\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eline\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eInt\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003e#line\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cp\"\u003e#if\u003c/span\u003e \u003cspan class=\"cp\"\u003eDEBUG\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eprintInfo\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    \u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    =================================================================================================================================================\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    【File    Name】: \u003c/span\u003e\u003cspan class=\"si\"\u003e\\(\u003c/span\u003e\u003cspan class=\"n\"\u003efileName\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"bp\"\u003esplit\u003c/span\u003e\u003cspan class=\"si\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;/\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e)\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"bp\"\u003elast\u003c/span\u003e \u003cspan class=\"p\"\u003e??\u003c/span\u003e  \u003cspan class=\"s\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e)\u003c/span\u003e\u003cspan class=\"s\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    【FunctionName】: \u003c/span\u003e\u003cspan class=\"si\"\u003e\\(\u003c/span\u003e\u003cspan class=\"n\"\u003emethodName\u003c/span\u003e\u003cspan class=\"si\"\u003e)\u003c/span\u003e\u003cspan class=\"s\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    【Fuction Line】: \u003c/span\u003e\u003cspan class=\"si\"\u003e\\(\u003c/span\u003e\u003cspan class=\"n\"\u003eline\u003c/span\u003e\u003cspan class=\"si\"\u003e)\u003c/span\u003e\u003cspan class=\"s\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    【Print   Time】: \u003c/span\u003e\u003cspan class=\"si\"\u003e\\(\u003c/span\u003e\u003cspan class=\"n\"\u003eDate\u003c/span\u003e\u003cspan class=\"si\"\u003e())\u003c/span\u003e\u003cspan class=\"s\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    【   Message  】:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    \u003c/span\u003e\u003cspan class=\"si\"\u003e\\(\u003c/span\u003e\u003cspan class=\"n\"\u003emessage\u003c/span\u003e\u003cspan class=\"si\"\u003e)\u003c/span\u003e\u003cspan class=\"s\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    =================================================================================================================================================\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    \u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"bp\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eprintInfo\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eseparator\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eterminator\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cp\"\u003e#endif\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Swift Debug Print"},{"content":"Swift UITextView 扩展 Placeholder import UIKit fileprivate var kTextViewPlaceholderLabel : Int = 0x2019_00 fileprivate var kTextViewPlaceholder : Int = 0x2019_01 fileprivate var kTextViewPlaceholderColor : Int = 0x2019_02 fileprivate var kTextViewPlaceholderFont : Int = 0x2019_03 fileprivate var kTextViewPlaceholderKeys : Int = 0x2019_04 extension UITextView { /// 占位符 var x_placeholder: String { get { if let placeholder = objc_getAssociatedObject(self, \u0026amp;kTextViewPlaceholder) as? String { return placeholder } else { return \u0026#34;\u0026#34; } } set { objc_setAssociatedObject(self, \u0026amp;kTextViewPlaceholder, newValue, .OBJC_ASSOCIATION_RETAIN) x_placeholderLabel.text = newValue } } /// 占位符颜色 var x_placeholderColor: UIColor { get { if let placeholderColor = objc_getAssociatedObject(self, \u0026amp;kTextViewPlaceholderColor) as? UIColor { return placeholderColor } else { return UIColor.subTitleColor } } set { objc_setAssociatedObject(self, \u0026amp;kTextViewPlaceholderColor, newValue, .OBJC_ASSOCIATION_RETAIN) x_placeholderLabel.textColor = newValue } } /// 占位符字体 var x_placeholderFont: UIFont { get { if let placeholderFont = objc_getAssociatedObject(self, \u0026amp;kTextViewPlaceholderColor) as? UIFont { return placeholderFont } else { return UIFont.systemFont(ofSize: 12) } } set { objc_setAssociatedObject(self, \u0026amp;kTextViewPlaceholderColor, newValue, .OBJC_ASSOCIATION_RETAIN) x_placeholderLabel.font = newValue } } /// 占位符 标签 @IBInspectable var x_placeholderLabel: UILabel { get { var _placeholderLabel = UILabel(font: self.font ?? UIFont.systemFont(ofSize: 12), color: .subTitleColor, alignment: .left) if let label = objc_getAssociatedObject(self, \u0026amp;kTextViewPlaceholderLabel) as? UILabel { _placeholderLabel = label } else { objc_setAssociatedObject(self, \u0026amp;kTextViewPlaceholderLabel, _placeholderLabel, .OBJC_ASSOCIATION_RETAIN) } addPlaceholderLabelToSuperView(label: _placeholderLabel) return _placeholderLabel } set { objc_setAssociatedObject(self, \u0026amp;kTextViewPlaceholderLabel, newValue, .OBJC_ASSOCIATION_RETAIN) addPlaceholderLabelToSuperView(label: newValue) } } /// 是否需要添加占位符到父视图 fileprivate var x_placeHolderNeedAddToSuperView: Bool { get { if let isAdded = objc_getAssociatedObject(self, \u0026amp;kTextViewPlaceholderKeys) as? Bool { return isAdded } return true } set { objc_setAssociatedObject(self, \u0026amp;kTextViewPlaceholderKeys, newValue, .OBJC_ASSOCIATION_RETAIN) } } /// 添加占位符到父视图 /// /// - Parameter label: 占位符 标签 fileprivate func addPlaceholderLabelToSuperView(label: UILabel) { guard x_placeHolderNeedAddToSuperView else { return } x_placeHolderNeedAddToSuperView = false NotificationCenter.default.addObserver(self, selector: #selector(x_textChange(noti:)), name: UITextView.textDidChangeNotification, object: nil) addSubview(label) label.snp.makeConstraints { (make) in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 7, left: 2, bottom: 0, right: 0)) } } /// 编辑事件 @objc fileprivate func x_textChange(noti: NSNotification) { let isEmpty = text.isEmpty print(\u0026#34;text:\\(String(describing: text))\\nisEmpty:\\(isEmpty)\u0026#34;) x_placeholderLabel.text = isEmpty ? x_placeholder : \u0026#34;\u0026#34; x_placeholderLabel.isHidden = !isEmpty } } ","permalink":"https://ceeyang.com/posts/2019-08-08-uitextview%E6%89%A9%E5%B1%95%E6%B7%BB%E5%8A%A0placeholder/","summary":"\u003ch2 id=\"swift-uitextview-扩展-placeholder\"\u003eSwift UITextView 扩展 Placeholder\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-Swift\" data-lang=\"Swift\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"nc\"\u003eUIKit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003efileprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003ekTextViewPlaceholderLabel\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eInt\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mh\"\u003e0x2019_00\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003efileprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003ekTextViewPlaceholder\u003c/span\u003e      \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eInt\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mh\"\u003e0x2019_01\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003efileprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003ekTextViewPlaceholderColor\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eInt\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mh\"\u003e0x2019_02\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003efileprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003ekTextViewPlaceholderFont\u003c/span\u003e  \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eInt\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mh\"\u003e0x2019_03\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003efileprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003ekTextViewPlaceholderKeys\u003c/span\u003e  \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eInt\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mh\"\u003e0x2019_04\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eextension\u003c/span\u003e \u003cspan class=\"nc\"\u003eUITextView\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e/// 占位符\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003ex_placeholder\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eString\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kr\"\u003eget\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eplaceholder\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eobjc_getAssociatedObject\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ekTextViewPlaceholder\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"nb\"\u003eString\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eplaceholder\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kr\"\u003eset\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eobjc_setAssociatedObject\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ekTextViewPlaceholder\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003enewValue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOBJC_ASSOCIATION_RETAIN\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ex_placeholderLabel\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etext\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003enewValue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e/// 占位符颜色\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003ex_placeholderColor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eUIColor\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kr\"\u003eget\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eplaceholderColor\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eobjc_getAssociatedObject\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ekTextViewPlaceholderColor\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003eUIColor\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eplaceholderColor\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eUIColor\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esubTitleColor\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kr\"\u003eset\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eobjc_setAssociatedObject\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ekTextViewPlaceholderColor\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003enewValue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOBJC_ASSOCIATION_RETAIN\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ex_placeholderLabel\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etextColor\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003enewValue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e/// 占位符字体\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003ex_placeholderFont\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eUIFont\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kr\"\u003eget\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eplaceholderFont\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eobjc_getAssociatedObject\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ekTextViewPlaceholderColor\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003eUIFont\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eplaceholderFont\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eUIFont\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esystemFont\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eofSize\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e12\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kr\"\u003eset\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eobjc_setAssociatedObject\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ekTextViewPlaceholderColor\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003enewValue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOBJC_ASSOCIATION_RETAIN\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ex_placeholderLabel\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efont\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003enewValue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e/// 占位符 标签\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003e@IBInspectable\u003c/span\u003e \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003ex_placeholderLabel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eUILabel\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kr\"\u003eget\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003e_placeholderLabel\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eUILabel\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efont\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efont\u003c/span\u003e \u003cspan class=\"p\"\u003e??\u003c/span\u003e \u003cspan class=\"n\"\u003eUIFont\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esystemFont\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eofSize\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e12\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"n\"\u003ecolor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esubTitleColor\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ealignment\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"kr\"\u003eleft\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003elabel\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eobjc_getAssociatedObject\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ekTextViewPlaceholderLabel\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003eUILabel\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003e_placeholderLabel\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elabel\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eobjc_setAssociatedObject\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ekTextViewPlaceholderLabel\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003e_placeholderLabel\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOBJC_ASSOCIATION_RETAIN\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eaddPlaceholderLabelToSuperView\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003e_placeholderLabel\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003e_placeholderLabel\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kr\"\u003eset\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eobjc_setAssociatedObject\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ekTextViewPlaceholderLabel\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003enewValue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOBJC_ASSOCIATION_RETAIN\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eaddPlaceholderLabelToSuperView\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003enewValue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e/// 是否需要添加占位符到父视图\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003efileprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003ex_placeHolderNeedAddToSuperView\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eBool\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kr\"\u003eget\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eisAdded\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eobjc_getAssociatedObject\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ekTextViewPlaceholderKeys\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"nb\"\u003eBool\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eisAdded\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kr\"\u003eset\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eobjc_setAssociatedObject\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ekTextViewPlaceholderKeys\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003enewValue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOBJC_ASSOCIATION_RETAIN\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e/// 添加占位符到父视图\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e///\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e/// - Parameter label: 占位符 标签\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003efileprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003eaddPlaceholderLabelToSuperView\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eUILabel\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eguard\u003c/span\u003e \u003cspan class=\"n\"\u003ex_placeHolderNeedAddToSuperView\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ex_placeHolderNeedAddToSuperView\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eNotificationCenter\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eaddObserver\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eselector\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"k\"\u003e#selector\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex_textChange\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enoti\u003c/span\u003e\u003cspan class=\"p\"\u003e:)),\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eUITextView\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etextDidChangeNotification\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eobject\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eaddSubview\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esnp\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emakeConstraints\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emake\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003emake\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eedges\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eequalToSuperview\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003einset\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eUIEdgeInsets\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etop\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e7\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kr\"\u003eleft\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ebottom\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kr\"\u003eright\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e/// 编辑事件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003e@objc\u003c/span\u003e \u003cspan class=\"n\"\u003efileprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003ex_textChange\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enoti\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eNSNotification\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eisEmpty\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"bp\"\u003eisEmpty\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"bp\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;text:\u003c/span\u003e\u003cspan class=\"si\"\u003e\\(\u003c/span\u003e\u003cspan class=\"nb\"\u003eString\u003c/span\u003e\u003cspan class=\"si\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edescribing\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003etext\u003c/span\u003e\u003cspan class=\"si\"\u003e))\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s\"\u003eisEmpty:\u003c/span\u003e\u003cspan class=\"si\"\u003e\\(\u003c/span\u003e\u003cspan class=\"bp\"\u003eisEmpty\u003c/span\u003e\u003cspan class=\"si\"\u003e)\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ex_placeholderLabel\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etext\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"bp\"\u003eisEmpty\u003c/span\u003e \u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003ex_placeholder\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ex_placeholderLabel\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eisHidden\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"bp\"\u003eisEmpty\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"UITextView+Placeholder"},{"content":"iOS UITabBarController 嵌套 UINavigationController 本文处理两个问题: 1. 自定义导航按钮后, 滑动返回手势失效问题 2. TabBar 嵌套 NavigationBar 的 TabBar 显示隐藏错乱等问题 主要思路 使用 UITabBarController 作为 Window 的 rootViewController 自定义 UINavigationController 控制器, 全局控制 tabbar 的显示与隐藏 添加自定义控制器为 UINavigationController 的 root 控制器 UITabBarController 添加每个 UINavigationController 控制器 直接上代码 自定义的 UITabBarController :\nimport UIKit class HomeTabBarController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() createChildController() } /// 通过自定义方法添加所有子控制器 func createChildController() { addChildVC(childVc: HomeViewController(), title: \u0026#34;首页\u0026#34;, image: \u0026#34;IMG_Home\u0026#34;, selectedImage: \u0026#34;IMG_Home_Selected\u0026#34;) addChildVC(childVc: MoreViewController(), title: \u0026#34;应用\u0026#34;, image: \u0026#34;IMG_More\u0026#34;, selectedImage: \u0026#34;IMG_More_Selected\u0026#34;) addChildVC(childVc: MineViewController(), title: \u0026#34;我的\u0026#34;, image: \u0026#34;IMG_Mine\u0026#34;, selectedImage: \u0026#34;IMG_Mine_Selected\u0026#34;) } /// 自定义添加子控制器 func addChildVC(childVc: UIViewController, title:String, image: String, selectedImage: String) { let nav = HomeNavigationController(rootViewController: childVc) let normalImage = UIImage(named: image) let selectedImage = UIImage(named: selectedImage) nav.navigationItem.title = title nav.tabBarItem = tabbarItem(with: title, normalImage: normalImage!, selectedImage: selectedImage!) addChildViewController(nav) } /// 快捷创建 UITabBarItem func tabbarItem(with title: String, normalImage: UIImage, selectedImage: UIImage) -\u0026gt; UITabBarItem{ let image = normalImage.withRenderingMode(.alwaysOriginal) let _selectedImage = selectedImage.withRenderingMode(.alwaysOriginal) let tabBarItem = UITabBarItem(title: title, image: image, selectedImage: _selectedImage) tabBarItem.setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor.mainColor], for: .selected) tabBarItem.setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor.tabbarNormalColor], for: .normal) return tabBarItem } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } //让图片和文字在 iPad 下仍然保持上下排列 extension UITabBar { override open var traitCollection: UITraitCollection { if UIDevice.current.userInterfaceIdiom == .pad { return UITraitCollection(horizontalSizeClass: .compact) } return super.traitCollection } } 自定义的 UINavigationController :\nimport UIKit class HomeNavigationController: UINavigationController, UIGestureRecognizerDelegate { override func viewDidLoad() { super.viewDidLoad() /// 自定义返回按钮后返回手势失效 /// 手动实现返回手势代理 interactivePopGestureRecognizer?.delegate = self } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func pushViewController(_ viewController: UIViewController, animated: Bool) { /// 如果当前控制器的子控制器个数大于等于1, 说明推出的控制器为子控制器 if childViewControllers.count \u0026gt; 0 { /// 自定义返回按钮 let backBtn = UIButton() backBtn.setImage(UIImage(named: \u0026#34;IMG_Back\u0026#34;), for: .normal) backBtn.addTarget(self, action: #selector(backButtonDidClick), for: .touchUpInside) viewController.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backBtn) /// 子控制器隐藏 bottomBar viewController.hidesBottomBarWhenPushed = true } super.pushViewController(viewController, animated: animated) } /// 返回按钮点击事件 @objc func backButtonDidClick() { popViewController(animated: true) } /// 滑动返回手势 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -\u0026gt; Bool { /// 判断是否需要滑动返回手势 if gestureRecognizer == navigationController?.interactivePopGestureRecognizer { return navigationController!.childViewControllers.count \u0026gt; 1 } /// 解决当前控制器为根控制器的时候, 返回手势卡屏的问题 if childViewControllers.count == 1 { return false } return true } } ","permalink":"https://ceeyang.com/posts/2018-09-18-ios-tabbar-%E5%B5%8C%E5%A5%97-navbar/","summary":"\u003ch1 id=\"ios-uitabbarcontroller-嵌套-uinavigationcontroller\"\u003eiOS UITabBarController 嵌套 UINavigationController\u003c/h1\u003e\n\u003cpre\u003e\u003ccode\u003e本文处理两个问题:\n1. 自定义导航按钮后, 滑动返回手势失效问题\n2. TabBar 嵌套 NavigationBar 的 TabBar 显示隐藏错乱等问题\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"主要思路\"\u003e主要思路\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e使用 UITabBarController 作为 Window 的 rootViewController\u003c/li\u003e\n\u003cli\u003e自定义 UINavigationController 控制器, 全局控制 tabbar 的显示与隐藏\u003c/li\u003e\n\u003cli\u003e添加自定义控制器为 UINavigationController 的 root 控制器\u003c/li\u003e\n\u003cli\u003eUITabBarController 添加每个 UINavigationController 控制器\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"直接上代码\"\u003e直接上代码\u003c/h2\u003e\n\u003cp\u003e自定义的 UITabBarController :\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-swift\" data-lang=\"swift\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"nc\"\u003eUIKit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eHomeTabBarController\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eUITabBarController\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eoverride\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003eviewDidLoad\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kc\"\u003esuper\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eviewDidLoad\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ecreateChildController\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e/// 通过自定义方法添加所有子控制器\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003ecreateChildController\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eaddChildVC\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003echildVc\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eHomeViewController\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e \u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;首页\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;IMG_Home\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eselectedImage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;IMG_Home_Selected\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eaddChildVC\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003echildVc\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eMoreViewController\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e \u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;应用\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;IMG_More\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eselectedImage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;IMG_More_Selected\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eaddChildVC\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003echildVc\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eMineViewController\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e \u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;我的\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;IMG_Mine\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eselectedImage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;IMG_Mine_Selected\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e/// 自定义添加子控制器\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003eaddChildVC\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003echildVc\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eUIViewController\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"nb\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eselectedImage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003enav\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eHomeNavigationController\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erootViewController\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003echildVc\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003enormalImage\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eUIImage\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enamed\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eselectedImage\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eUIImage\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enamed\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eselectedImage\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003enav\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enavigationItem\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etitle\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003enav\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etabBarItem\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etabbarItem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003enormalImage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003enormalImage\u003c/span\u003e\u003cspan class=\"p\"\u003e!,\u003c/span\u003e \u003cspan class=\"n\"\u003eselectedImage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eselectedImage\u003c/span\u003e\u003cspan class=\"p\"\u003e!)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eaddChildViewController\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enav\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e/// 快捷创建 UITabBarItem\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003etabbarItem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ewith\u003c/span\u003e \u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003enormalImage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eUIImage\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eselectedImage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eUIImage\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eUITabBarItem\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eimage\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003enormalImage\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewithRenderingMode\u003c/span\u003e\u003cspan class=\"p\"\u003e(.\u003c/span\u003e\u003cspan class=\"n\"\u003ealwaysOriginal\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003e_selectedImage\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eselectedImage\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewithRenderingMode\u003c/span\u003e\u003cspan class=\"p\"\u003e(.\u003c/span\u003e\u003cspan class=\"n\"\u003ealwaysOriginal\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003etabBarItem\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eUITabBarItem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eselectedImage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003e_selectedImage\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003etabBarItem\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esetTitleTextAttributes\u003c/span\u003e\u003cspan class=\"p\"\u003e([\u003c/span\u003e\u003cspan class=\"n\"\u003eNSAttributedStringKey\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eforegroundColor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eUIColor\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emainColor\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eselected\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003etabBarItem\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esetTitleTextAttributes\u003c/span\u003e\u003cspan class=\"p\"\u003e([\u003c/span\u003e\u003cspan class=\"n\"\u003eNSAttributedStringKey\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eforegroundColor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eUIColor\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etabbarNormalColor\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enormal\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003etabBarItem\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eoverride\u003c/span\u003e \u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003edidReceiveMemoryWarning\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kc\"\u003esuper\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edidReceiveMemoryWarning\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Dispose of any resources that can be recreated.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//让图片和文字在 iPad 下仍然保持上下排列\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eextension\u003c/span\u003e \u003cspan class=\"nc\"\u003eUITabBar\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eoverride\u003c/span\u003e \u003cspan class=\"n\"\u003eopen\u003c/span\u003e \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003etraitCollection\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eUITraitCollection\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eUIDevice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003euserInterfaceIdiom\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epad\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eUITraitCollection\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ehorizontalSizeClass\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecompact\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003esuper\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etraitCollection\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e自定义的 UINavigationController :\u003c/p\u003e","title":"iOS UITabBarController 嵌套 UINavigationController"},{"content":"前言 一年一度的 WWDC（苹果全球开发者大会）于北京时间 6月5日 凌晨1点开幕。废话不多说，来看看这次WWDC 都有哪些亮点吧!\niOS 12 和 ARKit 2.0 关键词：官方防沉迷最为致命\niOS 12 iOS 12 相较于 iOS 11 并没有太多UI上的变动，刚更新完 bate 版本的 iOS 12，完全感觉不到这是个新系统。\niOS 12 主要是对安全和性能的优化，iOS 12 在旧设备上的运行速度比 iOS 11更块，程序加载速度快了一倍。（PS：看来苹果并没有放弃旧设备）\nARKit 2.0 Apple 与 皮克斯 合作开发了一种用于共享AR内容的新文件格式，新的 AR 格式名为 USDZ。\n作为一个含着金苹果出生的新生儿，USDZ 一开始就得到了 Adobe Creative Cloud （包括 Photoshop CC、InDesign CC、Illustrator CC、Dreamweaver CC、Premiere Pro CC）套件的支持。\n同时，面向开发者的开发套件 ARKit 则升级到了二代，主要提升了面部跟踪、渲染能力、3D 探测和共享体验等能力。\n随后展示了一款名为 Measure 的 App，可使用AR查看物品大小。\n最后为了演示新的 AR 能力和效果，苹果请来了乐高的创意总监来捧场。这是一个真实的乐高积木建筑物为基础，最多四个人可以用苹果 AR 应用进行游戏，可以在真实环境中模拟出各种虚拟的形象和建筑。\n相册优化 iOS 12 的相册将大大提升搜索性能，系统不仅会提出搜索建议，还会帮你按主题整理照片。\nSiri变得更聪明 iOS 12 中，苹果为 Siri 提供了更加高效的操作，让它可以操作各个应用内部的功能，并且能在锁屏界面建议用户下一步的行动。\n苹果还发布了一款名为「Shortcuts」的应用，它允许用户自定义 Siri 搜索指令，支持通过拖拽来快速编辑指令，同时还提供了一个指令库供用户下载现成的命令，就像是为 Siri 打造的 Workflow 自动化工具。\n看到这里，相比熟悉苹果的朋友大概明白了，Siri 的本次改进，很可能是源于它收购的效率神器 Workflow，堪称一个用 Siri 唤醒的 Workflow。\n原生应用大更新 iOS 12 中，不少原生应用都得到了更新。\niBooks 更名为 Apple Books，采用类似 App Store 的新界面设计。 新闻应用（News）在 iPad 上新添加了侧边栏，方便浏览，也突出策划内容。 语音备忘录现在支持 iCloud 了 股市的界面重构，可以看到股票全天走势，并打通 News 应用，方便看财经新闻； CarPlay 开放了 CarPlay 变得更加开放了，曾经被苹果狠心抛弃的 Google Maps 和 Google 的干儿子地图 Waze，以及来自东方的神秘力量高德地图成为首批 CarPlay 支持的第三方导航，从此“志玲姐姐为您导航”将可以常伴林肯领航者车主左右，中国梦和美国梦一起实现。\n防手机沉迷 - 划重点！ 本次 iOS 12 的重点就是：防沉迷！\n鉴于手机上瘾成为了一个社会议题，今年 Google 和苹果都不约而同地将“防沉迷”加入到了系统更新当中：\n**Do Not Disturb（别吵我）**功能将关掉手机的视觉通知，在夜深人静想起她或梦见她的时候，来了邮件也不会亮屏刺破夜的静谧和黑暗 Deliver Quietly（安静通知）则是将消息推送静默化、不显示在锁屏，也不出声，也不会在 app 右上角标红 Grouped Notifications（分组通知）可以将某一类型的通知归组，微信群聊消息不再有轰炸的感觉 **Reports（应用报告）**可以用周报告的形式，告诉用户用什么应用最多，哪个应用通知最多，每天起床第一个打开的是什么应用等等 App Limits（应用限制）可以给某个应用规定使用时间，当然这不是强制性的，用户可以突破限制继续“吃鸡” Allowances（零用钱？） 是家长限制孩子使用应用的新特性 iMessage 和 FeacTime Animoji 新增了 4 个新表情（幽灵，考拉，老虎和霸王龙），用户还可以为自己量身定做 Animoji ，并用到各种场景——这就是全新的 Memoji 技术\niOS 版 FaceTime 迎来了群聊功能，最多支持 32 人同时聊天，成员可以随时加入或离开聊天。聊天界面用瀑布流的形式呈现，正在说话的成员窗口会自动放大。macOS 版 FaceTime 同样也得到了更新。\ntvOS 关键词：优化试听体验\ntvOS 今年的变化比较小，更新主要集中在了影视资源以及细节优化上。\nApple TV 4K 将支持杜比全景声和杜比视界，让你在家里也能获得电影院般的听觉体验。\nwatchOS 5 关键词：运动进行到底\n随着 Apple Watch 成长的，还有它的操作系统 watchOS，这一次 watchOS 升级到了第五代。\nApple Watch 的功能朝着运动的方向发展，此次 watchOS 5 的更新，也以运动为主。\nwatchOS 5 的一个小惊喜是让 Apple Watch 成为了对讲机，这个应用名为 Walkie Talkie（对讲机）。\nApple Watch 早就支持了 Apple Pay，不过在通知上，Apple Watch 显然可以做得更多，比如值机和给滴滴师傅付款评分，手表不再只是个通知器，也能做些轻交互。\n还有 Apple Watch 可以浏览网页了~\nmacOS Mojave 关键词：夜间模式、全新的App Store\n对于大多数人来说，macOS 更新最大的悬念，是新系统叫什么名字。\n答案是：macOS Mojave，Mojave 中译名是莫哈韦沙漠，位于在美国加利福尼亚西南，出于洛杉矶和拉斯维加斯之间。\n在 Mojave 这版系统中，苹果加入了一套适应暗光环境下使用的夜间主题，并对 Mac App Store 的交互界面进行了重塑，整个系统的改变甚至连库克都称为是苹果的一次 “巨大的跨越”。\n夜间模式 / 动态桌面 不少用户会在暗光环境下使用电脑，即便是将屏幕亮度调到最低，也难免会因为白底色为主的主题而感到刺眼。在这次更新中，macOS Mojave 新增加了一套暗色主题，不同于目前将菜单和程序栏调成暗色的选项，新系统上的是一套全局暗色主题，即便是在文件夹、应用里都是以黑色为主色呈现。\n另外，系统可根据时间变化对桌面进行自动调整，日间使用时系统为正常主题；夜间使用时系统则自动切换至暗色模式主题。此时，台下的开发者们爆发出了一阵欢呼，大概是这个主题能够提升程序员朋友夜间加班的幸福感吧。\n智能分类 macOS 会跟据文件类型和标签对桌面的文件进行自动分类整理，从此再也不用担心满桌面都是文件了。\n快速查看升级 访达在这次系统上的更新并不多，只是针对图片查看增添了 “图库视图” 功能。通过 “图库视图”，用户可更加便捷快速地浏览到访达文件夹内的图片内容，与此同时功能内部也内置了图片元数据显示窗，用户可以借助数据窗口了解到图片的相关详情，并且可对图片进行快速编辑操作。\n截图/录屏操作 - 类似iOS 此次 macOS Mojave 加入了 “系统级” 的截屏功能，用户也可以在截屏后对截屏图片进行简单的标记。不过实际上，目前不少社交软件其实都已经具备了截图 + 标记的相关功能，且在体验上也相当不错。\n安全权限 当我们在 iOS 系统上打开刚下载的应用程序时，系统会弹出弹窗，提示是否允许程序访问用户信息和手机硬件。而这次苹果也将相关的安全控制策略从 iOS “搬” 到了 macOS 上，当用户打开某个网址或程序时，系统会弹出 “是否允许访问” 的弹窗以获得用户批准。这也可能是为了呼应最近越发严格的隐私政策。\n在 macOS 上运行的几款 iOS 程序 将 iOS 的应用程序搬到 macOS 上运行是不少玩家曾经有过的想法。此次苹果在新版的 macOS 系统上加入了 iOS 端的新闻、股市、家、语音备忘录四个程序，用户可以在桌面系统上通过这几款应用阅读新闻、控制家庭设备等等。\n在发布会的最后，苹果否认了将对 iOS 和 macOS 进行合并的传闻，但考虑到 iOS 平台有非常庞大的应用数量，他们也希望其中的部分应用能来到 macOS。于是苹果在发布会上为大家提前预览了一个准备多年的项目，macOS 将可以使用 iOS 的 UIKit 框架进行开发，以降低开发多平台应用的成本。\n比如这次 macOS Mojave 中新增的 4 款应用——News、股票、语音备忘录、家庭——均采用了这种新技术。\n全新的 Mac App Store 在 iOS 11 对 Mac App Store 进行了重新设计后，macOS Mojave 也迎来了全新设计的 Mac App Store。新版拥有与 iOS 上 App Store 类似的发现页，里面能看到每日编辑推荐和一些 App 的使用技巧。进入 App 页面后，你可以看到视频预览和与 iOS 类似的评分系统。为了方便用户评分，新版 macOS 还加入了和 iOS 一样的 App 内打分功能。此外，苹果还宣布包括 Office 365 和 Adobe Lightroom CC 在内的一批重量级 App 将在今年稍后登录 Mac App Store。\n结语 是的，这次的 WWDC 只有软件，没有新的电子设备发布，没有新 iPad Pro、没有 iPhone SE2、没有带八代酷睿的新 MacBook，唯一能和“硬件”沾上边的就是一个新的彩虹表带。\n时至今日，苹果生态已经日趋完善了，大概苹果的产品经理们也想不出什么石破天惊的功能让大家 wow 一声了，有的只是细节层面的改进。作为看客和用户，也只能接受这样的现实了。\n对了，那个可以四个人一起玩的乐高积木和 AR 应用，倒是可以考虑买来玩一下，不要一边说没有新东西，一边又对新东西视而不见。\n对于 iOS 开发者来说，macOS 将可以使用 iOS 的 UIKit 框架进行开发是一个值得关注的点。\n参考 WWDC 2018 Keynote Apple WWDC 2018: what\u0026rsquo;s new? All the announcements from the keynote iOS 加入「防沉迷」，macOS 有了暗色主题，今年的 WWDC 重点都在系统上 苹果 WWDC 2018：最全总结看这里，不错过任何重点 ","permalink":"https://ceeyang.com/posts/2018-06-05-5%E5%88%86%E9%92%9F%E5%B8%A6%E4%BD%A0%E7%9C%8B%E5%AE%8C-wwdc-2018/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e一年一度的 WWDC（苹果全球开发者大会）于北京时间 6月5日 凌晨1点开幕。废话不多说，来看看这次WWDC 都有哪些亮点吧!\u003c/p\u003e\n\u003ch2 id=\"ios-12-和-arkit-20\"\u003eiOS 12 和 ARKit 2.0\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e关键词：官方防沉迷最为致命\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"ios-12\"\u003eiOS 12\u003c/h3\u003e\n\u003cp\u003eiOS 12 相较于 iOS 11 并没有太多UI上的变动，刚更新完 bate 版本的 iOS 12，完全感觉不到这是个新系统。\u003c/p\u003e\n\u003cp\u003eiOS 12 主要是对安全和性能的优化，iOS 12 在旧设备上的运行速度比 iOS 11更块，程序加载速度快了一倍。（PS：看来苹果并没有放弃旧设备）\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"https://cdn.mos.cms.futurecdn.net/RdxhPVv8fAyM6oHsRgF6dH-650-80.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"arkit-20\"\u003eARKit 2.0\u003c/h3\u003e\n\u003cp\u003eApple 与 皮克斯 合作开发了一种用于共享AR内容的新文件格式，新的 AR 格式名为 USDZ。\u003c/p\u003e\n\u003cp\u003e作为一个含着金苹果出生的新生儿，USDZ 一开始就得到了 Adobe Creative Cloud （包括 Photoshop CC、InDesign CC、Illustrator CC、Dreamweaver CC、Premiere Pro CC）套件的支持。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"https://images.ifanr.cn/wp-content/uploads/2018/06/WWDC-10.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e同时，面向开发者的开发套件 ARKit 则升级到了二代，主要提升了面部跟踪、渲染能力、3D 探测和共享体验等能力。\u003c/p\u003e\n\u003cp\u003e随后展示了一款名为 Measure 的 App，可使用AR查看物品大小。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"https://cdn.mos.cms.futurecdn.net/4tbGCxGUGsH9VwSLsfMDK5-650-80.png\"\u003e\u003c/p\u003e\n\u003cp\u003e最后为了演示新的 AR 能力和效果，苹果请来了乐高的创意总监来捧场。这是一个真实的乐高积木建筑物为基础，最多四个人可以用苹果 AR 应用进行游戏，可以在真实环境中模拟出各种虚拟的形象和建筑。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"https://images.ifanr.cn/wp-content/uploads/2018/06/WWDC-9.jpg\"\u003e\n\u003cimg loading=\"lazy\" src=\"https://images.ifanr.cn/wp-content/uploads/2018/06/WWDC-11.jpg\"\u003e\u003c/p\u003e","title":"5分钟带你看完 WWDC 2018"},{"content":"安装 xcode-select --install Xcode版本切换 显示当前使用的xocde版本 $ xcode-select --print-path 选择Xcode中的默认版本 $ sudo xcode-select -switch /Applications/Xcode.app ","permalink":"https://ceeyang.com/posts/2018-05-05-xcode%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%B7%A5%E5%85%B7%E7%AE%A1%E7%90%86/","summary":"\u003ch2 id=\"安装\"\u003e安装\u003c/h2\u003e\n\u003cpre\u003e\u003ccode\u003excode-select --install\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"https://upload-images.jianshu.io/upload_images/545662-f9031dfcce085f8f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/459\"\u003e\u003c/p\u003e\n\u003ch2 id=\"xcode版本切换\"\u003eXcode版本切换\u003c/h2\u003e\n\u003ch3 id=\"显示当前使用的xocde版本\"\u003e显示当前使用的xocde版本\u003c/h3\u003e\n\u003cpre\u003e\u003ccode\u003e$ xcode-select --print-path\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"选择xcode中的默认版本\"\u003e选择Xcode中的默认版本\u003c/h3\u003e\n\u003cpre\u003e\u003ccode\u003e$ sudo xcode-select -switch /Applications/Xcode.app\n\u003c/code\u003e\u003c/pre\u003e","title":"Xcode命令行工具管理"},{"content":"private 权限扩大 在 Swift 4 中，extension 可以读取 private 变量了。\nSwift 3 中，如果将主体函数的变量定义为 private，则其 extension 无法读取此变量，必须将其改为 filePrivate 才可以。\n单向区间 单向区间是一个新的类型，主要分两种：确定上限和确定下限的区间。直接用字面量定义大概可以写成 …6和 2…\n例如\nlet intArr = [0, 1, 2, 3, 4] let arr1 = intArr[...3] // [0, 1, 2, 3] let arr2 = intArr[3...] // [3, 4] 字符串改动 String 操作简化了 String 许多要通过 .characters 进行的操作，可以直接用 String 进行操作了。\n例如：\nlet greeting = \u0026#34;Hello, 😜!\u0026#34; // No need to drill down to .characters let n = greeting.count let endOfSentence = greeting.index(of: \u0026#34;!\u0026#34;)! 新增 Substring 类型 swift 4 为字符串片段新增了一个叫 Substring 的类型。\n当你创建一个字符串的片段时，会产生一个 Substring 实例。Substring 与 String 用法相同， 因为子串和原字符串共享内存，所以对子串的操作快速而且高效。\nlet greeting = \u0026#34;Hi there! It\u0026#39;s nice to meet you! 👋\u0026#34; let endOfSentence = greeting.index(of: \u0026#34;!\u0026#34;)! // 产生 Substring 实例 let firstSentence = greeting[...endOfSentence] // firstSentence == \u0026#34;Hi there!\u0026#34; // `Substring` 与 `String` 用法相同 let shoutingSentence = firstSentence.uppercased() // shoutingSentence == \u0026#34;HI THERE!\u0026#34; 但是要注意一个 Substring 保留从其生成的完整的 String值。 当您传递一个看似很小的 Substring 时，这可能导致意外的高内存开销。所以使用 Substring时，最好转化为 String.\nlet newString = String(substring) 换行可以不用 \\n了！ Swift 3，字符串换行要插入 \\n。 例如：\n在 Swift 4 可以这样操作:\n用两个 “”“ 包裹起来的字符串会自动添加 \\n 换行，更加直观了。注意：换行与缩进参照的是第二个 “”“ 号的位置。\n嗯，我觉得OK！\n支持 Unicode 9 Swift 4 支持 Unicode 9，为现代表情符号修正了一些问题。\nlet family1 = \u0026#34;👨‍👩‍👧‍👦\u0026#34; let family2 = \u0026#34;👨\\u{200D}👩\\u{200D}👧\\u{200D}👦\u0026#34; family1 == family2 // → true 居然还有这种操作~\n新增 KeyPath 数据类型 KeyPath 是 Swift 4 新增加的数据类型。\n定义两个结构体 Person与Book\nstruct Person { var name: String } struct Book { var title: String var authors: [Person] var primaryAuthor: Person { return authors.first! } } let abelson = Person(name: \u0026#34;Harold Abelson\u0026#34;) let sussman = Person(name: \u0026#34;Gerald Jay Sussman\u0026#34;) let book = Book(title: \u0026#34;Structure and Interpretation of Computer Programs\u0026#34;, authors: [abelson, sussman]) book[keyPath: \\Book.title] book[keyPath: \\Book.primaryAuthor.name] // 相当与 book.title book.primaryAuthor.name 这里 \\Book.title 与 \\Book.primaryAuthor.name 就是 KeyPath.\nKeyPath 可以用 .appending 拼接\nlet authorKeyPath = \\Book.primaryAuthor let nameKeyPath = authorKeyPath.appending(path: \\.name) // nameKeyPath = \\Book.primaryAuthor.name 新增 swapAt() 函数 Swift 4 引入了一种在集合中交换两个元素的新方法: swapAt()\nSwift 3 交换集合中的元素的用 swap()\nvar numbers = [1,2,3,4,5] swap(\u0026amp;numbers[0], \u0026amp;numbers[1]) // numbers = [2,1,3,4,5] Swift 4 中可以直接用\nvar numbers = [1,2,3,4,5] numbers.swapAt(0,1) // numbers = [2,1,3,4,5] 其他改动 其他改动如：新的整数协议、泛型下标、NSNumber bridging等\n可以参考：whats new in swift4\n","permalink":"https://ceeyang.com/posts/2018-11-09-swift4.2%E6%96%B0%E7%89%B9%E6%80%A7/","summary":"\u003ch3 id=\"private-权限扩大\"\u003eprivate 权限扩大\u003c/h3\u003e\n\u003cp\u003e在 Swift 4 中，\u003ccode\u003eextension\u003c/code\u003e 可以读取 \u003ccode\u003eprivate\u003c/code\u003e 变量了。\u003c/p\u003e\n\u003cp\u003eSwift 3 中，如果将主体函数的变量定义为 \u003ccode\u003eprivate\u003c/code\u003e，则其 \u003ccode\u003eextension\u003c/code\u003e 无法读取此变量，必须将其改为 \u003ccode\u003efilePrivate\u003c/code\u003e 才可以。\u003c/p\u003e\n\u003ch3 id=\"单向区间\"\u003e单向区间\u003c/h3\u003e\n\u003cp\u003e单向区间是一个新的类型，主要分两种：确定上限和确定下限的区间。直接用字面量定义大概可以写成 \u003ccode\u003e…6\u003c/code\u003e和 \u003ccode\u003e2…\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e例如\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-swift\" data-lang=\"swift\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eintArr\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003earr1\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eintArr\u003c/span\u003e\u003cspan class=\"p\"\u003e[...\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \t\u003cspan class=\"c1\"\u003e// [0, 1, 2, 3]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003earr2\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eintArr\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mf\"\u003e3.\u003c/span\u003e\u003cspan class=\"p\"\u003e..]\u003c/span\u003e \t\u003cspan class=\"c1\"\u003e// [3, 4]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"字符串改动\"\u003e字符串改动\u003c/h3\u003e\n\u003ch4 id=\"string-操作简化了\"\u003eString 操作简化了\u003c/h4\u003e\n\u003cp\u003e\u003ccode\u003eString\u003c/code\u003e 许多要通过 \u003ccode\u003e.characters\u003c/code\u003e 进行的操作，可以直接用 String 进行操作了。\u003c/p\u003e\n\u003cp\u003e例如：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-swift\" data-lang=\"swift\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003egreeting\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello, 😜!\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// No need to drill down to .characters\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003en\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003egreeting\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"bp\"\u003ecount\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eendOfSentence\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003egreeting\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eindex\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eof\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;!\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e!\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"新增-substring-类型\"\u003e新增 Substring 类型\u003c/h4\u003e\n\u003cp\u003eswift 4 为字符串片段新增了一个叫 \u003ccode\u003eSubstring\u003c/code\u003e 的类型。\u003c/p\u003e\n\u003cp\u003e当你创建一个字符串的片段时，会产生一个 \u003ccode\u003eSubstring\u003c/code\u003e 实例。\u003ccode\u003eSubstring\u003c/code\u003e 与 \u003ccode\u003eString\u003c/code\u003e 用法相同， 因为子串和原字符串共享内存，所以对子串的操作快速而且高效。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-swift\" data-lang=\"swift\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003egreeting\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hi there! It\u0026#39;s nice to meet you! 👋\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eendOfSentence\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003egreeting\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eindex\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eof\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;!\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e!\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// 产生 Substring 实例\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003efirstSentence\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003egreeting\u003c/span\u003e\u003cspan class=\"p\"\u003e[...\u003c/span\u003e\u003cspan class=\"n\"\u003eendOfSentence\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// firstSentence == \u0026#34;Hi there!\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// `Substring` 与 `String` 用法相同\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eshoutingSentence\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003efirstSentence\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003euppercased\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// shoutingSentence == \u0026#34;HI THERE!\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e但是要注意一个 \u003ccode\u003eSubstring\u003c/code\u003e 保留从其生成的完整的 \u003ccode\u003eString\u003c/code\u003e值。 当您传递一个看似很小的 \u003ccode\u003eSubstring\u003c/code\u003e 时，这可能导致意外的高内存开销。所以使用 \u003ccode\u003eSubstring\u003c/code\u003e时，最好转化为 \u003ccode\u003eString\u003c/code\u003e.\u003c/p\u003e","title":"Swift 4.2 新特性"},{"content":"Git 使用教程 一、Git 的安装 Windows版本 : 直接前往git官网下载msysgit安装包进行安装即可，安装流程和一般的windows软件的安装流程差不多，没什么区别。\nMac版本: Mac已经自带了git，无需安装。\n二、配置Git提交的用户名和邮件 这样做的目的是为了在git的log日志里面可以清楚的区分出每次的提交人是谁，以防日后出了问题能够立马清晰准确的定位出是哪个屌丝挖的坑，操作步骤：\n打开 git 控制台： Windows：随便在哪个空白位置右键，然后 git bash，调出git的命令控制台。 Mac：随便打开一个你喜欢的终端即可(Terminal, Iterm2 等)。 然后输入以下命令： $ git config --global user.name \u0026#34;Your Name（用户名）\u0026#34; $ git config --global user.email \u0026#34;email@example.com（邮箱）\u0026#34; 这里简单的说明一下，加上--global参数是表示配置的全局范围，针对所有的git项目而言的，当然也可以分别给某一个项目配置，进入到某个项目里面，去掉–-global参数即可。\n三、生成 SSHKey Git推荐使用SSH协议传输文件（代码），会生成一个公钥和私钥，公钥配置在服务端，每次可以省去验证，方便快捷。当然使用https协议也是可以的，只不过是每次都需要输入用户名和密码，相对来说比较繁琐。\n步骤: 任意空白处右键调出git Bash命令控制台，如果是Mac系统，打开终端就行了，输入以下命令：\n$ ssh-keygen -t rsa -C \u0026#34;youremail@example.com\u0026#34; 然后一路回车，使用默认值即可，应该是需要3个回车的，设置密码为空(注意按3个回车的时候别输入其他的，不然设置的密码不为空，你就等着每次提交输入密码吧)。 如果一切顺利的话，可以在用户主目录里找到.ssh目录，里面有 id_rsa 和 id_rsa.pub 两个文件，这两个就是SSH Key的秘钥对，id_rsa 是私钥，不能泄露出去，id_rsa.pub 是公钥，可以放心地告诉任何人。 一般这个 id_rsa.pub 里面的内容是要配到git服务器上面的（由统一的人去管理），然后我们就可以从服务器上面拿代码和提交代码了。Mac系统默认目录为~/.ssh。\n四、获取代码与提交代码 首先给大家介绍几个概念，一个是本地 worksapce 工作区，一个是 stash 暂存区，一个是 localRepository 本地库，一个是 remoteRepository 远程库。\n从git服务器下载代码到本地 git clone git@github.com:ceeyang/ceeyang.github.io.git 查看本地分支和远程分支，其中-a参数查看远程分支 git branch git branch -a 创建本地分支 git brnach newBranch 切换本地分支 git checkout newBranch 创建本地新分支并切换到该分支 git checkout -b newBranch 基于本地分支创建远程分支 git push origin newBranch 删除分支（注意：删除分支需要先切换到其他的分支，如master分支，再执行删除操作） git checkout master git branch -d dev 创建分支并且切换分支 基于远程分支创建本地分支并且切换到该本地分支，基于远程 dev 分支在本地创建一个 dev 分支，并且切换到 dev 分支。 // 1 git checkout -b newBranch origin/newBranch // 2 git checkout -b newBranch // 3 git checkout newBranch 暂存本地工作区的修改 git add README.MD 通常使用用（\u0026quot;.\u0026ldquo;代表所有文件，注意\u0026rdquo;.\u0026ldquo;前面的空格）\ngit add . 提交暂存区的内容到本地库中 git commit -m \u0026#34;注释\u0026#34; 抓取远程库最新提交的代码, git fetch origin dev 或者：\ngit fetch git fetch是简写(git 会自动搜索和当前分支名相同的远程分支名)\n将本地修改与远程进行合并此时，若遇到冲突，需要先手动解决冲突，再执行合并。 git rebase origin/dev 这个时候如果出现了冲突，就先解决冲突，冲突解决完之后，需先缓存到本地工作区，然后执行合并。\ngit add . git rebase --continue 安装changeId自动生成hook 注意：如果使用了gerrit代码审查工具，提交的时候就必须要先生成这个changID值，如果没有这个changID,则不能到gerrit代码审核工具中进行代码审核操作了。而gerrit代码审查工具的存在就没有意义了。 操作步骤：进入到工作空间（workspace）中的仓库目录，输入如下命令： scp -p -P 29418 username@www.dreamtech.com.cn:hooks/commit-msg .git/hooks/ 将本地仓库中的代码push推送到远程仓库中 这里需要注意： 我们也可以直接用命令git push origin dev将代码直接push到远程代码仓库，但是由于现在我们这边是使用了gerrit代码审查工具，所有不能直接这样推送，必须使用git push origin head:refs/for/dev 这个命令将代码推送到gerrit服务器上，然后我们再登录到gerrit中（会有一个web页面供我们操作）进行审核之后，执行最后合并，此时如果报错的话 有可能是因为你的changID没有生成，使用之前介绍的命令生成changID。 git push origin HEAD:refs/for/dev 或者:\ngit push origin head:refs/for/dev 登录到gerrit服务器中，进行代码审核，然后执行合并（这一步先不管，专人统一审核，合并提交代码到远程中央仓库） 五、撤销修改 场景一：当你改乱了工作区某个文件的内容，想直接丢弃工作区的修改时，使用 git checkout -- filename 通常用（\u0026rdquo;.\u0026ldquo;代表所有文件，注意\u0026rdquo;.\u0026ldquo;前面的空格）\ngit checkout -- . 场景二：当你不但改乱了工作区的某个文件的内容，而且还提交到了暂存区（在本地执行了commit提交，但是还没有commit提交到远程服务器）时，想丢弃修改，分两步： 第一步: 使用命令:\ngit reset HEAD filename 或者使用.代替全部：\ngit reset HEAD . 就又回到了场景一.\n第二步: 再按照场景一的步骤操作.\n场景三：已经提交了和不合适的修改带版本库时，想要撤销本次修改，则可以使用版本回退，但是前提是还没有推送push到远程库 版本回退见下面介绍\n六、版本回退 HEAD指向的版本就是当前版本，因此，Git允许我们在版本的历史之间穿梭，使用命令\ngit reset --soft commit_id 或者\ngit reset --soft HEAD^ 穿梭前，用git log可以查看提交历史，以便确定要回退到哪个版本。 要重返未来，用git reflog查看命令历史，以便确定要回到未来的哪个版本。\n七、Stash 暂存 关于git Stash的详细解释，适用场合，这里做一个说明： 当前工作区内容已被修改，但是并未完成。这时 Boss 来了，说前面的分支上面有一个 Bug ，需要立即修复。可是我又不想提交目前的修改，因为修改没有完成。但是，不提交的话，又没有办法checkout到另外的分支。此时用git Stash就相当于备份当前工作区了。然后在Checkout过去修改，就能够达到保存当前工作区，并及时恢复的作用。 之后，另一个版本的 bug 修复完毕之后，再使用checkout切换回之前的分支，然后再使用git stash pop命令或者git stash apply命令就又可以恢复回来了。\ngit stash save \u0026#34;备注说明\u0026#34; 备份当前的工作区的内容，从最近的一次提交中读取相关内容，让工作区保证和上次提交的内容一致。同时，将当前的工作区内容保存到Git栈中。\ngit stash pop stashId 从Git栈中读取最近一次保存的内容，恢复工作区的相关内容。由于可能存在多个Stash的内容，所以用栈来管理，pop会从最近的一个stash中读取内容并恢复。\ngit stash list 显示Git栈内的所有备份，可以利用这个列表来决定从那个地方恢复。\ngit stash clear 清空Git栈。此时使用gitg等图形化工具会发现，原来stash的哪些节点都消失了。\n八、git如何放弃所有本地修改 git checkout . 放弃本地所有修改的。没有的提交的，都返回到原来的状态 git stash . 把所有没有提交的修改暂存到stash里面。可用git stash pop恢复。 git reset --hard HASH 返回到某个节点，不保留修改。\ngit reset --soft HASH 返回到某个节点。保留修改\n九、Git只更新远程库中的单个或者指定的文件 在开发过程中，我们有的时候并不需要更新远程所有的文件，而只需要更新某一个或者说特定的文件，如何操作呢？ SVN 更新单个文件只需要svn up file/to/updat，非常简单，而且没有任何歧义。 但是git由于在远程服务器和本地都有一个代码库，这样只更新单个文件的话比SVN稍微要麻烦一点。\n如果想拿远程git服务器上的最新版本（或者某个特定版本）去覆盖本地的修改，可以使用git pull命令，但是这样会全面更新本地代码库和工作拷贝。 如果想放弃本地工作拷贝所做的修改，可以使用 git checkout filePath命令，但该命令只能用本地库覆盖你的工作拷贝，并不能取得远程版本的更新。 所以，正确的方法应该是先更新本地库（但是不更新工作拷贝），然后用本地库来更新单个的工作拷贝文件。 例如：假设说开发小伙A在本地新增或者是修改了某个文件file1,然后提交到远程库了，之后开发小伙B想要拿到这个file1文件，（注意场景：这里是只想拿到这一个文件file1，小伙A有可能提交了多个文件），那么小伙B这边可以这么做：\n第一步，抓取远程库最新修改：git fetch 第二步，指定某个文件进行更新: git checkout origin/dev -- filePath 十、分支的合并 假设我们当前的版本有一个master主干分支，同时还有一个dev开发分支，因为通常情况下我们做开发都是在dev分支下进行的开发，之后开发完成后需要将dev分支开发的功能同步到master主干分支上，也就是合并到master分支上，那么怎么操作呢？\n1.首先切换到需要合并的分支上，打个比方说：现在我们需要将dev分支合并到master主干分支，第一步：首先我们需要切换到master主干分支上 :\ngit checkout master 2.为了保证当前的本地master分支的内容与远程服务器中master分支的内容保持同步，我们需要先同步代码:\ngit pull origin master 通常我们会拆成两个步骤来完成：\ngit fetch origin master 或者\ngit fetch git rebase origin/master 3.将dev分支合并到当前master分支\ngit merge dev 或者\ngit merge --no-ff dev 4.此时如果在合并的过程中出现了冲突，则需要先解决冲突，解决方法同 svn 差不多\n5.冲突解决完之后，再继续执行合并操作\ngit add -A git rebase --continue 6.合并成功之后，我们再将master分支push推送到远程库中即可\ngit push origin master 十一、如何合并某个分支中的一个或者是几个commit（s）提交 git cherry-pick可以选择某一个分支中的一个或者是几个commit(s)来进行合并操作。在实际应用中，我们可能会遇到这样的情况：\n假设现在我们有个稳定版本的分支叫做release，另外还有一个开发版本的分支叫做dev，然后我们在dev开发分支里面修改了一些东西（新增了几个个功能，或者是修复了几个bug等等），之后 commit 了几个（这里注意是几个）更新操作（也就是提交到了本地库），但是后来我们发现同样需要在release分支上同步应用某个更新，我们不能直接将release分支和dev分支执行合并，因为这样将会导致稳定版本混乱，但是确实又想增加一个dev分支中的某个功能到release分支中来，那么这时我们就可以使用cherry-pick命令来解决这个问题了，就是对已经存在的commit进行再次commit（提交）操作。简单用法：\ngit cherry-pick \u0026lt;commit_id\u0026gt; 注意：当执行完cherry-pick以后，将会生成一个新的提交，这个新的提交的哈希值和原来的不一致，但标识名是一样的。\n1.首先，切换到dev开发分支\ngit checkout dev 2.然后找到dev分支的commitId 38361a6（说明：本来commitId是38361a68138140827b31b72f8bbfd88b3705d77a这么多位，但是可以只取前7位就行了。使用git log命令可以查看提交的 commitId）\ngit log commit 38361a68138140827b31b72f8bbfd88b3705d77a 3.之后再次切换回 release 分支\ngit checkout release 4.更新一下 release 版本的代码，保持与远程仓库的同步\ngit fetch origin release git rebase orgin/release 5.执行cherry-pick命令，同步 dev 分支的修改代码\ngit cherry-pick 38361a6 6.如果顺利，就会出现下面这段提示，当然也有可能不出现这段提示，只要没报冲突，则说明代码同步成功了。\nFinished one cherry-pick # On branch old_cc # Your branch is ahead of \u0026#39;origin/old_cc\u0026#39; by 3 commits. 7.如果在cherry-pick的过程中出现了冲突就手动解决冲突，解决完冲突之后根据提示继续执行同步。\ngit add -A git cherry-pick --continue 8.最后push到远程仓库。\ngit push origin release 十二、一个完整的团队开发流程 1.克隆远程仓库\ngit clone ssh://xxx@www.dreamtech.com:12256/xxx 2.切换到对应分支:git checkout dev、git checkout -b dev or git checkout -b dev origin/dev\ngit checkout -b dev origin/dev 3.查看当前分支状态，查看提交。\ngit status git log 4.添加 commit msg ,一个项目只需要添加一次即可。\nscp -p -P 29418 username@www.dreamtech.com.cn:hooks/commit-msg .git/hooks/ 5.当本地修改后，缓存本地文件。添加 commit msg。\ngit add -A 或者 git add . git commit -m \u0026#34;注释\u0026#34; 6.抓取远程提交，合并本地代码。\ngit fetch origin dev git rebase origin/dev 这个时候如果出现冲突，注意出现冲突只可能会是在合并(rebase)时出现冲突，那我们就直接解决冲突即可，解决完之后,再依次执行：\ngit add -A git rebase --continue 9.推送到远程仓库。\ngit push origin HEAD:refs/for/dev 或者如果远程库（gerrit仓库）只是一个空的仓库的话，这就需要先将本地库的代码关联到gerrit远程库（即将本地库与远程库建立映射关系），之后再推送上去。执行以下命令：\ngit remote add origin ssh://xxx@www.dreamtech.com:12256/xxx git push -u origin HEAD:refs/for/master 这里加了-u参数，首次推送的话需要加上这个-u参数，以后都不需要了。\n","permalink":"https://ceeyang.com/posts/2017-06-25-git-%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B/","summary":"\u003ch1 id=\"git-使用教程\"\u003eGit 使用教程\u003c/h1\u003e\n\u003ch2 id=\"一git-的安装\"\u003e一、Git 的安装\u003c/h2\u003e\n\u003ch6 id=\"windows版本-\"\u003eWindows版本 :\u003c/h6\u003e\n\u003cp\u003e直接前往git官网下载\u003ca href=\"http://msysgit.github.io/\"\u003emsysgit安装包\u003c/a\u003e进行安装即可，安装流程和一般的windows软件的安装流程差不多，没什么区别。\u003c/p\u003e\n\u003ch6 id=\"mac版本\"\u003eMac版本:\u003c/h6\u003e\n\u003cp\u003eMac已经自带了git，无需安装。\u003c/p\u003e\n\u003ch2 id=\"二配置git提交的用户名和邮件\"\u003e二、配置Git提交的用户名和邮件\u003c/h2\u003e\n\u003cp\u003e这样做的目的是为了在git的log日志里面可以清楚的区分出每次的提交人是谁，以防日后出了问题能够立马清晰准确的定位出是哪个屌丝挖的坑，操作步骤：\u003c/p\u003e\n\u003ch5 id=\"打开-git-控制台\"\u003e打开 git 控制台：\u003c/h5\u003e\n\u003cul\u003e\n\u003cli\u003eWindows：随便在哪个空白位置右键，然后 \u003ccode\u003egit bash\u003c/code\u003e，调出git的命令控制台。\u003c/li\u003e\n\u003cli\u003eMac：随便打开一个你喜欢的终端即可(\u003cstrong\u003eTerminal\u003c/strong\u003e, \u003cstrong\u003eIterm2\u003c/strong\u003e 等)。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch5 id=\"然后输入以下命令\"\u003e然后输入以下命令：\u003c/h5\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ git config --global user.name \u0026#34;Your Name（用户名）\u0026#34;\n$ git config --global user.email \u0026#34;email@example.com（邮箱）\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e这里简单的说明一下，加上\u003ccode\u003e--global\u003c/code\u003e参数是表示配置的全局范围，针对所有的git项目而言的，当然也可以分别给某一个项目配置，进入到某个项目里面，去掉\u003ccode\u003e–-global\u003c/code\u003e参数即可。\u003c/p\u003e\n\u003ch2 id=\"三生成-sshkey\"\u003e三、生成 SSHKey\u003c/h2\u003e\n\u003cp\u003eGit推荐使用SSH协议传输文件（代码），会生成一个公钥和私钥，公钥配置在服务端，每次可以省去验证，方便快捷。当然使用https协议也是可以的，只不过是每次都需要输入用户名和密码，相对来说比较繁琐。\u003c/p\u003e\n\u003ch5 id=\"步骤\"\u003e步骤:\u003c/h5\u003e\n\u003cp\u003e任意空白处右键调出\u003ccode\u003egit Bash\u003c/code\u003e命令控制台，如果是Mac系统，打开终端就行了，输入以下命令：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ ssh-keygen -t rsa -C \u0026#34;youremail@example.com\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e然后一路回车，使用默认值即可，应该是需要3个回车的，设置密码为空(注意按3个回车的时候别输入其他的，不然设置的密码不为空，你就等着每次提交输入密码吧)。\u003c/br\u003e\n如果一切顺利的话，可以在用户主目录里找到.ssh目录，里面有 \u003cstrong\u003eid_rsa\u003c/strong\u003e 和 \u003cstrong\u003eid_rsa.pub\u003c/strong\u003e 两个文件，这两个就是SSH Key的秘钥对，\u003cstrong\u003eid_rsa\u003c/strong\u003e 是私钥，不能泄露出去，\u003cstrong\u003eid_rsa.pub\u003c/strong\u003e 是公钥，可以放心地告诉任何人。\u003c/br\u003e\n一般这个 \u003cstrong\u003eid_rsa.pub\u003c/strong\u003e 里面的内容是要配到git服务器上面的（由统一的人去管理），然后我们就可以从服务器上面拿代码和提交代码了。Mac系统默认目录为\u003ccode\u003e~/.ssh\u003c/code\u003e。\u003c/p\u003e\n\u003ch2 id=\"四获取代码与提交代码\"\u003e四、获取代码与提交代码\u003c/h2\u003e\n\u003cp\u003e首先给大家介绍几个概念，一个是本地 \u003cstrong\u003eworksapce\u003c/strong\u003e 工作区，一个是 \u003cstrong\u003estash\u003c/strong\u003e 暂存区，一个是 \u003cstrong\u003elocalRepository\u003c/strong\u003e 本地库，一个是 \u003cstrong\u003eremoteRepository\u003c/strong\u003e 远程库。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e从git服务器下载代码到本地\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egit clone git@github.com:ceeyang/ceeyang.github.io.git\n\u003c/code\u003e\u003c/pre\u003e\u003cul\u003e\n\u003cli\u003e查看本地分支和远程分支，其中\u003ccode\u003e-a\u003c/code\u003e参数查看远程分支\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egit branch\ngit branch -a\n\u003c/code\u003e\u003c/pre\u003e\u003cul\u003e\n\u003cli\u003e创建本地分支\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egit brnach newBranch\n\u003c/code\u003e\u003c/pre\u003e\u003cul\u003e\n\u003cli\u003e切换本地分支\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egit checkout newBranch\n\u003c/code\u003e\u003c/pre\u003e\u003cul\u003e\n\u003cli\u003e创建本地新分支并切换到该分支\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egit checkout -b newBranch\n\u003c/code\u003e\u003c/pre\u003e\u003cul\u003e\n\u003cli\u003e基于本地分支创建远程分支\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egit push origin newBranch\n\u003c/code\u003e\u003c/pre\u003e\u003cul\u003e\n\u003cli\u003e删除分支（注意：删除分支需要先切换到其他的分支，如\u003ccode\u003emaster\u003c/code\u003e分支，再执行删除操作）\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egit checkout master\ngit branch -d dev\n\u003c/code\u003e\u003c/pre\u003e\u003cul\u003e\n\u003cli\u003e创建分支并且切换分支\n基于远程分支创建本地分支并且切换到该本地分支，基于远程 dev 分支在本地创建一个 dev 分支，并且切换到 dev 分支。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// 1\ngit checkout -b  newBranch origin/newBranch  \n// 2\ngit checkout -b newBranch\n// 3\ngit checkout newBranch\n\u003c/code\u003e\u003c/pre\u003e\u003cul\u003e\n\u003cli\u003e暂存本地工作区的修改\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egit add README.MD\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e通常使用用（\u0026quot;\u003ccode\u003e.\u003c/code\u003e\u0026ldquo;代表所有文件，注意\u0026rdquo;\u003ccode\u003e.\u003c/code\u003e\u0026ldquo;前面的空格）\u003c/p\u003e","title":"Git 快速入门教程"},{"content":"若我只是你一红尘过客，\n愿我不曾扰你\n愿岁月静好。\n一个人，一杯咖啡，一台电脑，一下午。\n在悠扬的旋律中将自己的心事铺开，往事如似水流年，在眼前一幕幕滑过，或清晰如画，或淡淡如烟。\n很是感谢这些年你的陪伴，我将倾尽我所有，为博与你共赴余生。\n如果终将无法相濡以沫，那就相忘于江湖吧。\n我会记得你，祝岁月静好。\n","permalink":"https://ceeyang.com/posts/2017-06-25-foryou001/","summary":"\u003cp\u003e若我只是你一红尘过客，\u003c/p\u003e\n\u003cp\u003e愿我不曾扰你\u003c/p\u003e\n\u003cp\u003e愿岁月静好。\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003e一个人，一杯咖啡，一台电脑，一下午。\u003c/p\u003e\n\u003cp\u003e在悠扬的旋律中将自己的心事铺开，往事如似水流年，在眼前一幕幕滑过，或清晰如画，或淡淡如烟。\u003c/p\u003e\n\u003cp\u003e很是感谢这些年你的陪伴，我将倾尽我所有，为博与你共赴余生。\u003c/p\u003e\n\u003cp\u003e如果终将无法相濡以沫，那就相忘于江湖吧。\u003c/p\u003e\n\u003cp\u003e我会记得你，祝岁月静好。\u003c/p\u003e","title":"岁月静好"},{"content":"时间过得太快，转眼已经是六月底了。离今年自己的目标好远。 一直想写点东西，却不知道怎么写，要如何才能养成写作的好习惯。 最近玩的心思太重了。还记得是某天，在公司上班听歌，突然想再走一朝江湖了，然后去玩儿了倩女。 也差不就是那个时间开始玩儿到现在，大概是三个月前吧。一直玩儿到现在。我也不知道自己在写啥，思绪很乱。\n从今年的1月1号开始，基本都是下班就走，不加班，周末不加班，没了刚出来的那种拼搏的动力了。 沉沦了半年，去年说的今年要跳槽也没跳，一直没去学习新的知识，知识拿着现有的用，缺啥找啥，感觉也没认真复习。 今天才醒悟时间过去了一半，不能再这样沉沦下去了，该好好规划一下了。\n首先不能再继续玩儿了，玩儿得心不能太重，多花时间在学习工作上。 ","permalink":"https://ceeyang.com/posts/2017-06-25-%E8%AF%B4%E8%AF%B4%E6%9C%80%E8%BF%91%E7%9A%84%E8%BF%91%E5%86%B5%E5%90%A7/","summary":"\u003cpre\u003e\u003ccode\u003e时间过得太快，转眼已经是六月底了。离今年自己的目标好远。\n\n一直想写点东西，却不知道怎么写，要如何才能养成写作的好习惯。\n\n最近玩的心思太重了。还记得是某天，在公司上班听歌，突然想再走一朝江湖了，然后去玩儿了倩女。\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e也差不就是那个时间开始玩儿到现在，大概是三个月前吧。一直玩儿到现在。我也不知道自己在写啥，思绪很乱。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e从今年的1月1号开始，基本都是下班就走，不加班，周末不加班，没了刚出来的那种拼搏的动力了。\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e沉沦了半年，去年说的今年要跳槽也没跳，一直没去学习新的知识，知识拿着现有的用，缺啥找啥，感觉也没认真复习。\n今天才醒悟时间过去了一半，不能再这样沉沦下去了，该好好规划一下了。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e首先不能再继续玩儿了，玩儿得心不能太重，多花时间在学习工作上。\n\u003c/code\u003e\u003c/pre\u003e","title":"说说近来近况与计划"},{"content":"WWDC2017于今天凌晨1点准时开始，说实话笔者等这一天等很久了。\n##以下是笔者的上手体验：\n系统界面的更新 iMessage：通过iCloud将iMessage里的对话内容进行云端同步。 Camera：相机可以直接读取图片中的二维码，每张照片压缩率为此前的两倍，你会有更多空间存储照片，此外还可更好地分类，笔者测试了一下，拍一张照片是比以前用的内存小，各位内存吃紧的小伙伴有福利了。 Control Center：更多的功能将加入其中，改变音量大小等变为了模块式，界面优化明显。 Siri：通过深度学习，可以理解你的言语，结合上下文进一步知晓你的兴趣爱好等，笔者对iOS9的 Siri有点小失望，这一代的估计不错，不过笔者来得及没体验，留个各位同学自己上手体验吧。 Apple Music：可以与朋友一同分享音乐，包括微信，QQ。 App Store：经过重新设计，内容将通过“今日推荐”、“游戏”、“App”三个标签页进行展示。 Keyboard：内置键盘加入中国的拼音输入，并且新增单手操作模式。 Fiels：属于 iOS 的文件管理器。不过貌似没啥用，不能获取其他 APP 的沙盒文件。 ###凌晨看完发布会直播，眼睛已经涩得不行，只能去睡觉，第二天体验新版的 iOS11 系统了，一大早来公司就下载了新系统安装，刚开始还不适应，不过感觉也还行。使用中发现许多 bug,不过毕竟是测试版，就不多做评价。下面笔者带你走进 iOS11，至于是否更新测试版本各位同学自己决定咯。\n系统界面的更新 顶部状态了的信号图标变了，变成安卓风格，个人觉得还是以前的小圆点好看，虽然笔者用的是表示信号强度的数字。以前锁屏界面的通知消息能够右滑清除，新版的系统好像不可以了。只能长安，或者重按，笔者用的 iPhone 7，带 3DTouch 的功能，重按后出现了上面的通知详情界面。上拉能显示历史通知消息，笔者这里把历史通知都删除了，看不到所以就没截图。不管是在通知消息上面右滑还是在空白处右滑都会打开相机。有点小小的不习惯啊。\n进入主页的动画效果也有改变。\nCamera：照相机 哈哈，相机已经可以直接读取图片中的二维码了，也就是说拿着相机就可以扫描其他二维码，虽然整个过程需要打开浏览器，再打开各个 APP，过程比较繁琐，但这进步也是值得称赞的。不过笔者跳转到微信后没任何反应，哈哈，看来又是 bug 。期待后期的使用了。等等，听说安卓的早就可以了？安卓同学请略过QAQ。\nControl Center：控制中心 控制中心可以自定义菜单，如图，笔者选了一些功能。笔者这里只提几点：点击 WiFi 后只能打开与关闭，重按也只能打开一个更多的页面，并不能在控制中心选择 WiFi，蓝牙也是如此。看以前暴露的视频，WiFi 和蓝牙是可以在控制中心选择的，但笔者没测试出来，或许是 bug 来着。字体大小按钮，可以将全局的字体整体放大缩小，拿 iPhone 当老年机的话笔者感觉这个功能会有点用。辅助功能里面新增几个选项：SOS，Pay，重新启动，SOS 创建临时急救方案，点一下自动拨打急救电话，貌似以前也有，现在提到控制中心了而已。其他几个也不多说。 计算器页面也重新做了次，各位同学不知意下如何。\nApple Music：苹果音乐 Apple Music 支持分享到 QQ 和微信，微博暂时还不能分享过去。\nApp Store：苹果应用商店 App Store 界面大变样，转为你各人设置，根据平时爱好推荐新品。话说王者农药居然是在搜索榜第一位！\nKeyboard：键盘添加中午输入，并新增单手操作模式 哈哈，用惯了搜狗的笔者还是觉得有点鸡肋，仅仅是各人观点，勿喷。不知小伙伴们意下如何，哈哈。\nFiels：iOS 的文件管理器 ： 更新系统后会发现多了一个叫做 Files（文件）的 APP，打开使用了几下，出了不少 bug。不得不吐槽一下，除了能打开 iCloud Drive 上的备份文件之外，好像并没什么用。不能打开其他 APP 中的文件，QQ音乐啥的里面的音乐一首都搜索不到，AirDrop 上传到手机的文件也收不到，不知道这个管理器能干啥。或许是给土豪们用来管理 iCloud 的，哈哈。\n今早第一时间来就更新了新系统，使用中还是发现不少 bug,这里就不一一列出来了。笔者建议小伙伴们还是等待一段时间，等稳定了再安装新版本吧。\n","permalink":"https://ceeyang.com/posts/2017-6-6-ios11%E4%B8%8A%E6%89%8B%E4%BD%93%E9%AA%8C/","summary":"\u003cp\u003eWWDC2017于今天凌晨1点准时开始，说实话笔者等这一天等很久了。\u003c/p\u003e\n\u003cp\u003e##以下是笔者的上手体验：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"#0\"\u003e系统界面的更新\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003col\u003e\n\u003cli\u003eiMessage：通过iCloud将iMessage里的对话内容进行云端同步。\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#1\"\u003eCamera\u003c/a\u003e：相机可以直接读取图片中的二维码，每张照片压缩率为此前的两倍，你会有更多空间存储照片，此外还可更好地分类，笔者测试了一下，拍一张照片是比以前用的内存小，各位内存吃紧的小伙伴有福利了。\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#4\"\u003eControl Center\u003c/a\u003e：更多的功能将加入其中，改变音量大小等变为了模块式，界面优化明显。\u003c/li\u003e\n\u003cli\u003eSiri：通过深度学习，可以理解你的言语，结合上下文进一步知晓你的兴趣爱好等，笔者对iOS9的 Siri有点小失望，这一代的估计不错，不过笔者来得及没体验，留个各位同学自己上手体验吧。\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#7\"\u003eApple Music\u003c/a\u003e：可以与朋友一同分享音乐，包括微信，QQ。\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#8\"\u003eApp Store\u003c/a\u003e：经过重新设计，内容将通过“今日推荐”、“游戏”、“App”三个标签页进行展示。\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#9\"\u003eKeyboard\u003c/a\u003e：内置键盘加入中国的拼音输入，并且新增单手操作模式。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"#10\"\u003eFiels\u003c/a\u003e：属于 iOS 的文件管理器。不过貌似没啥用，不能获取其他 APP 的沙盒文件。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e###凌晨看完发布会直播，眼睛已经涩得不行，只能去睡觉，第二天体验新版的 iOS11 系统了，一大早来公司就下载了新系统安装，刚开始还不适应，不过感觉也还行。使用中发现许多 bug,不过毕竟是测试版，就不多做评价。下面笔者带你走进 iOS11，至于是否更新测试版本各位同学自己决定咯。\u003c/p\u003e\n\u003ch4 id=\"系统界面的更新\"\u003e\u003ch4 id=\"0\"\u003e系统界面的更新\u003c/h4\u003e\u003c/h4\u003e\n\u003cp\u003e\u003cimg alt=\"image.png\" loading=\"lazy\" src=\"http://upload-images.jianshu.io/upload_images/1395849-89f22bb32d743551.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image.png\" loading=\"lazy\" src=\"http://upload-images.jianshu.io/upload_images/1395849-bb06699395d4615b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image.png\" loading=\"lazy\" src=\"http://upload-images.jianshu.io/upload_images/1395849-9a4a239e38e15083.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\"\u003e\u003c/p\u003e\n\u003cp\u003e　　顶部状态了的信号图标变了，变成安卓风格，个人觉得还是以前的小圆点好看，虽然笔者用的是表示信号强度的数字。以前锁屏界面的通知消息能够右滑清除，新版的系统好像不可以了。只能长安，或者重按，笔者用的 iPhone 7，带 3DTouch 的功能，重按后出现了上面的通知详情界面。上拉能显示历史通知消息，笔者这里把历史通知都删除了，看不到所以就没截图。不管是在通知消息上面右滑还是在空白处右滑都会打开相机。有点小小的不习惯啊。\u003c/p\u003e\n\u003cp\u003e　　进入主页的动画效果也有改变。\u003c/p\u003e\n\u003chr\u003e\n\u003ch4 id=\"camera照相机\"\u003e\u003ch4 id=\"1\"\u003e Camera：照相机\u003c/h4\u003e\u003c/h4\u003e\n\u003cp\u003e\u003cimg alt=\"image.png\" loading=\"lazy\" src=\"http://upload-images.jianshu.io/upload_images/1395849-7c0f3436f456dfe1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image.png\" loading=\"lazy\" src=\"http://upload-images.jianshu.io/upload_images/1395849-cbf0edacf405a6de.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\"\u003e\u003c/p\u003e\n\u003cp\u003e　　哈哈，相机已经可以直接读取图片中的二维码了，也就是说拿着相机就可以扫描其他二维码，虽然整个过程需要打开浏览器，再打开各个 APP，过程比较繁琐，但这进步也是值得称赞的。不过笔者跳转到微信后没任何反应，哈哈，看来又是 bug 。期待后期的使用了。等等，听说安卓的早就可以了？安卓同学请略过QAQ。\u003c/p\u003e\n\u003chr\u003e\n\u003ch4 id=\"control-center控制中心\"\u003e\u003ch4 id = \"4\"\u003e Control Center：控制中心 \u003ch4\u003e\u003c/h4\u003e\n\u003cp\u003e\u003cimg alt=\"image.png\" loading=\"lazy\" src=\"http://upload-images.jianshu.io/upload_images/1395849-7fd4b10dd5234ffa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image.png\" loading=\"lazy\" src=\"http://upload-images.jianshu.io/upload_images/1395849-9b97ee5ee4f9daa7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image.png\" loading=\"lazy\" src=\"http://upload-images.jianshu.io/upload_images/1395849-0e275d2ee493a4a2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image.png\" loading=\"lazy\" src=\"http://upload-images.jianshu.io/upload_images/1395849-c9db4c44d48d77ba.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\"\u003e\u003c/p\u003e\n\u003cp\u003e　　控制中心可以自定义菜单，如图，笔者选了一些功能。笔者这里只提几点：点击 WiFi 后只能打开与关闭，重按也只能打开一个更多的页面，并不能在控制中心选择 WiFi，蓝牙也是如此。看以前暴露的视频，WiFi 和蓝牙是可以在控制中心选择的，但笔者没测试出来，或许是 bug 来着。字体大小按钮，可以将全局的字体整体放大缩小，拿 iPhone 当老年机的话笔者感觉这个功能会有点用。辅助功能里面新增几个选项：SOS，Pay，重新启动，SOS 创建临时急救方案，点一下自动拨打急救电话，貌似以前也有，现在提到控制中心了而已。其他几个也不多说。 计算器页面也重新做了次，各位同学不知意下如何。\u003c/p\u003e\n\u003chr\u003e\n\u003ch4 id=\"apple-music苹果音乐\"\u003e\u003ch4 id = \"7\"\u003e Apple Music：苹果音乐 \u003ch4\u003e\u003c/h4\u003e\n\u003cp\u003e\u003cimg alt=\"image.png\" loading=\"lazy\" src=\"http://upload-images.jianshu.io/upload_images/1395849-e214b931552eab83.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\"\u003e\u003c/p\u003e","title":"iOS11上手体验"},{"content":"　I have a dream.It\u0026rsquo;s marry you.\nSo much words wants to talk you.\nI miss you so much already and I haven’t even know you yet!\n","permalink":"https://ceeyang.com/posts/2017-01-09-i-have-a-dream/","summary":"\u003cp\u003e　　I have a dream.It\u0026rsquo;s marry you.\u003c/p\u003e\n\u003cp\u003e　　So much words wants to talk you.\u003c/p\u003e\n\u003cp\u003e　　I miss you so much already and I haven’t even know you yet!\u003c/p\u003e","title":"I have a dream"},{"content":"　Stay with me or bring me away.\n","permalink":"https://ceeyang.com/posts/2017-01-09-about-leave/","summary":"\u003cp\u003e　　Stay with me or bring me away.\u003c/p\u003e","title":"Leave me"},{"content":"Finally I make up my mind You are the one I want to find How I wish I could stay by your side I promise wherever I will follow you Life is not easy as you told Hrer I am always trying to convince my soul Unwilling to hear you lose your hope If you love me tell me so I love you or te amo Above suspicion,you're my whole But you are irresolute When you hold me in your arms never never be apart I can feel you when you go so far your love is in my palm inside my heart Finally I make up my mind You are the one I want to find How I wish I could stay by your side I promise wherever I will follow you Life is not easy as you told Here I am always trying to convince my soul Unwilling to hear you lose your hope If you love me tell me so I love you or te amo Above suspicion, you're my whole But you are irresolute When you hold me in your arms never never be apart I can feel you when you go so far your love is in my palm inside my heart Maybe someday you have to go Let me be the first one to know I will give my diary to you Everyday I write it out of love, just for you ","permalink":"https://ceeyang.com/posts/2017-1-7-%E5%8B%87%E6%B0%94/","summary":"\u003cpre\u003e\u003ccode\u003eFinally I make up my mind\nYou are the one I want to find\nHow I wish I could stay by your side\nI promise wherever I will follow you\nLife is not easy as you told\nHrer I am always trying to convince my soul\nUnwilling to hear you lose your hope\nIf you love me tell me so\nI love you or te amo\nAbove suspicion,you're my whole\nBut you are irresolute\nWhen you hold me in your arms never never be apart\nI can feel you when you go so far your love is in my palm inside my heart\nFinally I make up my mind\nYou are the one I want to find\nHow I wish I could stay by your side\nI promise wherever I will follow you\nLife is not easy as you told\nHere I am always trying to convince my soul\nUnwilling to hear you lose your hope\nIf you love me tell me so\nI love you or te amo\nAbove suspicion, you're my whole\nBut you are irresolute\nWhen you hold me in your arms never never be apart\nI can feel you when you go so far your love is in my palm inside my heart Maybe someday you have to go\nLet me be the first one to know\nI will give my diary to you\nEveryday I write it out of love, just for you\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cembed autostart=\"true\" hidden=\"true\" loop=\"true\" src=\"http://data.5sing.kgimg.com/G032/M00/19/11/wIYBAFX0dyqAGVXtAFcBeBHNpIo979.mp3\"\u003e\u003c/embed\u003e\u003c/p\u003e","title":"勇气"},{"content":" 世事无常，总是让人琢磨不透。\n","permalink":"https://ceeyang.com/posts/2017-08-26-%E6%B7%B1%E5%A4%9C%E7%8B%AC%E7%99%BD/","summary":"\u003chr\u003e\n\u003cp\u003e世事无常，总是让人琢磨不透。\u003c/p\u003e","title":"深夜独白"},{"content":"Sometimes I want to write someting,but when I make my tool ready,I had forgot what I want to say. People always are. You are the reason why I become stronger,but still you are my weakness. Finally I make my mind you are the one I want to find. I wanna do someting for you, but I cant't find the reason for you. ","permalink":"https://ceeyang.com/posts/2017-1-7-%E9%9A%8F%E7%AC%94/","summary":"\u003cpre\u003e\u003ccode\u003eSometimes I want to write someting,but when I make my tool ready,I had forgot what I want to say.\nPeople always are.\n\nYou are the reason why I become stronger,but still you are my weakness.\n\nFinally I make my mind you are the one I want to find.\n\nI wanna do someting for you, but I cant't find the reason for you.\n\u003c/code\u003e\u003c/pre\u003e","title":"随笔"},{"content":"App Hotfix(热修复)详解 定义：\n从广义的角度理解，大家都比较认同 Hotfix 是在移动端不需要重新发版，通过在线更新对版本 Bug 的修复。\n现在比较流行的热修复技术分为三种： 一、使用JSPatch进行热修复： Objective-C 是动态语言，OC上所有方法的调用/类的生成都通过 Objective-C Runtime 在运行时进行，我们可以通过类名和方法名反射得到相应的类和方法，也可以替换某个类的方法为新的实现，还可以新注册一个类，为类添加方法替换方法，通过这些即可实现动态修复 APP 技术。\nJSPatch是一个在Github上的开源项目，JSPatch下载地址。JSPatch 的实现主要是通过 Objective-C的 runtime 原理,即利用JS传递字符串给OC，OC通过 Runtime 接口调用和替换OC方法。具体实现原理请参考作者的帖子：JSPatch 实现原理详解 (整改版)。\nOC转JS工具,具体实现参考该博客\n大体实现思路如下： 首先，开发者提供热修复脚本； 其次，要将脚本上传到后台，后台需要提供上传的操作页面； 然后，终端设备每次运行后请求获取最新的脚本文件； 最后，解析脚本文件，调用 JSPatch 引擎，执行脚本文件并修复； 1、开发者提供热修复脚本: 脚本的书写：\n脚本书写也很简单，先用Objective-C将要需要更改的代码改好，然后根据需要修改的代码更改成js代码即可，具体书写方法请参照 JSPatch使用说明，或者使用上面提供的OC转JS工具。\n例如：\nOBjective-C代码，这里是需要修改的内容。\n@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; /** 该代码在上线后的项目里面并没有 * 即在 APP 上线后又临时修改的。 * 添加或者修改我们需要改动代码，如无需改动，该方法不变，此处拿修改标题做测试，可以做很堵其他操作； */ self.title = @\u0026#34;Welcome to use JSPatchDemo\u0026#34;; } /** * 省略其他代码 */ @end javascript代码：JS 属于链式语法，相信很多人都会，可以查看 JSPatch 的语法规则自己写，也可以通过OC转JS工具将上面的代码转换成JS 代码\ndefineClass(\u0026#39;ViewController\u0026#39;, { viewDidLoad: function() { self.super().viewDidLoad(); /** 该代码在上线后的项目里面并没有 * 即在 APP 上线后又临时修改的。 * 添加或者修改我们需要改动代码，如无需改动，该方法不变，此处拿修改标题做测试，可以做很堵其他操作； */ self.setTitle(\u0026#34;Welcome to use JSPatchDemo\u0026#34;); }, }); 至此，热修复脚本已经编写完成。\n2、要将脚本上传到后台，后台需要提供上传的操作页面: 此处需要后台配置，将上面已经写好的脚本放到服务器上面，并开发接口由APP 请求接口获取到脚本文件\n3、终端设备每次运行后请求获取最新的脚本文件: 终端获取请求后台开发的接口，获取到脚本内容，可以根据当前版本与脚本编号判断当前脚本是否是最新脚本，如果是最新脚本，则执行脚本内容。\n4、解析脚本文件，调用 JSPatch 引擎，执行脚本文件并修复: 首先将JSPatch添加到项目中\npod \u0026#39;JSPatch\u0026#39; 然后导入头文件#import \u0026quot;JSPatch.h\u0026quot;, 在程序启动的时候注册 JSPatch\n@implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //此处作为演示，加载本地的 js 文件 //热修复功能一般是从服务器段请求下来，然后再经过版本判断等操作，再加载等。 NSString * jsPath = [[NSBundle mainBundle] pathForResource:@\u0026#34;JSFileName\u0026#34; ofType:@\u0026#34;js\u0026#34;]; //在 js 文件存在的情况下，调用 JSPatch，如果js 文件不存在，程序会崩溃。 if ([[NSFileManager defaultManager] fileExistsAtPath:jsPath] ) { NSString *scriptPatch = [NSString stringWithContentsOfFile:jsPath encoding:NSUTF8StringEncoding error:nil]; [JPEngine startEngine]; [JPEngine evaluateScript:scriptPatch]; } } 至此，利用JSPatch引擎进行 APP 热修复功能差不多就如此了，剩下的就是调试优化上诉各个环节。\n二、使用ReactNative实现热修复： ReactNative是facebook提供的一种开源框架，使用JS语法进行跨平台开发，深受开发者青睐。直接使用ReactNative实现热修复的好像不是很多，比较流行的是使用微软的CodePush。\n这个是CodePush的官方网站地址,这个是CodePushGitHut地址。具体实现参考该博客，讲的特别详细，我就不具体讲解了。\n三、使用Lua+Wax打补丁的方案实现热修复： 由于 lua热修复框架多年不更新，此处省略。\n","permalink":"https://ceeyang.com/posts/2017-1-5-app-hotfix%E7%83%AD%E4%BF%AE%E5%A4%8D%E8%AF%A6%E8%A7%A3/","summary":"\u003ch1 id=\"app-hotfix热修复详解\"\u003eApp Hotfix(热修复)详解\u003c/h1\u003e\n\u003cp\u003e定义：\u003c/p\u003e\n\u003cp\u003e　　从广义的角度理解，大家都比较认同 Hotfix 是在移动端不需要重新发版，通过在线更新对版本 Bug 的修复。\u003c/p\u003e\n\u003ch2 id=\"现在比较流行的热修复技术分为三种\"\u003e现在比较流行的热修复技术分为三种：\u003c/h2\u003e\n\u003ch2 id=\"一使用jspatch进行热修复\"\u003e一、使用JSPatch进行热修复：\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e　　Objective-C 是动态语言，OC上所有方法的调用/类的生成都通过 Objective-C Runtime 在运行时进行，我们可以通过类名和方法名反射得到相应的类和方法，也可以替换某个类的方法为新的实现，还可以新注册一个类，为类添加方法替换方法，通过这些即可实现动态修复 APP 技术。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cblockquote\u003e\n\u003cp\u003e　　\u003ca href=\"https://github.com/bang590/JSPatch\"\u003eJSPatch\u003c/a\u003e是一个在Github上的开源项目，\u003ca href=\"https://github.com/bang590/JSPatch\"\u003eJSPatch下载地址\u003c/a\u003e。\u003ca href=\"https://github.com/bang590/JSPatch\"\u003eJSPatch\u003c/a\u003e 的实现主要是通过 Objective-C的 runtime 原理,即利用JS传递字符串给OC，OC通过 Runtime 接口调用和替换OC方法。具体实现原理请参考作者的帖子：\u003ca href=\"https://segmentfault.com/a/1190000003870981\"\u003eJSPatch 实现原理详解 (整改版)\u003c/a\u003e。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cblockquote\u003e\n\u003cp\u003e　　\u003ca href=\"http://bang590.github.io/JSPatchConvertor\"\u003eOC转JS工具\u003c/a\u003e,具体实现参考该\u003ca href=\"http://www.jianshu.com/p/8cec322531ae\"\u003e博客\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003chr\u003e\n\u003ch3 id=\"大体实现思路如下\"\u003e大体实现思路如下：\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca href=\"#1\"\u003e首先，开发者提供热修复脚本\u003c/a\u003e；\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#2\"\u003e其次，要将脚本上传到后台，后台需要提供上传的操作页面\u003c/a\u003e；\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#3\"\u003e然后，终端设备每次运行后请求获取最新的脚本文件\u003c/a\u003e；\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#4\"\u003e最后，解析脚本文件，调用 JSPatch 引擎，执行脚本文件并修复\u003c/a\u003e；\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch4 id=\"1开发者提供热修复脚本\"\u003e\u003cspan id = \"1\"\u003e1、开发者提供热修复脚本:\u003c/span\u003e\u003c/h4\u003e\n\u003cp\u003e脚本的书写：\u003c/p\u003e\n\u003cp\u003e　　脚本书写也很简单，先用Objective-C将要需要更改的代码改好，然后根据需要修改的代码更改成js代码即可，具体书写方法请参照 \u003ca href=\"https://github.com/bang590/JSPatch\"\u003eJSPatch\u003c/a\u003e使用说明，或者使用上面提供的\u003ca href=\"http://bang590.github.io/JSPatchConvertor\"\u003eOC转JS工具\u003c/a\u003e。\u003c/p\u003e\n\u003cp\u003e例如：\u003c/p\u003e\n\u003cp\u003e　　OBjective-C代码，这里是需要修改的内容。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-Objective-C\" data-lang=\"Objective-C\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e@implementation\u003c/span\u003e \u003cspan class=\"nc\"\u003eViewController\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e-\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"nf\"\u003eviewDidLoad\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nb\"\u003esuper\u003c/span\u003e \u003cspan class=\"n\"\u003eviewDidLoad\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"cm\"\u003e/** 该代码在上线后的项目里面并没有\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e        *  即在 APP 上线后又临时修改的。\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e        *  添加或者修改我们需要改动代码，如无需改动，该方法不变，此处拿修改标题做测试，可以做很堵其他操作；\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e        */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e@\u0026#34;Welcome to use JSPatchDemo\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"cm\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e      * 省略其他代码\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e      */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e@end\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e　　javascript代码：JS 属于链式语法，相信很多人都会，可以查看 JSPatch 的语法规则自己写，也可以通过\u003ca href=\"http://bang590.github.io/JSPatchConvertor\"\u003eOC转JS工具\u003c/a\u003e将上面的代码转换成JS 代码\u003c/p\u003e","title":"App热修复详解"},{"content":"前言 对于习惯使用Storyboard的人来说，设置圆角、描边是一件比较蛋疼的事，因为苹果没有在xcode的Interface Builder上直接提供修改控件的圆角，边框设置。\n我们来说说如何对某个控件进行圆角、描边处理：\n初级 对于一个初学者来说，如果要进行某个控件的圆角、描边设置，就要从Storyboard关联出属性，然后再对属性进行代码处理。\n如下代码：\nself.myButton.layer.cornerRadius = 20; self.myButton.layer.masksToBounds = YES; self.myButton.layer.borderWidth = 2; self.myButton.layer.borderColor = [UIColor blackColor].CGColor; 这样不仅需要Storyboard关联出属性，还要写一堆代码对属性进行设置，不得不说实在麻烦~\n中级 更聪明的做法是使用Storyboard提供的Runtime Attributes为控件添加圆角描边。\n选中控件，然后在Runtime Attributes框中输入对应的Key与Type与Value,这样程序在运行时就会通过KVC为你的控件属性进行赋值。(不仅仅是圆角、描边~)\n如下图\n设置圆角、描边的Key为：\nlayer.borderWidth layer.borderColorFromUIColor layer.cornerRadius clipsToBounds 我这次在测试时，\n这样做不用关联出属性，但是需要输入大串字符串，也是不够方便。\n高级 创建UIView的分类，使用IBInspectable+ IB_DESIGNABLE关键字：\n#import \u0026lt;UIKit/UIKit.h\u0026gt; IB_DESIGNABLE @interface UIView (Inspectable) @property(nonatomic,assign) IBInspectable CGFloat cornerRadius; @property(nonatomic,assign) IBInspectable CGFloat borderWidth; @property(nonatomic,assign) IBInspectable UIColor *borderColor; @end #import \u0026#34;UIView+Inspectable.h\u0026#34; @implementation UIView (Inspectable) -(void)setCornerRadius:(CGFloat)cornerRadius{ self.layer.masksToBounds = YES; self.layer.cornerRadius = cornerRadius; } -(void)setBorderColor:(UIColor *)borderColor{ self.layer.borderColor = borderColor.CGColor; } -(void)setBorderWidth:(CGFloat)borderWidth{ self.layer.borderWidth = borderWidth; } - (CGFloat)cornerRadius{ return self.layer.cornerRadius; } - (CGFloat)borderWidth{ return self.layer.borderWidth; } - (UIColor *)borderColor{ return [UIColor colorWithCGColor:self.layer.borderColor]; } @end 附上：GitHub地址\n直接使用 直接将这两个文件拖入项目中即可使用，在右边栏将会显示圆角和描边的属性设置\n如图：\n动态显示设置效果 直接使用的话只有在运行时才能看到效果，\n例如要实时显示一个UIBUtton圆角、描边效果，需要创建一个类继承UIButton\n#import \u0026lt;UIKit/UIKit.h\u0026gt; #import \u0026#34;UIView+Inspectable.h\u0026#34; @interface myButton : UIButton @end #import \u0026#34;myButton.h\u0026#34; @implementation myButton @end 只要将button的Class选择该空白类即可\n关于IBInspectable与IB_DESIGNABLE的使用详情可以参考这篇文章《谈不完美的IBDesignable/IBInspectable可视化效果编程》\n","permalink":"https://ceeyang.com/posts/2016-12-01-%E5%BF%AB%E9%80%9F%E6%B7%BB%E5%8A%A0%E5%9C%86%E8%A7%92%E5%92%8C%E6%8F%8F%E8%BE%B9/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e对于习惯使用Storyboard的人来说，设置圆角、描边是一件比较蛋疼的事，因为苹果没有在xcode的Interface Builder上直接提供修改控件的圆角，边框设置。\u003c/p\u003e\n\u003cp\u003e我们来说说如何对某个控件进行圆角、描边处理：\u003c/p\u003e\n\u003ch1 id=\"初级\"\u003e初级\u003c/h1\u003e\n\u003cp\u003e对于一个初学者来说，如果要进行某个控件的圆角、描边设置，就要从Storyboard关联出属性，然后再对属性进行代码处理。\u003c/p\u003e\n\u003cp\u003e如下代码：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eself.myButton.layer.cornerRadius = 20;\nself.myButton.layer.masksToBounds = YES;\nself.myButton.layer.borderWidth = 2;\nself.myButton.layer.borderColor = [UIColor blackColor].CGColor;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e这样不仅需要Storyboard关联出属性，还要写一堆代码对属性进行设置，不得不说实在麻烦~\u003c/p\u003e\n\u003ch1 id=\"中级\"\u003e中级\u003c/h1\u003e\n\u003cp\u003e更聪明的做法是使用Storyboard提供的Runtime Attributes为控件添加圆角描边。\u003c/p\u003e\n\u003cp\u003e选中控件，然后在Runtime Attributes框中输入对应的\u003ccode\u003eKey\u003c/code\u003e与\u003ccode\u003eType\u003c/code\u003e与\u003ccode\u003eValue\u003c/code\u003e,这样程序在运行时就会通过KVC为你的控件属性进行赋值。(不仅仅是圆角、描边~)\u003c/p\u003e\n\u003cp\u003e如下图\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"http://ww4.sinaimg.cn/large/7853084cgw1fabg89aeqkj207b08j74y.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e设置圆角、描边的Key为：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003elayer.borderWidth\nlayer.borderColorFromUIColor\nlayer.cornerRadius\nclipsToBounds\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e我这次在测试时，\u003c/p\u003e\n\u003cp\u003e这样做不用关联出属性，但是需要输入大串字符串，也是不够方便。\u003c/p\u003e\n\u003ch1 id=\"高级\"\u003e高级\u003c/h1\u003e\n\u003cp\u003e创建UIView的分类，使用\u003ccode\u003eIBInspectable\u003c/code\u003e+ \u003ccode\u003eIB_DESIGNABLE\u003c/code\u003e关键字：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#import \u0026lt;UIKit/UIKit.h\u0026gt;\n\nIB_DESIGNABLE\n\n@interface UIView (Inspectable)\n\n@property(nonatomic,assign) IBInspectable CGFloat cornerRadius;\n@property(nonatomic,assign) IBInspectable CGFloat borderWidth;\n@property(nonatomic,assign) IBInspectable UIColor *borderColor;\n\n@end\n\u003c/code\u003e\u003c/pre\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#import \u0026#34;UIView+Inspectable.h\u0026#34;\n\n@implementation UIView (Inspectable)\n\n-(void)setCornerRadius:(CGFloat)cornerRadius{\n    self.layer.masksToBounds = YES;\n    self.layer.cornerRadius = cornerRadius;\n}\n-(void)setBorderColor:(UIColor *)borderColor{\n    self.layer.borderColor = borderColor.CGColor;\n}\n-(void)setBorderWidth:(CGFloat)borderWidth{\n    self.layer.borderWidth = borderWidth;\n}\n\n- (CGFloat)cornerRadius{\n    return self.layer.cornerRadius;\n}\n- (CGFloat)borderWidth{\n    return self.layer.borderWidth;\n}\n- (UIColor *)borderColor{\n    return [UIColor colorWithCGColor:self.layer.borderColor];\n}\n\n@end\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e附上：\u003ca href=\"https://github.com/qiubaiying/CircularAndStroke.git\"\u003eGitHub地址\u003c/a\u003e\u003c/p\u003e","title":"快速添加圆角和描边"},{"content":"积土成山，非斯须之作。\n个人简历请查看: https://ceeyang.com/Resume\n","permalink":"https://ceeyang.com/about/","summary":"\u003cp\u003e积土成山，非斯须之作。\u003c/p\u003e\n\u003cp\u003e个人简历请查看: \u003ca href=\"https://ceeyang.com/Resume\"\u003ehttps://ceeyang.com/Resume\u003c/a\u003e\u003c/p\u003e","title":"About"}]