swiftui

介绍 (Introduction)

SwiftUI introduced us to a whole new way of designing and coding interfaces. Gone are the old ways of subclassing UIKit (or AppKit) classes and hardwiring layout constraints. Instead, we now have a nice, declarative way of structuring and styling our controls and making sure the interface updates whenever new information or events arrive.

SwiftUI向我们介绍了一种全新的界面设计和编码方式。 继承UIKit(或AppKit)类和固定布局约束的旧方法已经一去不复返了。 取而代之的是,我们现在有了一种很好的声明式方法来构造和样式化控件,并确保只要有新信息或事件到达,接口就会更新。

To facilitate this new architecture, the good people at Apple took some of Swift’s best features (e.g. protocols, generics, opaque types) and combined them into SwiftUI. However, this comes at a hidden cost: If you’re not already well-versed in these features, there will be a bit of a learning curve and most likely a lot of cryptic error messages that will send you off to your favorite search engine. This article will look at some of these error messages and explain what they mean and what you can do to prevent them.

为了促进这种新架构的发展,Apple的好人采用了Swift的一些最佳功能(例如协议,泛型,不透明类型),并将它们组合到SwiftUI中。 但是,这需要付出一些隐性的代价:如果您还不熟悉这些功能,将会有一些学习过程,并且很可能会出现很多隐含的错误消息,这些错误消息会将您带到您最喜欢的搜索引擎。 本文将研究其中一些错误消息,并解释它们的含义以及如何防止这些错误消息。

建立视图 (Building a View)

When implementing a new SwiftUI view, you typically start small. You add some components to the body, you style them, and handle any interactions. At some point, your simple view starts to get too big or you get a lot of conditional logic or duplication in your body. So, you decide to move some of the logic out of the body and into a separate function. This function will take care of building some complex components for you, and since everything is a View in SwiftUI, you simply define the function type signature like this:

在实现新的SwiftUI视图时,通常从小处着手。 您将一些组件添加到body ,对其进行样式设置并处理任何交互。 在某个时候,您的简单视图开始变得太大,或者您的body了很多条件逻辑或重复项。 因此,您决定将某些逻辑移出body并移至单独的函数中。 该函数将为您构建一些复杂的组件,并且由于一切都是SwiftUI中的View ,因此您只需定义函数类型签名即可,如下所示:

private func buildComplexButton() -> View

Great! Well… apart from the compiler, which complains,

大! 好吧……除了编译器抱怨

“Protocol ‘View’ can only be used as a generic constraint because it has Self or associated type requirements.”

“协议“视图”只能用作通用约束,因为它具有“自身”或关联的类型要求。”

The problem here lies in the last part of the error message: Any object conforming to the View protocol will need to have an associated type Body that determines how the view is actually implemented. Attempting to return just a plain View from your function results in the compiler throwing up its hands and saying, “I don’t know what the return type will be without any additional information on the actual type that is conforming to this protocol.” It’s a bit like returning a generic type (such as Array) without specifying the type parameter list (What does the Array contain?). But that is exactly the point! We don’t want to pin ourselves down to a concrete type just yet. Our function might generate a variety of different views with different concrete types. Luckily, Swift 5.1 introduced the keyword some to help with this, and you’ve already seen it when creating a new view:

这里的问题出在错误消息的最后一部分:任何符合View协议的对象都需要具有一个 关联类型决定视图实际实现方式的Body 。 尝试从函数中仅返回普通View导致编译器举起手来,说:“如果不提供有关符合此协议的实际类型的任何其他信息,我将不知道返回类型将是什么。” 这有点像不指定类型参数列表( Array包含什么?)而返回泛型类型(例如Array )。 但这就是重点! 我们现在还不想将自己固定在一个具体的类型上。 我们的函数可能会生成具有不同具体类型的各种不同视图。 幸运的是,Swift 5.1引入了关键字some来解决这个问题,在创建新视图时您已经看到了它:

var body: some View

Naively, this means what you think it means: We return some View and we don’t really care what kind. This is commonly referred to as an opaque type: a type that has some capabilities (it’s a View), but we don’t know exactly what kind of view. So, we’ll update our function to the new signature and give it an implementation:

天真的,这意味着您认为它意味着什么:我们返回一些 View而我们实际上并不关心哪种类型。 通常将其称为不透明类型:具有某些功能的类型(它是View ),但是我们不确切知道哪种视图。 因此,我们将功能更新为新的签名并为其提供实现:

And all is well again! Well… as long as you make sure that every possible View that you return from this function has the exact same type. The restriction on opaque types is that the compiler will only allow them if every available code path will return the same concrete type. We’re only returning identical buttons, so no issues here. However, suppose we are implementing a user interface for a keypad.

而且一切都很好! 好吧……只要确保从此函数返回的每个可能的View都具有完全相同的类型。 对不透明类型的限制是,仅当每个可用代码路径都返回相同的具体类型时,编译器才允许它们。 我们只返回相同的按钮,因此这里没有问题。 但是,假设我们正在实现键盘的用户界面。

A simple keypad user interface

We’ve chosen to implement this as a grid of Buttons. Since all the buttons are more or less identical and we don’t want to hardcode each and every one of them, we use a builder function to create them. There are two main types of buttons: ones with a text label (the digits) and ones with an image (in this case, the delete and Face ID symbols coming from SF Symbols). Simplified, it looks like this:

我们选择将其实现为Buttons的网格。 由于所有按钮或多或少都是相同的,并且我们不想对每个按钮进行硬编码,因此我们使用了一个builder函数来创建它们。 按钮主要有两种类型:带有文本标签(数字)的按钮和带有图像的按钮(在这种情况下,为SF Symbols的Delete和Face ID符号)。 简化后,它看起来像这样:

We’re still returning Buttons, so this must work, right? Well, the compiler unfortunately says no:

我们仍在返回Buttons ,所以这必须工作,对吗? 好吧,编译器不幸地拒绝了:

“Function declares an opaque return type, but the return statements in its body do not have matching underlying types.”

“函数声明了不透明的返回类型,但是其主体中的return语句没有匹配的基础类型。”

Odd. A button is a button, right? But if we examine the documentation, we will see that Button is actually a generic type and not a plain struct like Text:

奇。 一个按钮就是一个按钮,对不对? 但是,如果我们仔细阅读文档 ,将会发现Button实际上是一个通用类型,而不是像Text这样的普通结构:

struct Button<Label> where Label : View

And this holds for a lot of the SwiftUI built-in types — most notably the ones that can contain other views or content. So, we are trying to return either a Button<Text> or a Button<Image> that the compiler (correctly) identifies as two different types and hence refuses to cooperate. This is one of those situations where the rigorous typing of Swift is working against us.

这适用于许多SwiftUI内置类型-最值得注意的是可以包含其他视图或内容的类型。 因此,我们试图返回Button<Text>Button<Image> ,编译器正确地将它们标识为两种不同的类型,因此拒绝合作。 这是Swift严格键入对我们不利的情况之一。

Fortunately, there are two ways to solve this issue, and both deal with satisfying the compiler just enough that it’ll allow us to compile and run our code:

幸运的是,有两种方法可以解决此问题,并且两种方法都足以使编译器满意,从而使我们能够编译和运行代码:

  1. Embedding our views in a Group, preserving as much type information as possible.

    将我们的意见嵌入到Group ,并保留尽可能多的类型信息。

  2. Wrapping our views in AnyView, effectively removing type information.

    将我们的视图包装在AnyView ,可以有效地删除类型信息。

Both methods have their peculiarities and it’s ultimately up to you to decide which one suits you best.

两种方法都有其独特性,最终由您决定哪种方法最适合您。

嵌入组 (Embedding in a Group)

This is what some people consider the “cleanest” approach because embedding your mixed content in a Group preserves all typing information. However, it introduces some types you might not expect and you’re currently limited to only the simple if statements for any conditional switching. This means no if case let or switch statements. If that’s not an issue, then go right ahead. It looks something like this:

这就是某些人认为的“最干净”的方法,因为将您的混合内容嵌入到Group保留所有键入信息。 但是,它引入了一些您可能不会想到的类型,并且当前您仅限于用于任何条件切换的简单if语句。 这意味着没有if case letswitch语句。 如果这不是问题,那就继续吧。 看起来像这样:

Now, this isn’t some “magic” fix that changes the way opaque types work. It merely introduces some additional types that make sure that from a compiler perspective, this function always returns the same type. If we inspect it, we see that the type returned is:

现在,这不是改变不透明类型工作方式的“魔术”解决方案。 它只是引入了一些其他类型,这些类型可以确保从编译器的角度来看,此函数始终返回相同的类型。 如果我们检查它,我们看到返回的类型是:

Group<_ConditionalContent<Button<Text>, Button<Image>>>

