到目前为止,您已经听说过Swiftui ......这种新的,声明性和“异常简单的”方式来构建Apple设备的用户界面。自去年宣布以来,苹果开发区一直在嗡嗡作响,播客,教程以及所有放大炒作的其他资源。根据苹果,它有可能是“你写的最强大的UI代码”。那么我们都在等待什么?让我们选择Swifui,永远不要回头......但是那么,为什么不选择两者?

像许多长期的应用程序一样,Okcupid拥有少数不同IOS版188bet金宝搏官网本的用户。虽然旧版本上的用户百分比很小,但它不可擦除。既不是使用SWIFTUI重写用户界面所需的工作时间。也有一些重要的赔率和目的是考虑的,就像Swifui没有相同的事实一样UICollectionView.,在OKCupID应用程序(以及许多其他人中)大量使用的核心组件。188bet金宝搏官网

凭借这些各种思想,本文探讨了如何明确地整合Swifui,而不会为iOS 12及以上的支持,以及第一次从Swifui的建设中学到的经验教训。在Ok188bet金宝搏官网cupid,我们希望首先使用uikit和swifui来选择性地选择某些功能,并使用uikit和swifui,共享业务逻辑代码。考虑到这一点,我创建了一个名为loginsample的Xcode项目,以说明登录应用程序的常见用户旅程。通过克隆,随之而来GitHub上的项目回购。为了使事物简单,使用Regex替换使用服务器的验证验证。像Okcupid应188bet金宝搏官网用程序一样,LogInsample支持iOS版本10和Up。

目标

该项目的两个主要目标之一是在构建用户界面时使用共享抽象,无论是使用uikit或swiftui构建。这的主要例子是LoginViewModel.它提供配置登录视图所需的大多数值和信息。

另一个主要目标是观点应该看几乎当它们在屏幕上呈现时相同,并且表现相同。

登录样本 - 屏幕截图

跳入

Swifui和Uikit之间切换的第一步是创建一个视图控制器,该控制器用作SWIFTUI或UIKIT元素的容器。在这种情况下,它是logincontainerviewController.。容器的主要责任是包含一个子视图控制器,其中包含支持登录应用程序的视图。版本检查用于确定要显示的子视图控制器:

// logincontainerviewcontroller.swift override func viewdidload(){super.viewDidload()addchildViewController()}私有Func addChildViewController(){如果#available(ios 13,*){let loginview = loginview(委托:委托)添加(UihostingController (rootview:loginVIEW),帧:View.frame)} else {add(loginviewcontroller(委托:委托),frame:View.frame)}}}}

上面的代码非常简单。如果用户使用iOS 13和UP,则UihostingController.可以使用它的根视图是SWIFTUI的位置登录概述,否则使用由控制的uikit视图LoginViewController.。喜欢Apple的文件状态, 这UihostingController.是完美的解决方案“当您想要将Swifui视图集成到Uikit视图层次结构中。”上述Snippit中也包含在方便的延期中Sundell Swify.UIViewController.这使得易于添加子视图控制器。

分享是关怀

用uikit慢慢地将swiftie与ukit一起融合一些代码两次。这是不可避免的。用户界面构造,配置和状态使用其各自的框架进行编码。但是,可以共享用于配置的值。通过从一个来源共享数据,特别是LoginViewModel.,只需要在一个地方进行更新,这最大化效率,并最大限度地减少视图之间差异的机会。

除了LoginViewModel.,有loginViewDelegate.这是由此采用的Logincoordinator.并传递到登录视图。其他共享依赖性是Logindatamanager.,负责与之通信的对象loginvalidator.确定是否创建一个用户登录对象或返回要显示给用户的错误。可以从每个生命周期的开头看到共享依赖项。初始化者登录概述LoginViewController.几乎相同:

