可以选择Swiftui和Uikit

乔尔
Apr 27, 2020 · 9最小阅读

By now, you have heard of SwiftUI… this new, declarative, and “exceptionally simple” way of building user interfaces for Apple devices. Ever since its announcement last year, the Apple development community has been buzzing with articles, podcasts, tutorials, and many other resources that all amplify the hype. According to Apple, it has the potential to be “the most powerful UI code you’ve ever written.” So what are we all waiting for? Let’s choose SwiftUI and never look back… but then again, why not choose both?

像许多长期存在的应用程序一样,Okcupid具有少数不同iO188bet金宝搏官网S版本的用户。虽然较旧版本的用户百分比很小,但不可忽视。使用SwiftUI重写用户界面所需的工作时间也不是。还有一些很大的赔率和结局要考虑,例如Swiftui没有等效的事实UICollectionView, a core component that is heavily utilized within the OkCupid app (and many others).

考虑到这些各种考虑,本文探讨了如何明智地整合Swiftui,而不会放弃对iOS 12及以上的支持,以及第一次在Swiftui中学到的经验教训。在Ok188bet金宝搏官网Cupid,我们的希望是通过使用Uikit和Swiftui以及共享的业务逻辑代码选择性地选择某些功能来并排构建。考虑到这一点,我创建了一个名为LoginSample的Xcode项目,以说明登录应用程序的常见用户之旅。随意跟随克隆Github上的项目回购。为简单起见,爵士的身份验证ver is replaced with local validation using regex. Like the OkCupid app, LoginSample supports iOS versions 10 and up.

Goals

该项目的两个主要目标之一是在构建用户界面时(无论是使用Uikit还是SwiftUI构建)时使用共享的抽象。这样的主要例子是loginViewModelwhich provides most of the values and information needed to configure the login view.

另一个主要目标是观点应该看起来几乎identical when they are rendered on screen, as well as behave the same.

跳进去

这first step to switching between SwiftUI and UIKit is creating a view controller that serves as the container for either the SwiftUI or UIKit elements. In this case, it’s thelogincontainerviewController。容器的主要责任是包含一个带有支持登录应用程序的视图的子视图控制器。版本检查用于确定要显示哪个子视图控制器:

这code above is very straightforward. If the user is using iOS 13 and up, auihostingControllercan be used where its root view is the SwiftUILoginView,否则使用由loginviewController。喜欢苹果的文档指出, 这uihostingController是完美的解决方案“当您想将Swiftui视图集成到Uikit视图层次结构中时。”上面的snippit中还包括一个方便的扩展Sundell SwifyonUiviewController这使得添加子视图控制器变得容易。

分享很关心

Slowly integrating SwiftUI side by side with UIKit requires writing一些代码两次。这是不可避免的。用户界面构建,配置和状态使用其各自的框架进行编码。但是,可以共享用于配置的值。通过共享来自一个来源的数据,特别是loginViewModel,只需要在一个地方进行更新,以最大程度地提高效率并最大程度地减少视图之间差异的机会。

In addition to theloginViewModel,有LoginViewDelegate这是由登录协调员并传递到登录视图。另一个共享依赖性是logindatamanager,负责与LoginValidator确定是否创建用户登录对象或返回错误以显示给用户。从每个生命周期的开头可以看到共享依赖性。初始化器LoginViewloginviewControllerare almost identical:

仔细看

loginViewModel是comprised of nested view models each containing the property values used to configure the login view:

在共享视图模型时,它并不完美。不完美的一部分在于尝试同时提供一个来自一个来源的两个框架所需的值。视图模型是真理的单一来源,但某些属性值的应用不同。此外,还有其他值根据框架需要不同类型。当更仔细地看着LoginTextModel

字符串很容易分享,因为细绳是the type used by both frameworks. The font used by each view can be shared but in order to useUifont在Swiftui中,初始化字体使用核心文本字体参考。这是通过投射Uifontas aCTFont, 这identifier of the opaque type used by Core Text.颜色在Swiftui中可以用UIColor。最后,UILabel期望数字然而Text使用LineLimit(_ :)

在分配字体可以利用免费桥接的情况下,其他视图模型中有一些值并不像堆栈视图对齐和内容模式那样直接共享。这些是通过为每个具有一个转换函数和默认实现的每个类型转换为另一种类型的转换函数和默认实现的可转换协议来解决的。这种方法不是理想的,我希望看到它在我们开始并排实施功能时会发展。