Again, Group is a generic type, but it introduces an additional (generic) type _ConditionalContent that has our button types (again generics) in the type parameter list. And this is actually the trick up SwiftUI’s sleeve: By being smart and introducing additional types, it can preserve all the original types and still make the compiler happy because we’re always returning the same type to satisfy the some View return type. But as I’ve mentioned, you’re limited to what SwiftUI can actually express. So, for example, any complex logic switching is off the table for now. Also, understand that this is a very simple case and it’s already generating a complex result type. Now imagine having a lot of nested logic and generic types, and this will soon become very hard to read and comprehend.

同样, Group是泛型类型,但它引入了一个附加的(泛型)类型_ConditionalContent ,该类型在类型参数列表中具有我们的按钮类型(再次为泛型)。 这实际上是SwiftUI的窍门:通过聪明并引入其他类型,它可以保留所有原始类型,并使编译器满意,因为我们总是返回相同的类型以满足some View返回类型。 但是正如我已经提到的那样,您仅限于SwiftUI可以实际表达的内容。 因此,例如,任何复杂的逻辑切换都暂时不在讨论之列。 另外,请了解这是一个非常简单的案例,并且已经在生成一个复杂的结果类型。 现在想象一下,有很多嵌套的逻辑和泛型类型,而这很快将变得很难阅读和理解。

So, the upside is that we maintain all our type information, but the downside is that we will be generating a lot of complex types and we’re limited to the expressiveness of the SwiftUI view builders.

因此,好处是我们保留了所有类型信息,但缺点是我们将生成许多复杂的类型,并且仅限于SwiftUI视图构建器的表现力。

在AnyView中包装 (Wrapping in AnyView)

Wrapping in AnyView is the other method, and it involves something called type erasure to effectively strip away information regarding the types of the views and making it seem like they’re all the same. It looks something like this:

在AnyView中包装是另一种方法,它涉及一种称为类型擦除的方法,可以有效地剥离有关视图类型的信息,并使它们看起来都一样。 看起来像这样:

We are wrapping our views here in an AnyView that conforms itself to the View protocol and will delegate any calls to it to the wrapped view (our buttons). To the outside world (i.e. the compiler), our function now always returns the exact same type (AnyView) and it will not complain.

我们在这里将视图包装在符合View协议的AnyView ,并将对它的所有调用委派给包装的视图(我们的按钮)。 对于外界(即编译器),我们的函数现在始终返回完全相同的类型( AnyView ),并且不会抱怨。

We can make this even easier by introducing an extension to View to provide a function that can return the type-erased view for us and make it work like many of the other modifiers:

我们可以通过向View引入扩展来提供一个函数,该函数可以为我们返回经过类型擦除的视图并使它像许多其他修饰符一样工作,从而使此操作变得更加容易:

The upside here is that we can use the full expressiveness of Swift (and not just whatever SwiftUI has implemented) with regards to control logic: if case let or switch or even other complex logic — it’s all possible. The downside is that you effectively lose access to the regular types and can only access the parts that AnyView exposes to you. Since, most of the time, the wrapping in AnyView will be the last thing you do, it’s not a very big issue and you can still access all the properties provided by the View protocol (since AnyView conforms to View).

这里的好处是,我们可以在控制逻辑方面使用Swift的完整表达能力(而不仅仅是SwiftUI实现的功能): if case letswitch或什至其他复杂的逻辑-一切皆有可能。 缺点是您实际上无法访问常规类型,并且只能访问AnyView公开给您的部分。 因为在大多数情况下, AnyView的包装将是您要做的最后一件事,所以这不是一个很大的问题,并且您仍然可以访问View协议提供的所有属性(因为AnyView符合View )。

There have been some concerns about performance due to the fact that SwiftUI has to destroy and rebuild the view hierarchy whenever the wrapped View inside the AnyView changes, but if you’re not constantly doing this (and most user interfaces don’t), there should not be an issue.

已经有大约性能,因为这样的事实,SwiftUI具有摧毁并重建视图层次每当包裹有些担忧View里面AnyView变化,但如果你不经常这样做(和大多数的用户界面没有),有应该不是问题。

结论 (Conclusion)

Building complex user interfaces in SwiftUI can quite rapidly become a frustrating experience due to the way the compiler dictates how we can handle generic types, protocols with associated types, and opaque types. Sooner or later, you’ll run into some of the aforementioned issues. We’ve seen two ways to circumvent these issues: one by embedding your content in a Group (type-preserving, but with the caveat that you’re limited to what SwiftUI can express) and one by wrapping in AnyView (effectively hiding type information from the compiler, but gaining more expressiveness). Both methods are valid and can be considered for use in your own apps, and now you should have an idea of why you might choose one over the other.

