当第一款基于UIKit的应用出现在macOS Mojave上时,很明显,要使这项技术适合开发新的Mac应用,还有很长的路要走。对于macOS Catalina,最初的这半步是由开发者完成的#39;它花了相当大的努力才使它看起来和感觉上都适合Mac。然而,当我们通过macOS 11和';Mac习语';,UIKit已经跨过了一个门槛,这使得它成为构建类似Mac的Mac应用程序的绝佳方式。大多数用户很难判断这些消息、播客或地图应用程序不是健壮的、原生的Mac应用程序,尽管这种质量水平在苹果公司中的分布并不均匀';在其他基于UIKit的Catalyst应用程序中,它为你提供了一个很好的基准,如果你付出努力,你可以通过Mac Catalyst实现什么。
然而,这篇文章的目的是探索不';不要按原样工作;现在,我已经使用Mac Catalyst在广播和粉彩中构建了两个成功的、高度评价的中等复杂度Mac应用程序,并在过去一年左右为开发人员构建了示例代码,我想花一些时间来展示我';我只找到了唐';我们还没有做足够多的工作来实现伟大的Mac应用程序,或许还可以为苹果提供一份清单,供其在未来版本的操作系统中解决。这是我策划的清单,肯定会有我没有做的事情';不要触及下面,因为他们已经';它直接影响了我';我正在或希望建造。向前
macOS上的UIKit最大的漏洞是它对基于文档的应用程序的处理。原因有很多,但让我们';让我们从基础开始:Xcode';s';文件应用程序';当您添加Mac Catalyst支持时,iOS模板在启动时崩溃,因为它缺少打开文件的基本权限。即使在解决了这个问题,并在其信息列表中添加了必要的文档类型之后,实际上尝试打开一个文件将使文档浏览器进入一个显示文件选择器的无休止循环。单凭这一点就可能导致潜在的基于文档的应用程序的即时死亡,因为即使是有经验的开发人员也会在试图修补内置模板以按预期运行时突然不知所措。
假设你';我已经越过了这一点,或者在iPhone或iPad上已经有了一个构建良好的文档应用程序,你';我们刚刚开始';Mac和#39;支持你';我会很快发现,你必须定义和特殊情况下的行为,只是不';不要在macOS上做正确的事情,因为UIDocumentBrowserViewController是布局的基础。然后你会发现';近期开放';菜单就是不';默认情况下不工作,而是显示一个保存面板。或者在启动应用程序时,但在选择文档之前,会显示一个空白窗口,因为UIDocumentBrowserViewController需要嵌入到现有窗口中,并且可以';我不能展示一个独立于此的采摘者。
这个Xcode模板不是';甚至没有更新窗口场景,所以即使它真的工作了,它也不会';不支持多个文档、窗口标签或任何你可能期望在macOS上使用基于文档的应用程序。
UIKit和#39;s document browser设置为窗口的根视图控制器,但它不是';在一个文件采集器独立于应用程序窗口,并且在文件打开之前根本不应该有窗口的世界里,这是不合适的。如果你真的设法克服了这一切,走出了另一边,你';ll很可能已经取代了所有的UIKit';它的文档选择器处理与您手动触发的特定于平台的开放面板,甚至可能完全重写UIKit';它的文件系统本身。
没有一款苹果应用程序会对Catalyst的这一方面进行跟踪,而且它显示了这一点。如果苹果内部在Catalyst中创建一个多窗口文档编辑器应用程序,比如TextEdit,然后一步一步地修复他们遇到的所有问题,使其功能与AppKit版本相同,那么对我们所有人来说都将大有裨益。不幸的是,斯威夫特游乐场没有';不要碰这些东西,因为它们是筒仓式的;基于Xcode的编辑器,因此它不会';它似乎无法推动这些改进。
Catalyst为macOS带来了对iOS设置捆绑包的支持,苹果甚至已经开始在其自己的非Catalyst应用程序(如iOS模拟器)中使用macOS,但与iOS一样,该系统的限制和静态性令人难以置信——这就是为什么几乎每个应用程序都在iOS上滚动自己的设置窗口。用户帐户注册、应用内购买以及主题或替代图标集等都需要一种设置UI,而这种UI不是';使用设置包是不可能的。
如果深入研究,您会发现Catalyst提供了使用UIKit窗口场景API构建设置窗口所需的所有工具。我有一些示例代码可以演示这一点。然而,仍有缺失的部分:
您无法控制Catalyst中的窗口按钮(无需连接到AppKit),因此您可以';t表示窗口不可调整大小以禁用绿色交通灯
UIKit窗口都使用状态恢复,这意味着如果在“设置”窗口打开时退出应用程序,它将在下次启动时再次弹出。没有Mac应用能做到这一点
在大多数Mac应用程序的首选窗口中,都可以找到类似NSTabView的软件。虽然不重要,但它在概念上很重要;语境相关
总的来说,如果你使用AppKit Bridgeting来修补粗糙的边缘,你可以获得90%的成功。然而,如果所有这些东西都能开箱即用,那就太好了。
我的应用程序中排名第一的用户请求,以及排名第一的主题I';在过去的两年里,开发者一直在问我,这一切都是关于把UI放在菜单栏上。每个人,但苹果似乎都希望应用程序支持带有指示器、菜单和用户界面的菜单栏状态区域。在很多情况下,来到Mac的iPhone开发者都有一个应用程序,可以在全球范围内固定在菜单栏上,但作为一个漂浮在桌面上的窗口,它必须由用户管理,这是毫无意义的。
目前,UIKit中根本没有能够满足这一需求的概念。我为Pastel想出了自己的方法,这是作为示例代码提供的,但它';很明显,需要有一些官方认可的方式将UI放入菜单栏。是否为';如果有一个类似于我的新的popover演示模式,或者一个定制的菜单栏额外的应用扩展,它真的需要成为开发者工具箱中的一个选项。因为没有苹果';中国自己的应用程序可以做到这一点,而且它';It’这条路已经够远了,我不知道';我看不到他们在没有开发人员大量反馈的情况下添加这个。它';当他们的应用程序无法运行时,开发者会受到负面评论的打击;我不支持Mac用户体验的这一主要功能,苹果公司对此一无所知。
虽然我很欣赏添加到UICollectionView以执行常规表视图功能的新配置API,但不幸的是,默认情况下它们没有';t为您提供在macOS上正常工作的表视图。你';只要尝试匹配AppKit NSTableView的基本行为(包括选择和非活动状态),就需要数百行代码,在输入select或更深奥的AppKit键盘快捷键时,完全由您自己完成。除此之外,UICollectionView总体上并不';我不具备理解点击、双击、触摸、键盘触发或触笔触摸之间区别的技巧——它们';我们都只是';选择';。macOS,也可以说是iPadOS,需要能够定制鼠标的选择/驱动行为,这样你就可以做一些事情,比如单击选择某个东西,双击打开/触发/驱动它,但不干扰基于触摸的选择。
类似地,点击列表或集合视图应该可以选择键盘焦点,就像大多数Mac应用一样。当你在Finder中选择一个文件时,你希望键盘跟随你,这样你就可以使用箭头键导航等,而不必先点击tab键将焦点移动到图标视图;默认情况下,UICollectionView没有';在Mac Catalyst中不能像那样工作,这会影响表视图样式列表和网格。UICollectionView确实有一个_shouldBecomeFocusedOnSelection属性,它不是';它没有向开发人员公开,但解决了macOS上一大堆挥之不去的行为问题,我将其归类为必备的公共API。
当然,房间里的大象是多列表视图,这是许多桌面应用程序的一个重要核心部分,也是UIKit完全忽略的主题,因此基于Catalyst的Mac应用程序完全无法使用。我真的希望在将来的某个时候能看到这个API,但现在它';这更像是一个愿望清单。
苹果确实决定给UIKit开发者一个通往AppKit的桥梁是NSToolbar,这样他们就可以直接控制自己的应用程序';工具栏的行为。不幸的是,只有NSToolbar相关功能的一个子集被桥接,这意味着你';如果你需要像搜索字段这样的东西,或者像Safari风格的URL字段、卷滑块这样的自定义绘制视图,或者需要手动指定固定大小的地方,你必须开始使用自己的AppKit桥接器。尽管像Maps这样的苹果应用程序可以做到这一点,但如果没有桥接,从工具栏项目中呈现弹出窗口也是不可能的。
在广播节目中,我选择了另一个方向,使用UIKit来';假';工具栏,类似于播客中使用的工具栏。在有搜索字段的Pastel中,我必须使用AppKit桥来处理该功能。在我简化的浏览器示例代码Flare中,我甚至对AppKit类进行了子类化,以绘制带有favicon区域的自定义地址栏。它';作为开发人员,所有这些途径都对我们开放,这很好,但是我希望能够在我的工具栏区域中使用UIKit视图,并让它们按照预期工作,而不需要任何桥接和粘合代码。在苹果公司';他们已经避开了NSToolbar,转而使用UIKit来伪装自己的应用,比如消息和播客,但这有两个方面让开发人员的事情变得复杂:
无法使用公共API更改标题栏高度,以便根据自定义工具栏高度正确定位窗口控件
苹果依赖于一种非公开的UITitlebar标题可归属模式(of';transparent';),这样,该区域中的鼠标操作就可以正确地传递给下面的UIKit控件
如果没有这两件事,你可以在自己的应用程序中尝试,如果你';你有足够的勇气去冒应用程序审查的风险,用UIKit代替NSToolbar只是不';对于大多数开发人员来说,这是不可行的,这使得我们只能使用部分NSToolbar API子集。
窗口行为的许多方面都是';t现在通过UIKit公开,比如禁用窗口按钮或全屏模式、更改标题栏透明度、修改标题栏/工具栏区域大小,甚至在屏幕上设置窗口框架/位置,或在退出/重新启动时保存/恢复。这样做的副作用是你';我会找到应用程序窗口,比如偏好面板,让你在不应该的时候全屏显示它们';t、 或者不可调整大小的窗口仍有活动的缩放按钮,或者窗口在恢复时忘记了大小。作为一名开发人员,无法访问NSWindow的所有这些方面是一个严重的限制,并会导致更糟糕的用户体验。
我的观点是,Catalyst开发人员应该有办法控制他们呈现给用户的UI的每个方面——以及窗口&;Windows chrome是该应用程序最重要的部分之一。Catalyst的工作方式是,每个UIWindowScene都由一个NSWindow子类包装,该子类处理一系列隐式行为,但控制其表示的桥梁过于简单。
Inspector panels是AppKit桥接的一个方面,最初被非正式地吹捧为Catalyst开发人员的一个选项。然而,在这样一个面板窗口中根本无法获取基于UIKit的内容。即使你要带着swizzling进城,基本的UIWindowScene类也总是映射到NSWindow子类,这使得所有基于NSPanel的功能都被锁定。上面提到的窗口场景的所有其他问题也使得这项任务很难伪造。macOS中的面板可以在全屏上漂浮在其他窗口之上,be';非激活';因此,你可以在不失去主窗口焦点的情况下与他们互动,并将自己隐藏在曝光和任务控制之外。无法设置、保存或恢复帧也会有问题。
当然,您可以构建和维护一个单独的基于AppKit的视图层次结构来为inspector面板提供动力,但这会给大多数开发人员带来太多问题,而来回连接到代码库的UIKit部分是一种折磨。
我认为解决这个问题的最简单方法是提供某种模式表示模式,让UIKit应用程序产生一个视图控制器作为检查器面板,就像今天的Popover一样。不管怎么说,只要他们的行为像审查小组一样,他们的大多数行为都可以是隐性的。
(旁白:如果你在这一节结束时脑子里没有一首80年代的主题曲,我真羡慕你)
在AppKit中,当开始在某些视图中拖动窗口时,可以允许拖动窗口,也可以禁止在其他视图中拖动窗口。例如,在QuickTime中打开一个视频,您';我会发现你可以从播放区域的任何地方拖动窗口。视频播放是一个很好的窗口类型示例,您可能希望在其中进行这样的交互。Catalyst确实具有私有API(UIView';s#U ScenedRaggingBehaviorNPAN标志)这样的功能,我认为它可能是向开发人员公开的一个很好的候选者。
macOS上有两种形式的催化剂——iPad习惯用法和Mac习惯用法。在iPad的习惯用法中,你的所有指标都与iOS共享,最终结果在Mac电脑上以77%的比例呈现,导致文本模糊,在非视网膜显示器上输出低于标准。Mac习语仅适用于macOS 11及更高版本,在这种习语中,你可以获得1:1的像素缩放和一种用户界面体验,这种体验在很大程度上依赖于AppKit,比如本机按钮和其他控件。然而,除了对Interface Builder和SwiftUI中的填充进行最基本的更改之外,您的布局还需要大量的工作才能在两种模式下运行,而不需要一百万个ifdef,而您';我们真的被留在这里晾干了。
因为苹果的所有指标';s平台表示为点或像素,而不是真实世界的物理尺寸(如英寸或毫米),没有系统提供的解决方案来处理这样的多个UI比例。一个数字只是一个数字;它可以';每个平台都不一样。
我在所有Mac Catalyst项目中做的第一件事就是放入一个小包装,我称之为';UIFloat';,这对我来说是基于应用程序是否在Mac习惯用法下运行的扩展。然后,在我的项目中,我手动指定布局点、矩形、层角半径或字体大小的任何地方,我都会将其组件包装在UIFloat()中。因此,我的项目中没有原始数字,而是使用UIFloat。我马上就知道是不是';这是一个面向用户界面的数字,如果我忘了为macOS更改它,我也可以立即知道。这是一个神奇的解决方案,使复杂的用户界面如粉彩';iPad和Mac之间的维护非常简单,所以我不';我再也不用去想它了。没有这一点将严重阻碍Mac习语的采用,而我';我敢肯定,很多开发者第一次踏上了这条征程,他们只能接受iPad的习惯用法、模糊的应用程序以及类似iOS的按钮和控件。它';这也是SwiftUI的一个很好的解决方案,因为越来越多的布局从界面生成器转移到了代码。
这并不是Mac Catalyst独有的问题,但它现在已经成为为Mac构建应用程序的问题:
将这两个问题结合起来,你最终会得到一些应用,如果它们真的要支持macOS 11,那么它们的测试选项就非常有限。通过广播,我的TestFlight tester列表拥有数千名用户,我可以确保他们都能尝试与应用商店构建完全匹配的应用程序版本。其中没有一个能够在macOS 11上进行测试,因为苹果做出了一个奇怪的决定,将TestFlight与macOS 12紧密联系起来。由于我的开发机器在Apple Silicon上,因此无法在虚拟机上运行12.0之前的任何版本的macOS,所以我唯一拥有的macOS 11测试设备是一台10年前的MacBook Pro,它必须被破解(!)运行操作系统。由于macOS上的升级周期要长得多,用户(和开发者)可以将操作系统升级延迟数月甚至数年,没有哪位Mac开发者能够简单地瞄准最新版本的macOS,并削减与仅仅一年前的产品的兼容性。TestFlight没有任何理由以这种方式受到限制,对于Mac Catalyst和SwiftUI等每年都经历如此多变化的框架,您可以';不要指望你今天写的东西在没有严格测试的旧版本macOS上运行;调试。这些问题当然会随着时间的推移而消失,但你';在放弃对macOS 11的支持成为无争议之前,我们仍在谈论多年。
我个人认为';很久以前我们有一个';macOS模拟器';与iOS模拟器一样,您可以启动任何给定版本的macOS,并直接从Xcode运行应用程序。它应该同样容易构建&;在Mac电脑和iOS设备之间运行应用程序;随着越来越多的iOS开发者开始为macOS开发,开发者体验中这些过时的部分确实突显了他们的年龄以及他们与现代应用开发的脱节。
Mac Catalyst的位置很好;自推出以来,它每年都有很大的改进,对于大多数开发者来说,它是构建类似Mac的通用应用程序的最佳方式,可以在iPhone、iPad和Mac上运行。它的混合特性允许开发人员选择UIKit、SwiftUI和AppKit的哪些元素来实现他们想要的体验';我们正在寻找,或者将它们结合起来,以实现两个世界的最佳效果。它显然在苹果内部有很大的吸引力#39;s的产品团队,因为它';s成为信息、地图、播客、查找我的、游乐场、书籍、语音备忘录、股票、家庭和新闻的支持技术。搭配SwiftUI,它';无论是好是坏,它都会迅速成为应用商店中新Mac应用程序的实际标准——这就更需要优先考虑剩余的粗糙边缘。剩下的每一个障碍都只是给了开发者一个理由,让他们不去尝试额外的一英里,这取决于他们知道并且有效的iOS行为。
如果你';我之所以与Mac Catalyst保持距离,是因为第一方或第三方应用程序的负面体验,或者只是点击了';Mac和#39;在Xcode中选中复选框,但从未真正深入了解什么';还有可能,你';我们真的错过了。三年前,当这些讨论第一次出现时,这主要是理论上的,但现在有了三个主要项目、四个次要项目、十几个原型和一大堆示例代码,我可以有把握地说,我预计Mac Catalyst将为我的下一个十年在App Store上以及以后的发展提供动力。如果我必须构建&;在AppKit或SwiftUI中维护定制的Mac版本,我当然不会';我们无法在一个又一个版本中保持它们的最新特性。