// loginview.swift(swiftui)init(ViewModel:LoginViewModel = LoginViewModelfactory.Create(),DataManager:Logindatamanager = .init(),委托:logialViewDelegate?){self.viewModel = ViewModel Self.Datamanager = DataManager Self.Delegate =委托} // loginViewController.swift(uikit)init(ViewModel:LoginViewModel = LoginviewModelfactory.Create(),DataManager:Logindatamanager = .init(),委托:loginViewDelegate?){self.viewModel = ViewModel Self.Datamanager = DataManager Self.Delegate =委托Super.Init(nibname:nil,捆绑:nil)}

仔细看看

LoginViewModel.由嵌套视图模型组成,每个模型包含用于配置登录视图的属性值:

struct loginviewmodel {let backgroundcolor:uicolor let buttonmodel:loginstackmodel let compentstackmodel:logintextentryviewmodel:logintextentryviewmodel let formstackmodel:loginstackmodel let imagemodel:logimagemodel let passwordtextrymodel:logintextentryviewmodel let titlemodel:logintextmodel}

虽然查看模型是共享的,但它并不完美。部分缺陷的一部分在于尝试同时为来自一个来源的两个框架提供所需的值。视图模型是单一的真理来源,但某些属性值以不同方式应用。此外,还有其他值需要不同类型的值,具体取决于框架。在仔细观察时可以看到这一点logintextmodel.

// logintextmodel.swift struct logintextmodel {let字体:uifont let numberoflines:int let text:string let textcolor:uicolor} // loginviewontrollerconfigurator.swift(ukit)controll.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)text(viewmodel.text).font(viewmodel.font asCtfont).LineLimit(ViewModel.numberoflines).ForeColdColor(Color(ViewModel.TextColor))

字符串很容易分享,因为细绳两个框架使用的类型。每个视图使用的字体可以共享,但要使用Uifont.在Swifui,初始化字体使用核心文本字体引用是必需的。这是通过铸造一个来实现的Uifont.作为一个ctfont.,核心文本使用的不透明类型的标识符。颜色在Swifui中可以用一个初始化Uicolor.。最后,AUilabel.预计NumberFlines.然而文本用A.linelimit(_ :)

分配字体可以利用无线电话桥接,在其他视图模型中存在一些值,这些模型并不直接分享堆栈视图对齐和内容模式。通过为每个转换功能和默认实现为另一个转换为另一个类型的可转换功能来解决这些协议来解决这些协议。这种方法并不理想,我希望看到它随着我们开始实施的特色并排而发展。

值得一提的最后一点共享是UI组件,ErrorAlertview.从中归属UIView.。谢天谢地,Swifui确实提供了一些内置互操作性。在这种情况下,它是UIViewRepresentable.,允许集成A的包装器UIView.在Swifui视图层次结构中。ErrorAlertview.可以是返回的特定类型makeuiview(上下文:)只要它是类型,UIView.