由于编译器指示我们如何处理通用类型,具有关联类型的协议和不透明类型的方式,因此在SwiftUI中构建复杂的用户界面会很快变得令人沮丧。 迟早,您都会遇到一些上述问题。 我们已经看到了两种方法来解决这些问题:一种方法是将您的内容嵌入到一个Group (保留类型,但是需要注意的是,您限于SwiftUI可以表达的内容),另一种方法是通过包装在AnyView (有效地隐藏类型信息)从编译器,但获得更多的表现力)。 这两种方法都是有效的,可以考虑在自己的应用程序中使用,现在您应该知道为什么可能要选择一种方法了。

As a closing note, it is impressive how Swift preserves all the typing information when building views and how it works “most of the time” given the rigorous type checking that the compiler does. If you’re interested in this, I suggest you look at how ViewBuilder works. This is used under the hood to build SwiftUI views containing one or more child views and provide functionality to support basic logic in your view templates using, for example, TupleView and _ConditionalContent (the latter unfortunately being marked private). Swift by Sundell has a nice overview of many of the Swift 5.1 features that power SwiftUI/ViewBuilder.

作为结束语,令人印象深刻的是,在编译器进行严格的类型检查的情况下,Swift如何在构建视图时保留所有类型的信息,以及“大部分时间”如何工作。 如果您对此感兴趣,建议您查看ViewBuilder工作方式。 它在后台用于构建包含一个或多个子视图的SwiftUI视图,并使用TupleView_ConditionalContent (不幸的是后者被标记为私有)提供功能来支持视图模板中的基本逻辑。 Sundell的Swift很好地概述了支持SwiftUI / ViewBuilder的许多Swift 5.1功能 。

We’ve also sort of glossed over how type erasure exactly works in Swift, but it is actually used in more places in Swift, such as AnySequence and AnyPublisher. In the latter case, it is actually helpful to hide some type information not just from the compiler but also from others.

我们还对类型擦除在Swift中的工作原理进行了一些AnySequence ,但实际上它在Swift中的更多地方都得到了使用,例如AnySequenceAnyPublisher 。 在后一种情况下,不仅对编译器而且对其他类型隐藏一些类型信息实际上是有帮助的。

“When you use type erasure this way, you can change the underlying publisher implementation over time without affecting existing clients.” — Apple’s official documentation

“当您以这种方式使用类型擦除时,您可以随时间更改基础发布者实现,而不会影响现有客户端。” — 苹果官方文档

Again, I recommend an article by Swift by Sundell to get to grips with type erasure.

再次,我推荐Sundell的Swift撰写的一篇文章来处理类型擦除。

翻译自: https://medium.com/better-programming/a-mixed-bag-of-swiftui-11e018a280b7

swiftui

http://www.taodudu.cc/news/show-894145.html

相关文章:

  • 数据挖掘 点击更多 界面_8(更多)技巧,可快速改善用户界面
  • matlab绘制路线图_绘制国际水域路线图
  • figma下载_通过构建7个通用UI动画来掌握Figma中的动画
  • 黑客宣言_情感设计宣言
  • 钮扣电池电压电量_纽扣厂
  • 印发 指南 通知_通知设计的综合指南
  • 现代人的压力和焦虑_设计师如何建立减少焦虑和压力的体验
  • 去贵阳参观大数据到哪参观_您必须参观的四个世界
  • figma下载_我关于Figma文件封面的故事
  • lynda ux_如何进入UX领域
  • :寻找指定和的整数对_寻找时间:如何增加设计的时间
  • linkedin爬虫_重新设计Linkedin的指导功能-用户体验案例研究
  • 大萧条时期什么行业走俏_大流行时期的用户体验
  • nda协议_如何将NDA项目添加到您的投资组合
  • 小程序卡片叠层切换卡片_现在,卡片和清单在哪里?
  • $.when.apply_When2Meet vs.LettuceMeet:UI和美学方面的案例研究
  • 利益相关者软件工程_如何向利益相关者解释用户体验的重要性
  • 在当今移动互联网时代_谁在提供当今最好的电子邮件体验?
  • 网络低俗词_从“低俗小说”中汲取7堂课,以创建有影响力的作品集
  • webflow如何使用_我如何使用Webflow构建辅助项目以帮助设计人员进行连接
  • cv::mat 颜色空间_网站设计基础:负空间
  • shields 徽标_我的徽标素描过程
  • ui设计未来十年前景_UI设计的10条诫命
  • 如何了解自己的认知偏差_了解吸引力偏差
  • 女生适合学ux吗_UX设计色彩心理学,理论与可访问性
  • 视觉测试_视觉设计流行测验
  • figma下载_切换到Figma并在其中工作不必是火箭科学,这就是为什么
  • 初级爬虫师_初级设计师的4条视觉原则
  • ux和ui_糟糕的UI与UX番茄酱模因
  • csdn 用户 蚂蚁翘大象_用户界面设计师房间里的大象

