swiftui_SwiftUI的混合包

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.mzph.cn/news/275421.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

三年经验前端社招——有赞

大家好&#xff0c;我是若川&#xff0c;祝大家中秋节快乐。最近组织了源码共读活动《1个月&#xff0c;200人&#xff0c;一起读了4周源码》&#xff0c;已经有超50人提交了笔记&#xff0c;群里已经有超1200人&#xff0c;感兴趣的可以点此链接扫码加我微信 ruochuan12 参与。…

html的 button点击事件无效,InfoWindow里面加button,监听button点击事件无效 求解啊...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼点击infoWindw中的button按钮&#xff0c;无效果&#xff1b;覆盖默认的dom结构html,body,#container {width: 100%;height: 100%;margin: 0px;}p.my-desc {margin: 5px 0;line-height: 150%;}//创建地图var map new AMap.Map(con…

4月第1周业务风控关注 |国家广播电视总局发布《未成年人节目管理规定》

易盾业务风控周报每周呈报值得关注的安全技术和事件&#xff0c;包括但不限于内容安全、移动安全、业务安全和网络安全&#xff0c;帮助企业提高警惕&#xff0c;规避这些似小实大、影响业务健康发展的安全风险。 1、国家广播电视总局发布《未成年人节目管理规定》 国家广播电视…

Ubuntu 11.04 x64 下安装Python

在网上搜了下&#xff0c;找到了如下安装顺序&#xff1a; Install python2.7 wget http://www.python.org/ftp/python/2.7/Python-2.7.tar.bz2 tar xjf Python-2.7.tar.bz2 cd Python-2.7/ ./configure make sudo make altinstall Install setuptools cd .. wget http://pyp…

数据挖掘 点击更多 界面_8(更多)技巧,可快速改善用户界面

数据挖掘 点击更多 界面重点 (Top highlight)Creating beautiful, usable, and efficient UIs takes time, with many design revisions along the way. Making those constant tweaks to produce something that your clients, users, and yourself are truly happy with. I k…

Node.js+Express+MongoDB 实现学生增删改查

前言 选用Node.js&#xff0c;Express&#xff0c;MongoDB来实现一个学生信息的增删改查。 Express框架搭建服务器art-template模板实现页面MongoDB数据库Mongoose操作数据库安装 npm install express mongoosenpm install art-template express-art-templatenpm install body-…

html5波浪线条,HTML5 svg炫酷波浪线条动画插件

这是一款HTML5 svg炫酷波浪线条动画插件。该波浪动画插件基于tweenMax和SVG&#xff0c;也可以作为jQuery插件来使用&#xff0c;可以制作出漂亮的波浪线条动画特效。使用方法在页面中引入jquery和TweenMax.min.js文件&#xff0c;以及wavify.js和jquery.wavify.js文件。HTML结…

三年经验前端社招——腾讯微保

大家好&#xff0c;我是若川。祝大家中秋节快乐。最近组织了源码共读活动《1个月&#xff0c;200人&#xff0c;一起读了4周源码》&#xff0c;已经有超50人提交了笔记&#xff0c;群里已经有超1200人&#xff0c;感兴趣的可以点此链接扫码加我微信 ruochuan12 参与。本文经作者…

Matlab数理统计工具箱应用简介

1&#xff0e; 概述 Matlab 的数理统计工具箱是 Matlab 工具箱中较为简单的一个&#xff0c;其牵扯的数学知识是大家都很熟悉的数理统计&#xff0c;因此在本文中&#xff0c;我们将不再对数理统计的知识进行重复&#xff0c;仅仅列出数理统计工具箱的一些函数&#xff0c;这些…

matlab绘制路线图_绘制国际水域路线图

matlab绘制路线图Two years ago, Shopify was only available in English. Few people in Germany or Japan had heard about us. We had only just formed the international growth team to make Shopify available to people in their native tongue.两年前&#xff0c;Shop…

