到现在为止,你已经听说过SwiftUI ......这种新的声明,并构建用户界面为苹果设备的“非常简单”的方法。自从去年的公告,苹果开发社区一直嗡嗡与文章,播客,教程和许多其他资源,所有扩增炒作。根据苹果,它有潜力成为“你曾经写过的最强大的UI代码。”那么,我们什么都在等待什么?让我们选择SwiftUI永不回头......但话又说回来,为什么不选择两者兼而有之?

像许多长期存在的应用程序,OkCupid具有跨越不同的IOS188bet金宝搏官网版本的少数用户。虽然用户对旧版本的比例很小,它不是可取消。无论是将需要重写使用SwiftUI用户界面的工作时间。也有一些显著零碎考虑喜欢的事实,SwiftUI不具有同等的UICollectionView,即重金OkCupid应用(和许多其他的)内利用的核心组件。188bet金宝搏官网

考虑到这些不同的考虑,本文将探讨如何整合SwiftUI不理智放弃对iOS的12岁以上的支持,以及来自建设SwiftUI首次教训。在Ok188bet金宝搏官网Cupid我们的希望是开始选择性地使用这两个UIKit中和SwiftUI并排选择某些功能生成端,共享业务逻辑代码。考虑到这一点,我已经创建了一个Xcode项目名为LoginSample说明记录的普通用户的旅程到应用程序。随意通过克隆沿着沿在GitHub上项目的回购协议。为了简单起见,使用regex的本地验证代替了服务器的身份验证。与OkCupid应188bet金宝搏官网用程序一样,LoginSample也支持iOS 10及以上版本。

目标

这个项目的两个主要目标之一就是在构建用户界面时利用共享的抽象,不管它是使用UIKit还是SwiftUI构建的。这方面的主要例子是LoginViewModel它提供了大部分的值和信息来配置登录视图需要的。

另一个主要目标是,意见应看当它们被呈现在屏幕上,以及具有相同的行为是相同的。

login-sample-screenshots

跳跃

第一步SwiftUI和UIKit之间的切换创建充当用于任一SwiftUI或UIKit的元素的容器的视图控制器。在这种情况下,它的LoginContainerViewController。容器的主要职责是包含与支持登录到应用程序的视图子视图控制器。一个版本检查用于其子视图控制器决定显示:

// LoginContainerViewController.swift倍率FUNC viewDidLoad中(){super.viewDidLoad()addChildViewController()}私人FUNC addChildViewController(){如果#available(iOS的13,*){让loginView = LoginView(委托:代表)加(UIHostingController (rootView:loginView),帧:view.frame)}否则{加载(LoginViewController(委托:委托),帧:view.frame)}}

上面的代码是非常简单的。如果用户使用的是iOS 13时,一UIHostingController可用于其中其根视图是SwiftUILoginView,否则使用由受控的视图的UIKitLoginViewController。喜欢苹果公司的文档状态中,UIHostingController是完美的解决方案“当你想SwiftUI意见整合成一个UIKit的视图层次。”也包括在上面的这段是从方便的扩展Swify通过Sundellui这可以很容易地添加子视图控制器。

共享是关怀

通过边慢慢地整合SwiftUI侧的UIKit需要写作一些代码两次。这是不可避免的。用户界面构造、配置和状态使用各自的框架进行编码。但是,用于配置的值可以共享。通过共享来自一个数据源的数据,特别是LoginViewModel,更新只需要在一个地方进行,这样可以最大限度地提高效率和减少视图之间的差异。

除了LoginViewModel,这是LoginViewDelegate这被LoginCoordinator并传递到登录视图。另一个共享依赖项是LoginDataManager,负责与通信对象LoginValidator确定是否创建UserLogin对象或返回要显示给用户的错误。共享依赖关系可以从每个生命周期的开始看到。属性的初始化器LoginViewLoginViewController几乎是相同的:

// LoginView.swift(SwiftUI)INIT(视图模型:LoginViewModel = LoginViewModelFactory.create(),DATAMANAGER:LoginDataManager =的.init(),委托:?LoginViewDelegate){self.viewModel =视图模型self.dataManager = DATAMANAGER self.delegate =委托} // LoginViewController.swift(UIKit中)INIT(视图模型:LoginViewModel = LoginViewModelFactory.create(),DATAMANAGER:LoginDataManager =的.init(),委托:?LoginViewDelegate){self.viewModel =视图模型self.dataManager = DATAMANAGER self.delegate =委托super.init(nibName:无,束:无)}

再仔细一看

LoginViewModel由各自含有用于配置登录视图的属性值嵌套视图模型的:

struct LoginViewModel{让写成backgroundColor:用户界面颜色让buttonModel: LoginButtonModel让contentStackModel: LoginStackModel让emailTextEntryModel: LoginTextEntryViewModel让formStackModel: LoginStackModel让imageModel: LoginImageModel让passwordTextEntryModel: LoginTextEntryViewModel让titleModel: LoginTextModel}

虽然视图模型是共享的,但它并不完美。部分不完美之处在于试图从一个来源同时提供两个框架所需的价值。视图模型是唯一的真实来源,但是一些属性值的应用是不同的。此外,根据框架的不同,还有其他需要不同类型的值。这可以在更仔细地观察时看到LoginTextModel

// LoginTextModel.swift结构LoginTextModel {让字体:UIFont让numberOfLines的:int让文本:字符串让文字颜色:的UIColor} // LoginViewControllerConfigurator.swift(UIKit的)controller.titleLabel.font = viewModel.titleModel.font controller.titleLabel.numberOfLines =viewModel.titleModel.numberOfLines controller.titleLabel.text = viewModel.titleModel.text controller.titleLabel.textColor = viewModel.titleModel.textColor // LoginText.swift(SwiftUI)文本(viewModel.text).font(字体(viewModel.font如CTFont)).lineLimit(viewModel.numberOfLines).foregroundColor(颜色(viewModel.textColor))

字符串很容易共享,因为是由两个框架所使用的类型。由每个视图中使用的字体可以被共享,但为了使用UIFont在SwiftUI,初始化字形与核心文本字体的参考是必需的。这是通过转换a来实现的UIFont作为一个CTFont,由核心文本所使用的不透明类型的标识符。颜色在SwiftUI可以初始化与a的UIColor。最后,的UILabel预计numberOfLines文本使用一个lineLimit (_)

虽然分配的字体可以利用免费桥接的,但是也有一些不能直接共享像堆栈视图对齐和内容模式在其它视图模型几个值。这些都是通过为每个有一个转换功能和默认实现将一种类型转换到另一种转换协议解决。这种方法是不理想的,我希望看到它演变,我们开始通过边实施边功能。

最后值得一提的是一个UI组件,TheErrorAlertView从哪一个子类的UIView。值得庆幸的是,SwiftUI确实提供了一些内置的互操作性。在这种情况下,它的UIViewRepresentable,包装,允许整合的UIView在SwiftUI视图层次。ErrorAlertView可以返回的具体类型makeUIView(背景:)只要它的类型,的UIView