值得一提的最后一位分享是一个UI组件,erroralertViewuiview。值得庆幸的是,Swiftui确实提供了一些内置的互操作性。在这种情况下,是UiviewRementable,一个允许整合的包装器uiviewin the SwiftUI view hierarchy.erroralertView可以是返回的特定类型makeuiview(上下文:)as long as it's of type,uiview

如果此项目是一个功能齐全的应用程序,则在所有屏幕上显示给用户显示的错误视图很可能是相同的,而不仅仅是登录视图。通过利用UiviewRementable, 这same error view can be used eliminating duplication of effort for both frameworks.

其他经验教训

由于这是我第一次尝试使用Swiftui建造实质性的东西,因此通常在经过一些反复试验之后,我学到了一些东西。尽管该项目最终实现了前面提到的目标,但存在一些微小的差异。例如,TextFieldin SwiftUI has the option to include placeholder text but you can't specify a color for the text. If you look closely at both renderings of the login view side by side, you're notice the gray color is slightly different. I did come across a work around where you could overlay aTextview but for the sake of this exercise, I decided... meh. Ultimately my lackadaisical attitude towards placeholder text color did not extend to other issues that demanded more research, perseverance, and attention to detail.

向后兼容

As with any new framework, bugs abound (well, maybe not “abound” but I’m a sucker for alliteration). When I first attempted to build and run the登录样本在低于iOS 13的版本上,该应用程序将崩溃。谢谢YichenBman's answer在堆栈溢出上,我要做的就是添加-Weak_framework Swiftui作为价值Other Linker Flags在构建设置中的链接下。问题解决了。

Another step required for backwards compatibility was including the@可用的iOS 13的属性及其在导入swiftui的文件中包含结构:

过渡

In theLoginView,显示并隐藏erroralert视图取决于淋浴布尔物业。淋浴使用@State属性包装器允许修改值。当值修改值时,SwiftUI会破坏并重新创建视图结构而不会失去状态。erroralert有一个自定义修饰符,包括自定义过渡,movetopeDEDENOUTWITHOPACIESthat uses a functionasymmetric(insertion:removal:)指定在插入和删除视图时发生的不同过渡动画。简而言之淋浴true,该视图从顶部滑到屏幕上以及其值为屏幕错误的, 这view slides up off the screen.

When the value of淋浴改变了,这有点像预期的那样起作用,但是该视图在没有过渡的情况下在屏幕上和屏幕上跳下来:

有两个缺少的步骤使其正常工作。第一步是通过将更改包裹到值的值来使用明确的动画淋浴in a call to静止()。通过更改价值淋浴在动画块中,Swiftui知道将动画的任何依赖于它的过渡的视图动画。注意静止()是being usedanytime the value changes otherwise the insertionor去除将行不通。

第二个丢失的步骤是明确设置ZindexLoginView查看层次结构。谢谢Scott Gribben's answer在堆栈溢出上,我学会了嵌套视图何时来到父级视图层次结构中”Zindex不总是[总是]保持不变。”可以使用一个视图插入Zindex并使用另一个Zindex取决于Swiftui如何重新绘制视野。简而言之,过渡正在发生,但您可能看不到它,因为Zindex不一致。因此,您必须明确指定Zindex整体视图层次结构中的观点:

实施这两个缺失的步骤后,错误警报视图的行为如预期:

Et cetera

最后,有两种资源在帮助我学习和在整个终点线上获得登录样品至关重要。首先是保罗·哈德森(Paul Hudson)100天Swiftui,“免费的视频,教程,测试等”,第二个是瓦迪姆·布拉文(Vadim Bulavin文章。

包起来

我真的很喜欢将牙齿沉入Swiftui并编码这个项目。即使Swiftui是声明性且易于阅读的,但仍然花费一些时间来理解和实施。我想自己有些瞬间:“嗯,这呢?你是怎样做的?我将如何获得此视图以垂直与屏幕顶部保持一致?”值得庆幸的是,Apple/Swift社区一直在这里提供帮助。我期待看到框架的发展,尤其是其他开发人员如何与Uikit并肩建立用户界面。如前所述,您可以在GitHub上查看登录项目。谢谢阅读!

最初出版在https://tech.188bet金宝搏官网okcupid.comon April 27, 2020.

188bet金宝搏官网OkCupid技术博客

阅读Okcupid工程团队的故事,每天连接数188bet金宝搏官网百万人

188bet金宝搏官网OkCupid技术博客

OkCupid’s Engineering team is responsible for matching millions of people daily. Read their stories on the OkCupid tech blog

乔尔

写的

188bet金宝搏官网OkCupid技术博客

OkCupid’s Engineering team is responsible for matching millions of people daily. Read their stories on the OkCupid tech blog