2021年江苏高考各科成绩查询,江苏2021年高考总分及各科分数

江苏2021年高考总分及各科分数2021-04-16 08:46:02文/董月江苏高考将实施“33”模式&#xff0c;即语数外三门必考&#xff0c;然后在物理、化学、生物、历史、政治、地理六门学科中任选三门进行考试&#xff0c;并计入总分。“6选3”中的3门以等级确定&#xff0c;折算成分数计…

碎片时间学习前端,我推荐这些~

大家好&#xff0c;我是若川。祝大家中秋节快乐。前端技术日新月异&#xff0c;发展迅速&#xff0c;作为一个与时俱进的前端工程师&#xff0c;需要不断的学习。这里强烈推荐几个前端开发工程师必备的优质公众号&#xff0c;希望对你有所帮助。大家可以像我一样&#xff0c;利…

windows 端口冲突解决

windows 端口冲突解决 命令 说明 ps&#xff1a;我这里要解决的80端口冲突。 命令 netstat -ano&#xff0c;列出所有端口的使用情况&#xff0c;在列表中我们观察被占用的端口。 netstat -aon|findstr “PID” 查看被占用端口对应的PID&#xff0c;这里的"PID"是上一…

使用selector改变按钮状态

在res/drawable文件夹新增一个文件&#xff0c;此文件设置了图片的触发状态&#xff0c;你可以设置 state_pressed&#xff0c;state_checked&#xff0c;state_pressed&#xff0c;state_selected&#xff0c;state_focused&#xff0c;state_enabled 等几个状态&#xff1a; …

figma下载_通过构建7个通用UI动画来掌握Figma中的动画

figma下载Originally published on my personal blog.最初发布在我的 个人博客上 。 Most designers will spend many hours perfecting every pixel of their static UI designs but will barely spend any time perfecting the transitions between these pages.大多数设计人…

怎么用计算机上的打印设备打印,电脑中怎么添加打印机设备

电脑中怎么添加打印机设备电脑中怎么添加打印机设备呢&#xff0c;下面小编介绍一下。具体如下&#xff1a;1. 打开电脑&#xff0c;点击“控制面板”图标2. 在如图页面&#xff0c;找到“硬件和声音”&#xff0c;点击打开3. 然后点击”设备和打印机“选项4. 打开后&#xff0…

三年经验前端社招——朴朴科技

大家好&#xff0c;我是若川&#xff0c;祝大家中秋节快乐。最近组织了源码共读活动《1个月&#xff0c;200人&#xff0c;一起读了4周源码》&#xff0c;已经有超50人提交了笔记&#xff0c;群里已经有超1200人&#xff0c;感兴趣的可以点此链接扫码加我微信 ruochuan12 参与。…

EL表达式和JSTL标准标签库

一、EL表达式 什么是EL表达式 EL&#xff08;Express Lanuage&#xff09;表达式可以嵌入在jsp页面内部减少jsp脚本的编写EL出现的目的是要替代jsp页面中脚本的编写。EL表达式的作用 EL最主要的作用是获得四大域中的数据// 1. pageContext ${pageScope.key}; // 2. request ${r…

(转)细说Cookie

原文地址&#xff1a;http://www.cnblogs.com/fish-li/archive/2011/07/03/2096903.htmlCookie虽然是个很简单的东西&#xff0c;但它又是WEB开发中一个很重要的客户端数据来源&#xff0c;而且它可以实现扩展性很好的会话状态&#xff0c; 所以我认为每个WEB开发人员都有必要对…

数学在计算机科学上的应用文献,浅谈数学在计算机科学及应用中的作用

论文编号:SXJY040论文字数:5690,页数:06浅谈数学在计算机科学及应用中的作用[摘要]&#xff1a;数学作为伴随人类历史发展长期积累的智慧结晶&#xff0c;是学习和运用科学技术的语言&#xff0c;代表着人类智慧的最高成就。本文阐述了数学发展的科学趋势&#xff0c;并对数学与…