swiftui_SwiftUI的混合包相关推荐

  1. ios 按钮图片充满按钮_iOS有一些非常危险的按钮-UX评论

    ios 按钮图片充满按钮 I recently bought a cool thing off Amazon. It's an adapter for iPhone, making it easy t ...

  2. figma设计_设计原型的最简单方法:Figma速成课程

    figma设计 It doesn't matter if you haven't used any prototyping tools before or you're transitioning f ...

  3. myeclipse深色模式_完善深色模式的调色板

    myeclipse深色模式 Apps largely have a limited color palette which may already map well to dark mode. How ...

  4. layui选项卡嵌套选项卡_在ProtoPie中使用嵌套组件构建选项卡栏

    layui选项卡嵌套选项卡 One of the powerful features of ProtoPie is the ability to build fully portable and in ...

  5. 计算机视觉笔记本推荐_视觉灵感:Mishti笔记本

    计算机视觉笔记本推荐 The Mishti Notebook is a project close to my heart, wherein I experimented with screen pr ...

  6. open ai gpt_让我们来谈谈将GPT-3 AI推文震撼到核心的那条推文

    open ai gpt 重点 (Top highlight) "设计师"插件 (The 'Designer' plugin) A couple days ago, a tweet ...

  7. illustrator下载_平面设计:16个Illustrator快捷方式可加快工作流程

    illustrator下载 I know, I know - keyboard shortcuts sound so nerdy, and you're a graphic designer, not ...

  8. ux设计中的各种地图_UX写作中的移情

    ux设计中的各种地图 Demetri Martin is a master of comedic situations. If you've never seen Demetri Martin是喜剧情 ...

  9. 产品设计美学案例分析_美学在产品设计中的重要性

    产品设计美学案例分析 重点 (Top highlight) In one of my previous jobs, I had really interesting debates with the ...

最新文章

  1. Java的File类
  2. note-在VisualStudio中使用正则表达式
  3. The method setClass(Context, Class?) in the type Intent is not applicable for the arguments (GameV
  4. 透过汇编另眼看世界之DLL导出函数调用
  5. 看到碟摊上有D版的《阿猫阿狗2》
  6. 软件测试msf模型,Visual Studio 2010 Ultimate中MSF过程模型的设计
  7. linux3.3内核去哪下载,Linux Kernel下载|Linux Kernel v3.18.3 稳定版 - 121下载站
  8. C语言:存储类型,内存管理
  9. php 向文件夹中添加HTML文件,批量向html中插入内容
  10. 对SQLite数据库操作 操作db文件
  11. 测试图片真假软件,如何找出照片的PS痕迹__如何检测一张图片是否被PS过_飞翔教程...
  12. 图像处理之边缘检测[微分算子、Canny算子和LOG算子]
  13. 如何设计出一个比较合理的数据归档系统
  14. 那些悄悄变厉害的人,都在偷偷对自己下狠手
  15. Kafka生产者、消费者的消息可靠性方案实现
  16. Java 危矣!统治地位已不复存在?
  17. excel批量制作条形码_如何在Microsoft Excel中制作条形图
  18. 旋转目标检测 校准的深度特征用于目标检测SSA
  19. 阿里云OSS存储实例
  20. transition、trasform和translate

热门文章

  1. php 完美分页,php完美分页类程序
  2. java忽略引号中的分隔符_java – 令牌化但忽略引号内的分隔符
  3. python支持向量机回归_Python中支持向量机SVM的使用方法详解
  4. codeforces 1060 A
  5. 使用a标签调用手机系统的一些小技巧(打电话、发短信)
  6. C语言 · 出栈次序
  7. GMF学习系列(二) 一些知识点(续2)
  8. mysql gtid寻找位置_【MySQL】UUID与GTID以及如何根据GTID找寻filename和position
  9. 51芯片4*4列阵按键c语言程序,单片机城中社稷.doc
  10. mysql 取左_MySQL select语句从字符串左侧获取5个字符