\\n</dict>\\n\\n
Tabs({ barPosition: BarPosition.Start })\\n .barAdaptive(true) // 启用自适应布局\\n .platformStyle({ // 平台差异化样式\\n ios: {\\n itemSpacing: 8,\\n selectedColor: \'#007AFF\'\\n },\\n default: {\\n itemSpacing: 12,\\n selectedColor: \'#FF0000\'\\n }\\n })\\n
\\nWeb({\\n src: this.mobil_url,\\n controller: this.controller\\n})\\n.platformComponent({ // 平台原生组件映射\\n ios: (props) => new WKWebView(props)\\n})\\n
\\n@ObservedV2\\nclass ResponseData {\\n @Trace success: boolean = true;\\n @Trace data: Array<ItemData> = [];\\n \\n // 通用反序列化方法\\n static fromJSON(json: any): ResponseData {\\n const instance = new ResponseData();\\n instance.success = json.success;\\n instance.data = json.data.map(ItemData.fromJSON);\\n return instance;\\n }\\n}\\n
\\nnpm install -g @arkui-x/cli\\narkui-x init\\n
\\n# 生成iOS工程\\narkui-x build ios\\n\\n# 运行调试\\narkui-x run ios\\n
\\nconsole.info()
输出跨平台日志现象:iOS平台无法获取数据
\\n解决:
现象:iOS平台显示错位
\\n方案:
Column()\\n .width(\'100%\')\\n .platformAdaptive({ // 平台自适应布局\\n ios: { padding: 8 },\\n default: { padding: 12 }\\n })\\n
\\n处理策略:
\\n// 统一数据格式处理\\nprocessData(data: any): ResponseData {\\n if (data?.hotList) { // 处理不同平台的返回格式\\n return this.transformLegacyFormat(data.hotList);\\n }\\n return ResponseData.fromJSON(data);\\n}\\n
\\n性能优化:
\\nconst cachedData = localStorage.getItem(\'hotData\');\\nif (cachedData) {\\n this.myResponseData = ResponseData.fromJSON(JSON.parse(cachedData));\\n}\\n
\\n体验增强:
\\n多平台扩展:
\\n通过本项目的实践,我们验证了ArkUI-X在跨平台开发中的强大能力。开发者可以复用超过80%的HarmonyOS代码快速实现iOS应用开发,显著降低多平台维护成本。项目已开源至Gitee仓库,欢迎开发者共同参与完善。
\\n未来展望:
\\n通过持续优化,我们将进一步证明\\"一次开发,多端部署\\"理念的可行性,为移动应用开发提供新的范式参考。
","description":"通过ArkUI-X将鸿蒙下的新闻热搜聚合App转换为iOS 一、项目背景与技术选型\\n1.1 项目概述\\n\\n本案例基于鸿蒙(HarmonyOS)开发的聚合热搜热榜应用,通过调用韩小韩博客提供的热搜热榜聚合API,展示了多平台榜单数据并支持网页详情查看。项目采用ArkUI框架开发,现通过ArkUI-X实现iOS平台的无缝迁移。\\n\\n1.2 核心技术栈\\nHarmonyOS:原生开发平台\\nArkUI-X:华为推出的跨平台框架(官方文档)\\niOS:目标运行平台\\n网络请求:基于@kit.NetworkKit的HTTP模块\\n数据绑定:@ObservedV2与…","guid":"https://juejin.cn/post/7520539714647474176","author":"RunkBear","authorUrl":null,"authorAvatar":null,"insertedAt":"2025-06-28T10:47:01.531Z","publishedAt":"2025-06-28T08:09:14.238Z","media":[{"url":"https://images.weserv.nl/?url=p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/c5ec2a0fdbbc4201a4a4b27002ebc126~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgUnVua0JlYXI=:q75.awebp&default=https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/c5ec2a0fdbbc4201a4a4b27002ebc126~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgUnVua0JlYXI=:q75.awebp","type":"photo"},{"url":"https://images.weserv.nl/?url=p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/a92dd60912bb4987a45605886b70eac2~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgUnVua0JlYXI=:q75.awebp&default=https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/a92dd60912bb4987a45605886b70eac2~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgUnVua0JlYXI=:q75.awebp","type":"photo"}],"categories":["iOS","HarmonyOS"],"attachments":null,"extra":null,"language":null,"feeds":{"type":"feed","id":"53331366895638571","url":"rsshub://juejin/category/ios","title":"掘金 iOS","description":"掘金 iOS - Powered by RSSHub","siteUrl":"https://juejin.cn/ios","image":null,"errorMessage":null,"errorAt":null,"ownerUserId":null}},{"feedId":"53331366895638571","id":"161736585165403139","title":"【HarmonyOS next】ArkUI-X休闲益智儿童拼图【进阶】","url":"https://juejin.cn/post/7520510822168510516","content":"\\n\\n💡 代码仓库地址:gitee
\\n
在拼图游戏中,精准的位置计算是灵魂所在。我们通过PanGesture
手势监听实现拖动逻辑:
PanGesture()\\n .onActionUpdate((event: GestureEvent) => {\\n item.currentOffsetX = item.dragStartX + event.offsetX\\n item.currentOffsetY = item.dragStartY + event.offsetY\\n })\\n
\\n这里有两个关键点:
\\ndragStartX/Y
记录拖动起始点event.offsetX/Y
实时获取移动增量当松手时进行位置判定,采用50vp吸附阈值实现自动归位:
\\nconst isSnapped = Math.abs(currentX - targetX) < 50 \\n && Math.abs(currentY - targetY) < 50\\n
\\n为了让儿童更易识别目标位置,我们采用混合模式生成剪影效果:
\\nImage(item.imageResource)\\n .blendMode(BlendMode.DST_IN, BlendApplyType.OFFSCREEN)\\n
\\n这里的组合技解析:
\\n通过window
模块强制横屏显示:
window.getLastWindow().then(win => {\\n win.setPreferredOrientation(Orientation.LANDSCAPE)\\n})\\n
\\n采用百分比+固定值的混合布局策略:
\\nStack()\\n .width(\'100%\')\\n .height(\'100%\')\\n
\\n@ObservedV2
实现细粒度更新Trace
装饰器追踪关键数据变化animateTo({\\n duration: 200\\n}, () => { /* 动画逻辑 */ })\\n
\\n技术维度 | 实现方案 | 跨端收益 |
---|---|---|
手势交互 | PanGesture+坐标计算 | 双端手势行为一致 |
视觉效果 | BlendMode混合模式 | 图形渲染无平台差异 |
状态管理 | @ObservedV2+Trace数据追踪 | 状态同步效率提升30% |
布局系统 | 百分比+固定值混合布局 | 自适应不同屏幕尺寸 |
现象:iOS端出现轻微拖动延迟
\\n解决方案:将动画时长从300ms调整为200ms,并启用硬件加速
现象:华为设备剪影边缘模糊
\\n修复方案:添加离屏渲染参数BlendApplyType.OFFSCREEN
通过这个项目,我们验证了ArkUI-X框架的强大跨端能力。无论是华为的鸿蒙系统,还是iOS平台,都能保持90%以上代码复用率,真正实现了\\"一次开发,多端部署\\"的理想状态。期待ArkUI-X生态的进一步发展,为开发者打开更广阔的跨端开发新天地!
\\n\\n","description":"【HarmonyOS next】ArkUI-X休闲益智儿童拼图【进阶】 一、前言:当拼图遇上跨端开发\\n最近在开发一款跨平台的儿童拼图游戏时,我深刻体会到了ArkUI-X框架的威力——同一套代码竟能同时在华为Mate60 Pro和iPhone15上流畅运行!这不仅节省了开发成本,更重要的是确保了多端用户体验的一致性。今天我们就来聊聊这个项目的核心技术点,特别是拖动坐标计算和图片剪影生成这两个让人\\"又爱又恨\\"的难点。\\n二、开发环境速览\\n操作系统:macOS\\n开发工具:DevEco Studio 5.0.4(Build 5.0.11.100)\\n目标设备:…","guid":"https://juejin.cn/post/7520510822168510516","author":"RunkBear","authorUrl":null,"authorAvatar":null,"insertedAt":"2025-06-28T10:47:01.530Z","publishedAt":"2025-06-28T08:08:27.581Z","media":[{"url":"https://images.weserv.nl/?url=p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/4d13163183cf4bce81f90a6d5c9ba88a~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgUnVua0JlYXI=:q75.awebp&default=https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/4d13163183cf4bce81f90a6d5c9ba88a~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgUnVua0JlYXI=:q75.awebp","type":"photo"},{"url":"https://images.weserv.nl/?url=p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/a3d083a28b9c49dca1839a647bf0e202~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgUnVua0JlYXI=:q75.awebp&default=https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/a3d083a28b9c49dca1839a647bf0e202~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgUnVua0JlYXI=:q75.awebp","type":"photo"}],"categories":["iOS","HarmonyOS"],"attachments":null,"extra":null,"language":null,"feeds":{"type":"feed","id":"53331366895638571","url":"rsshub://juejin/category/ios","title":"掘金 iOS","description":"掘金 iOS - Powered by RSSHub","siteUrl":"https://juejin.cn/ios","image":null,"errorMessage":null,"errorAt":null,"ownerUserId":null}},{"feedId":"53331366895638571","id":"161697596013956096","title":"iOS swift-markdown 自定文字颜色","url":"https://juejin.cn/post/7520551398464208905","content":"🚀 完整代码已开源,欢迎交流:gitee
\\n
最近在做AI的产品,用到了Markdown渲染,其中有一个变态的需求\\n需要对一段文字的某几个字颜色做特殊处理
\\n其实实现思路很简单,一句话说完,就是自定义一个inline语法,然后实现MarkupVisitor
协议的visitInlineAttributes
方法
// https://docs.xiaohongshu.com/doc/35cfb0f7715be75c4e12f67ce3982a0b\\n public mutating func visitInlineAttributes(_ attributes: InlineAttributes) -> NSAttributedString {\\n\\n let result = NSMutableAttributedString()\\n\\n for child in attributes.children {\\n result.append(visit(child))\\n }\\n\\n if attributes.attributes.hasPrefix(\\"Color#\\") {\\n let color = attributes.attributes.components(separatedBy: \\"#\\").last ?? \\"FFFFFF\\"\\n result.addAttribute(.foregroundColor, value: UIColor.argb(\\"#\\\\(color)\\"))\\n }\\n\\n return result\\n }\\n \\n // let markdownText = \\"\\"\\"Opening ^[**这是一段加粗自定义颜色文字**](Color#333333) paragraph, with an ordered list of autumn leaves I found\\"\\"\\"\\n
\\n如果你是非inline的文字,而是一块,可以考虑使用该接口visitBlockDirective
// https://docs.xiaohongshu.com/doc/35cfb0f7715be75c4e12f67ce3982a0b\\n public mutating func visitBlockDirective(_ blockDirective: BlockDirective) -> NSAttributedString {\\n\\n let result = NSMutableAttributedString()\\n\\n for child in blockDirective.children {\\n result.append(visit(child))\\n }\\n\\n if blockDirective.name.hasPrefix(\\"Color#\\") {\\n let color = blockDirective.name.components(separatedBy: \\"#\\").last ?? \\"FFFFFF\\"\\n result.addAttribute(.foregroundColor, value: UIColor.argb(\\"#\\\\(color)\\"))\\n }\\n\\n return result\\n }\\n \\n // let markdownText = \\"\\"\\"Opening @Color#333333 { **这是一段加粗自定义颜色文字** } paragraph, with an ordered list of autumn leaves I found\\"\\"\\"\\n
\\nHow to add custom Markup to the MarkupVisitor in Swift-Markdown?
\\n相信各位似秃非秃小码农们都同意,Swift 是一门现代化、安全且表现力足够丰富的语言。不过,它毕竟还是一种偏静态的语言,灵活性无法和 Python、ruby 之类的动态语言相提并论。
\\n不过话虽如此,通过巧妙的一步步重构源代码,我们也可以用 Swift 完成之前貌似不可能完成的任务,所需的只是那么一丢丢耐心和执着而已。
\\n在本篇博文中,您将学到如下内容:
\\n希望在亲眼目睹本系列文章中 Swift 代码那循序渐进的重构和升华之后,小伙伴们倘若再遇到与此类似的语言设计问题,必能胸有成竹、胜券在握!
\\n无需等待,Let‘s go!!!;)
\\n对于前文中的问题,一种简单粗暴的解决方法是:强行让两种类型“蛮来生作”。
\\nextension AchievementEvaluator {\\n static func queryAll(context: NSManagedObjectContext) throws -> [Evaluator] {\\n let request = Evaluator.fetchRequest() as! NSFetchRequest<Evaluator> // ⚠️ 强制转换\\n return try context.fetch(request)\\n }\\n}\\n
\\n如您所见,我们通过 Swift 强制类型转换语法,将 Evaluator.fetchRequest 实际的类型与 Evaluator 类型强行匹配。
\\n虽然,这可以让编译器暂时闭嘴,但是也同时置我们自己于“刀山火海”之上!
\\n上述代码的风险是:我们需要自行确保类型转换的安全性,若 Evaluator.fetchRequest()
实际返回的请求类型与 Evaluator
不匹配,将立即导致运行时发生崩溃。
除了强行转换以外,我们还可以采用迂回战术:创建约束协议从而绕过编译器的“桎梏”。
\\n首先,新建一个约束协议 Fetchable:
\\n// 定义核心约束协议\\nprotocol Fetchable: NSManagedObject {\\n static func fetchRequest() -> NSFetchRequest<Self>\\n}\\n
\\n接着,对原来的 AchievementEvaluator 协议定义稍作调整,让其关联类型遵守我们上面创建的约束协议:
\\n// 原协议调整\\nprotocol AchievementEvaluator {\\n associatedtype Evaluator: Fetchable & AchievementEvaluator // 新增 Fetchable 约束\\n \\n static func queryAll(context: NSManagedObjectContext) throws -> [Evaluator]\\n}\\n
\\n随后,在 AchievementEvaluator 协议扩展中利用约束关系重新打造我们的 queryAll() 方法:
\\nextension AchievementEvaluator where Evaluator: Fetchable {\\n static func queryAll(context: NSManagedObjectContext) throws -> [Evaluator] {\\n let request = Evaluator.fetchRequest() // ✅ 类型已明确为 NSFetchRequest<Evaluator>\\n return try context.fetch(request)\\n }\\n}\\n
\\n最后,让 Achv_NoBreakVictory 成就实体类遵守 Fetchable 约束协议即可:
\\nextension Achv_NoBreakVictory: Fetchable, AchievementEvaluator {\\n typealias Evaluator = Achv_NoBreakVictory\\n}\\n
\\n虽然这种思路本身没什么问题,但可惜的是编译器还是会义无反顾的再次大声说“我恨你!”:
\\n\\n\\nProtocol \'Fetchable\' requirement \'fetchRequest()\' cannot be satisfied by a non-final class (\'Achv_NoBreakVictory\') because it uses \'Self\' in a non-parameter, non-result type position
\\n
通过上面的错误信息不难发现:大家貌似又回到了之前的“故步自封”—— 我们仍然需要让 Achv_NoBreakVictory 类加上 final 成为“孤家寡人”才能得偿所愿,这是我们不希望看到的。
\\n所以,我们又该如何随遇而安呢?
\\n其实,解决之道并没有想象的那么复杂,我们只需重新设计 Fetchable
协议即可。
我们的核心思想是:
\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n机制 | 作用 |
---|---|
entityName 属性 | 动态获取实体名称,避免依赖自动生成的 fetchRequest() |
手动构建 NSFetchRequest | 通过 NSFetchRequest<Self>(entityName:) 确保类型匹配 |
子类覆盖 entityName | 允许继承体系中的子类指定自己的实体名称 |
首先,通过 实体名称动态构建请求,绕过自动生成的 fetchRequest()
方法的限制:
protocol Fetchable: NSManagedObject {\\n static var entityName: String { get } // 要求实体提供名称\\n}\\n\\nextension Fetchable {\\n static func fetchRequest() -> NSFetchRequest<Self> {\\n // 手动构建请求,确保类型安全\\n return NSFetchRequest<Self>(entityName: entityName)\\n }\\n}\\n
\\n接下来,我们只要让 Achv_NoBreakVictory 类乖巧的提供 entityName 名称即可:
\\nextension Achv_NoBreakVictory: Fetchable, AchievementEvaluator {\\n static var entityName: String {\\n \\"Achv_NoBreakVictory\\"\\n }\\n \\n typealias Evaluator = Achv_NoBreakVictory\\n}\\n
\\n现在,编译源代码将如您所愿,一切都毫无问题,整个世界清净了!
\\n通过 动态实体名称 + 手动构建请求,既能保持类的可继承性,又能满足 Core Data 类型安全要求。其关键点在于:
\\nentityName
属性解耦实体名称与类型推断。entityName
以正确映射数据库实体。然而,我们还可以更进一步。
\\n观察上面 Achv_NoBreakVictory 类中对应 entityName 属性的代码可以发现:每个成就实体类的 entityName 就是它们自己类的名称。既然如此,为什么不把 entityName 也直接放到协议扩展中去呢?
\\nextension AchievementEvaluator where Evaluator: Fetchable {\\n \\n static var entityName: String {\\n \\"\\\\(Self.self)\\"\\n }\\n \\n static func queryAll(context: NSManagedObjectContext) throws -> [Evaluator] {\\n let request = Evaluator.fetchRequest() // ✅ 类型已明确为 NSFetchRequest<Evaluator>\\n return try context.fetch(request)\\n }\\n}\\n
\\n如上代码所示,我们将原本需要每个 AchievementEvaluator 实体类实现的 entityName 属性放到了 AchievementEvaluator 协议扩展中,大大减少了重复代码,这样的 DRY 和 KISS 谁能不爱呢?棒棒哒!
\\n或者我们干脆彻底摆脱 entityName 属性的限制,直接将其嵌入到 Fetchable 协议扩展的 fetchRequest() 方法中,让实现百尺竿头、更入佳境:
\\nextension Fetchable {\\n static func fetchRequest() -> NSFetchRequest<Self> {\\n // 手动构建请求,确保类型安全\\n return NSFetchRequest<Self>(entityName: \\"\\\\(Self.self)\\")\\n }\\n}\\n
\\n至此,我们通过不断迭代重构,彻底摆脱了最初文章开头 CoreData 成就托管类实现的恼人纠缠,小伙伴们还不赶快给自己一个大大的赞吧!❤️
\\n在本篇博文中,我们借助于精心设计的 Fetchable 约束协议成功的摆脱了 Swift 协议扩展中的“磨搅讹绷”,小伙伴们值得拥有!
\\n感谢观赏,再会啦!8-)
","description":"概述 相信各位似秃非秃小码农们都同意,Swift 是一门现代化、安全且表现力足够丰富的语言。不过,它毕竟还是一种偏静态的语言,灵活性无法和 Python、ruby 之类的动态语言相提并论。\\n\\n不过话虽如此,通过巧妙的一步步重构源代码,我们也可以用 Swift 完成之前貌似不可能完成的任务,所需的只是那么一丢丢耐心和执着而已。\\n\\n在本篇博文中,您将学到如下内容:\\n\\n一种很“硬”的解决方案\\n不想回到最初的样子\\n让编译器乖乖听话\\n\\n希望在亲眼目睹本系列文章中 Swift 代码那循序渐进的重构和升华之后,小伙伴们倘若再遇到与此类似的语言设计问题,必能胸有成竹…","guid":"https://juejin.cn/post/7520571945537142822","author":"大熊猫侯佩","authorUrl":null,"authorAvatar":null,"insertedAt":"2025-06-28T08:12:05.782Z","publishedAt":"2025-06-28T06:49:14.053Z","media":[{"url":"https://images.weserv.nl/?url=p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/21fefe01a46140aca0641f7c80c6a2c9~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5aSn54aK54yr5L6v5L2p:q75.awebp&default=https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/21fefe01a46140aca0641f7c80c6a2c9~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5aSn54aK54yr5L6v5L2p:q75.awebp","type":"photo"},{"url":"https://images.weserv.nl/?url=p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/cf2b9f3ba7ca47d09e663720bce248d4~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5aSn54aK54yr5L6v5L2p:q75.awebp&default=https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/cf2b9f3ba7ca47d09e663720bce248d4~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5aSn54aK54yr5L6v5L2p:q75.awebp","type":"photo"},{"url":"https://images.weserv.nl/?url=p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/a92c9d6f28054ecb8664a9c73d53e588~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5aSn54aK54yr5L6v5L2p:q75.awebp&default=https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/a92c9d6f28054ecb8664a9c73d53e588~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5aSn54aK54yr5L6v5L2p:q75.awebp","type":"photo"}],"categories":["iOS","Swift","数据库","Apple"],"attachments":null,"extra":null,"language":null,"feeds":{"type":"feed","id":"53331366895638571","url":"rsshub://juejin/category/ios","title":"掘金 iOS","description":"掘金 iOS - Powered by RSSHub","siteUrl":"https://juejin.cn/ios","image":null,"errorMessage":null,"errorAt":null,"ownerUserId":null}}],"analytics":{"listId":"67809261916145664","subscriptionCount":30}}')