struct errorlert:uiviewrepresentable {令留言:字符串宽度:cgfloat func makeuiview(上下文:context) - > erroralertview {returner ertview(消息:消息,宽度:宽度)} func updateuiview(_ uiview:errorerview,context:context){//不使用}}

如果此项目是一个完全运行的应用程序,则可能在所有屏幕上显示给用户的错误视图是相同的,而不仅仅是登录视图。通过利用UIViewRepresentable.,可以使用相同的错误视图消除两个框架的重复努力。

其他经验教训

因为这是我第一次尝试使用SWIFTUI建立重要的东西,我学到了一些事情,通常在一些试验和错误之后。虽然该项目最终实现了前面提到的目标,但有一些轻微的差异。例如,一个文本域在Swifui中,可以选择包含占位符文本,但无法为文本指定颜色。如果您仔细观察登录视图的两个渲染并排,则注意到灰色颜色略有不同。我确实遇到了一个你可以覆盖的工作文本看法,但为了这项运动,我决定了......嗯。最终,我对占位符文本颜色的缺陷态度并没有扩展到其他要求更多的研究,坚持不懈和对细节的关注。

向后兼容

与任何新框架一样,BUG比比皆是(嗯,也许不是“比比”,但我是一个傻瓜,因为声明)。当我第一次尝试建立并运行时loginsample.版本的项目低于iOS 13,该应用程序将崩溃。谢谢yichenbman的答案在堆栈溢出时,我需要做的就是添加-weak_framework swiftiui.作为价值其他联系人标志在构建设置中链接。问题解决了。

向后兼容性所需的另一个步骤包括@可用的iOS 13的属性及其在文件导入swifui文件中包含structs:

@Available(ios 13.0.0,*)struct loginview:查看{...}

过渡

在里面登录概述,显示和隐藏erroralert.视图取决于淋浴道布尔属性。淋浴道用来@状态属性包装器允许修改值。当修改该值时,SWIFTUI在不丢失状态的情况下会破坏并重新创建视图结构。erroralert.有一个自定义修改器,包括自定义转换,movetopededInoutwithopacity.使用函数不对称(插入:删除:)指定在插入和删除视图时要出现不同的转换动画。简单,何时淋浴道真的,视图从顶部滑动到屏幕上,当其值为时错误的,视图向上屏蔽屏幕。

// AnyTransition + Extension.Swift扩展{静态var moveTopeDedInoutWithopacity:AnyTransition {verTansion = AnyTransition.move(Edge:.top).combined(with:.opacity)removal = AnyTransition.move(边缘:.thep).combined(带:.opacity)返回.Asemmetric(插入:插入,删除:删除)}} // loginView.swift @state var showerror:bool = false var body:有些视图{...如果self.showerror {//显示或使用转换隐藏错误警报视图://`movetopededdinoutwithopacity`} ...}

当值淋浴道改变了,这与预期有点工作,但观点在没有过渡的情况下跳上屏幕:

登录样本 - 转换 - 不起作用

有两种缺失的步骤来制作它。第一步是通过将更改包裹到值来使用显式动画淋浴道呼吁汉语()。通过改变价值淋浴道在动画块中,Swifui知道要动动任何具有依赖于它的过渡的视图。注意汉语()正在使用任何值的时间变化否则插入或者删除不会工作。

// loginview.swift func buttentapped(){... insanimation {self.showerror = true} ...} func textentrytext rigetapped(){... insanimation {self.showerror = false} ...}

第二次缺失的步骤明确地设置了Zindex.在这内登录概述查看层次结构。谢谢斯科特格伯恩的答案在堆栈溢出上,我了解到嵌套视图来到父视图层次结构中,“他们的Zindex.不[始终]保持不变。“可以使用一个插入视图Zindex.并删除使用另一个Zindex.根据Swiftui如何重绘视图。简单来说,过渡正在发生,但你可能看不到它Zindex.并不一致。因此,您必须明确指定Zindex.总体视图层次结构中的视图:

var body:有些视图{... zstack(...){color(...).zindex(0)logilevstack(...){...){...} .zindex(1)如果self.showerror {ErrorAlert(...)...... .zindex(2)}} ...}

实现这两种缺少步骤后,错误警报视图按预期行为:

登录样本 - 转换工作

等等

最后,有两个资源在帮助我学习并在整个终点线上获取Loginsample项目至关重要。第一个是保罗哈德森的100天的swifui,“免费收集视频,教程,测试等”,第二个是Vadim BulaGin的键盘避免SWIFTUI视图文章。

包起来

我真的很喜欢沉入Swifui并编码这个项目。尽管Swifui是宣言性和易于阅读的,但它仍然需要时间来理解和实施。我想到了自己的时刻,“好吧,这对此是怎么样?你是怎么做到的?我怎么做到这一点?如何将这个视图垂直对齐屏幕上的垂直对齐?”值得庆幸的是,苹果/迅速的社区(原样)总是有助于帮助。我期待看到框架演变,特别是其他开发人员如何在uikit并排构建用户界面。如前所述,你可以查看GitHub上的Loginsample项目。谢谢阅读!