函数updateUIView(_ uiView: ErrorAlertView, context: context) {// not in use}

如果这个项目是一个功能完整的应用程序,那么很可能显示给用户的错误视图在所有屏幕上都是相同的,而不仅仅是登录视图。利用UIViewRepresentable中,相同的错误视图可用于消除的努力为两个框架重复。

其他的经验教训

由于这是我第一次尝试建立一些实质性的使用SwiftUI,我学到了一些东西,一般经过一些试验和错误。虽然该项目最终完成前面提到的目标,也有一些细微的差别。例如,文本域在SwiftUI有权选择包括占位符文本,但你不能指定文字的颜色。如果你仔细在登录视点侧的两个渲染由侧面看,你是通知灰色略有不同。我没有碰到过一个解决办法,你可以覆盖一个文本查看但对于这项工作的缘故,我决定......咩。最后我对占位符文本颜色无精打采的态度并没有延伸到要求进行更多的研究,毅力,以及对细节的关注等问题。

向后兼容

正如任何新的框架,漏洞比比皆是(好吧,也许不是“比比皆是”,但我的头韵吸盘)。当我第一次尝试构建和运行LoginSample版本上项目低于iOS的13,应用程序会崩溃。谢谢YichenBman的答案在堆栈溢出时,我所需要做的就是添加-weak_framework SwiftUI作为价值其它链接器标记在“生成设置”中的“链接”下。问题解决了。

为了向后兼容需要另一个步骤包括@available属性,适用于ios13,最多可将structs包含在文件中导入SwiftUI:

LoginView: View{…}

转变

在里面LoginView,显示和隐藏ErrorAlert视图依赖于showError布尔属性。showError使用@州属性包装以允许值进行修改。当该值被修改,SwiftUI破坏并重新创建视图结构,而不会失去跟踪状态的。ErrorAlert有一个自定义的修改,其中包括一个定制的过渡,moveTopEdgeInOutWithOpacity使用功能非对称(插入:去除:)指定插入和移除的图时发生不同的过渡的动画。简单来说,当showError真正时,视图从顶部滑入到屏幕上,当其值为中,视图向上滑动关闭屏幕。

// // // // // // // //移动(edge: .top) .合并(with: .不透明度)让移除=任意过渡。// LoginView.swift @State var showError: Bool = false var body: some View{…如果自我。showError{//使用过渡显示或隐藏错误警告视图:// ' movetopedgeinoutwith不透明度'}…}

当值showError改变,这个工作有所预期,但鉴于上下车没有过渡屏幕跃升:

login-sample-transition-not-working

有两名失踪的步骤,使其工作。第一步是通过包装变化的值使用显式动画showError在一个呼叫withAnimation ()。通过改变的值showError在动画块中,SwiftUI知道如何对依赖于它的过渡视图进行动画处理。请注意,withAnimation ()正在使用任何时间的值,否则改变插入要么移除是行不通的。

// LoginView.swift FUNC buttonTapped(){... withAnimation {self.showError =真} ...} FUNC textEntryTextFieldTapped(){... withAnimation {self.showError = FALSE} ...}

缺少的第二个步骤是显式设置用zIndexLoginView视图层次结构。谢谢斯科特·格里本的答案在stack overflow上,我了解到当嵌套视图在父视图层次结构中来来去去时,“their”用zIndex不[总是]保持不变。”该视图可以利用一个被插入用zIndex并使用另一个删除用zIndex根据SwiftUI如何重绘视图。简单来说,转型正在发生的事情,但你可能看不到它,因为用zIndex是不相符的。因此,你必须明确指定用zIndex在整个视图层次结构中的视图:

var body: some View{…ZStack(…){颜色(…).zIndex (0) LoginVStack(…){…} . zindex (1) if self。showError{ ErrorAlert(...) ... .zIndex(2) } } ... }

在实现了这两个缺失的步骤之后,错误警报视图的行为如预期的那样:

登录样本 - 过渡性工作

等等

最后,有迹象表明在帮助我学习,并得到冲过终点线的LoginSample项目的关键两个资源。首先是保罗·哈德逊100天的斯威夫特和第二“的视频,教程,测试,和更多免费回收”是瓦迪姆Bulavin的键盘为避免次SwiftUI篇文章。

结束

我真的很喜欢我的那颗牙齿成SwiftUI和编码这个项目。尽管SwiftUI是声明,易于阅读,它仍然需要时间来理解和执行。有我心想时刻,“好了,你看这个?你怎么做到的?怎么会我得到这个以垂直对齐屏幕的顶部?”值得庆幸的是苹果/斯威夫特共同体(这是)总是在那里帮助。我期待着看到的框架演变,特别是如何其他开发方法,通过侧UIKit中构建用户界面的一面。正如前面提到的,你可以检查出在GitHub上LoginSample项目。谢谢阅读!