询问与Swiftui有关的Apple 2022问答(第1部分)
#mobile #ios #swift #swiftui

Photo by [Marsha Reid](https://unsplash.com/@marsha_reid?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/question-mark?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)

Ask Apple为开发人员提供了与WWDC以外的苹果工程师直接沟通的机会。本文汇编了与活动中的Swiftui有关的一些问答,其中包括一些个人见解。这是文章的第1部分。

Part Two

问答

uiactivityViewController

Q:是否有任何计划向iOS的UIACtivityViewController添加Anative swiftui?

a:它已经可用了!请查看ShareLink

ContextAction

Q:在iOS 16早期和MacOS 13 Beta版本中,我们看到了一个新的.Contextaction修饰符,后来被删除。是否有任何建议在列表中检测行选择,类似于“ navigationLink”,但没有导航到另一个视图(例如,显示工作表或从列表中选择一个选项)?按钮可能适用于iOS和iPados,但对MacOS不适合。另外,。封闭式支持多个选择。会回来吗?

a:查看ContextMenu修饰符的主要Action参数。该API还具有支持多个选择的forselectionType参数。

在Swiftui 4.0中,ContextMenu修饰符已得到显着改善。例如,上下文菜单可以具有多个选项,支持primaryAction并具有可自定义的预览视图。上面提到的带有主要Actory参数的ContextMenu不仅可以用于列表。

contextMenu

如何测试@State变量

Q:是否有推荐方法在SwiftUI视图中测试@State变量?将这些变量重构为视图模型的唯一方法是?

a:如果在同一视图中存在多个相互关联的@State属性,则将它们提取到结构中可能是一个不错的选择。将它们提取到视图模型也是一种策略,但不是必需的。

在单元测试期间,在SwiftUI视图中很难在SwiftUI视图中测试依赖项(符合DynamicProperty协议)。这也是类似Redux的框架的优点之一(从视图中提取状态以易于测试)。请阅读文章âWriting testable code when using SwiftUiâ学习如何编写测试友好的查看代码。

创建一个类似于IM应用程序的底部文本输入栏

问:您好,我的问题是关于Textfield。假设我们要创建类似于iMessage的视图,您可以在其中看到一个消息列表(与此示例无关),在视图的底部有一个文本字段。当用户单击文本字段时,键盘出现在其工具栏中。我尝试在ToolbariTemGroup(位置:.bottombar)中添加一个Textfield,这是ToolbariTemGroup(位置:.Keyboard)中的第二个,然后在@focusstate变量的帮助下,我可以隐藏一个并将焦点移动到密钥板。这有点麻烦,我认为有两个文本字段是正确的方法。同样,使用这种方法,@focusstate变量变得无反应,并且无法将其设置为nil(返回上一个视图时不会删除键盘)。是否可以在纯Swiftui(不使用Uikit)中实现这一目标?你能给我一些指导来完成吗?

a:通常,我建议使用.safeareainset(Edge:.bottom)实现底部文本字段。然后根据其焦点状态自定义其显示样式。我希望这对您的设计很有用。

由于SwiftUI 3.0中的SafeAreainset视图修饰符的发布,因此在问题中实现案例将不再困难。有关更多信息,请参阅文章Mastering Safe Area in SwiftUI

如何避免重新计算使用EnvironmentObject时创建实例的视图

Q:如何在不同子树中的两个同胞视图之间共享状态(例如可观察到的),而不会重新计算顶级视图主体?例如,我可以在父视图中具有状态对象,并通过环境对象传递该对象。但是,如果内部的 @Perparion属性更改,则重新计算了父视图及其子树。

a:环境观察者是一个很好的工具。如果您不希望更新父的视图,则可以在不使用@StateObject或@ObseverObject的情况下创建对象。

请注意,如果需要在父视图中修改环境对象实例,则应确保不会重复重建父视图(SwiftUI重新创建视图类型的实例)。有关详细信息,请参阅Apple Engineers建议的StateObject and ObservedObject

导航路径

Q:我很高兴看到新的NavigationStack/Navigation Path,它们对我来说效果很好。我想知道是否可以检查导航路径以确定我的搜索视图是否已输入某个视图(作为示例)。我已经有一个使用NavigationPath的想法。Codablerepresentation,但是我担心这可能不是观察导航路径的最佳或最可持续的方式。谢谢!

a:没有办法进行内省导航路径。如果您需要了解路径的内容,则一种很好的方法是使用均匀的路径,例如@State Private var Path:[myenum],然后在该枚举类型上的导航截面上切换。

导航可以创建一个完全基于类型的数据集合,仅需要元素符合Hashable协议。 NavigationPath具有一个有趣而有力的功能,即在没有其元素的所有类型信息的情况下能够对JSON进行编码和解码。阅读文章Reverse Engineering SwiftUI’s NavigationPath Codability以了解如何实施。

锁定图表的Y轴等级

Q:我有一个迅速的图表,并且我实施了一个统治者,该统治者在通过聆听拖放事件的方式拖动时会显示出来。在阻力期间,Y轴刻度变得更大。在我的示例中,它在不拖动时从0到75,拖动时从0到100。有没有办法防止这种情况?

a:您可以使用.chartyscale(域:0 ... 75)锁定y轴刻度域。

隐式和明确的动画

问:你好!在不使用Onchange Decorator的情况下,是否有其他方法可以直接基于状态变化来动画视图?这是我的代码。

.onChange(of: modle.state.aChange { value in
    withAnimation(...) {
        self.isAnimated = value
    }
   }
)

a:您可以使用.Animation(.easeInoun,value:model.state)直接对特定状态进行动画动画。对模型的任何更改。状态将触发动画。

通过使用绑定到特定状态的动画修饰符(动画修饰符的旧版本已被弃用),可以实现更精确的动画效果。阅读文章âSwiftUI Animation Mechanismâ添加有关动画的更多信息。

自适应高度表

Q:如何在iOS 16中呈现与动态内容高度相匹配的表格?我想在表面上使用视图的高度。

a:谢谢您的问题。目前这是不可能的,但这是我们感兴趣的。

估计苹果工程师很忙,没有认真考虑这个问题。在iOS 16中,通过将演示文章与几何读取器相结合,可以创建与内容高度相匹配的表格。 Here是完整的代码。

sheet

工具栏构造器

Q:我希望更好地支持@toolbarbuilder或条件{toolbaritem(a)} else {toolbaritem(b)}。

a:您永远不会看到@toolbarbuilder,因为@ToolBarContentBuilder在iOS 16中得到了增强。 /p>

窗口组

qï¼早上好!我是Swiftui初学者。我的问题是关于场景的。在几乎所有的教程和示例代码存储库中,仅使用一个窗口组场景,所有内容都嵌套在ContentView中。如何使用多个场景有任何指导或示例?还是大多数应用程序只需要一个窗口组?

a:多个场景可用于构建复杂的应用程序,尤其是在MacOS上。例如,您可能需要一个同时定义窗口组和文档组的应用程序,或一个具有窗口组和辅助窗口的应用程序的应用程序。场景。场景的内容视图定义了场景创建的窗口中的视图内容,但是场景本身定义了应用程序的整体结构。

在SwiftUI 4.0中,WindowGroup收到了重大更新,真正实现了MacOS应用程序的开发。有关更多信息,请阅读Building Adaptable SwiftUI Applications for Multiple Platforms和WWDC 2022会议。

DocumentGroup

qï¼在MacOS上使用SwiftUI应用程序生命周期和DocumentGroup时,如果应用程序只是数据读取器,可以禁用新文件创建吗?答:DocumentGroup提供了一个initializer来创建读取器类型应用程序。它仅允许打开该内容类型的文件,而不得编辑它们。

MVVM

qï¼在Uikit时代,MVVM是一个常见的体系结构,视图显示的数据来自单独的ViewModel类。这仍然适用于Swiftui,还是结构本身现在被视为ViewModel?

a:Swiftui试图对应用程序的整体体系结构不可知。但是,从传统的ViewModel意义上讲,我不建议将视图(结构本身)作为ViewModel。这可能会导致一些不良后果,例如将视图的可重复性和将业务逻辑绑定到Swiftui视图的生命周期,从而使处理业务逻辑更加困难。简而言之,我们不建议将视图用作ViewModel。但是,Swiftui确实提供了实现经典MVVM体系结构的工具(例如stateObject,观察到的)。

Onappearãinithth ViewDidload

qï¼在我的应用程序中,我在uihostingController中托管SwiftUi视图,所有这些视图都在UitabbarController中。最近,我注意到Swiftui视图的概念是在意外的时间触发的,例如创建UitabbarController的时间,而不是当视图本身出现时。我想知道:1。何时应该在UitabbarController中呼唤SwiftUi的景色? 2.当视图出现在uitabbarcontroller中时,执行代码的推荐方法是什么?

a:在其他类型的uiviewControllers中使用UIHostingController时,您可以通过在托管控制器上调用方法来提早触发视图加载。对于非懒惰视图(例如lazyvstack),一旦初始化了托管控制器的视图,就会调用onappear。对于懒惰的视图,在托管控制器的视图上调用Layoutsubviews或sizethatfits时,将初始化该视图。因此,如果您看到在UitabbarController的Init方法中初始化的视图,则需要查看Init的确切完成。尝试将工作移动到UitabbarController的查看迪德。

懒惰容器中的视图会反复调用onappear和ondisappear,具体取决于它们是否出现在可见区域。但是,Onappear和onDisappear并不是视图寿命的起点和结尾。实际上,这些观点(懒惰容器中的视图)将持续存在,直到懒惰的容器被破坏为止。请阅读Timing of onAppear Invocation以获取更多信息。

通用导航模型

qï¼我​​们使用的是带有路径参数的导航扇形,但是当用户将窗口从阶段管理器中的常规调整到紧凑的窗口时,我们在过渡路径上遇到了麻烦。在定期宽度中,我们有一个带有导航堆栈的侧边栏中的详细视图。在紧凑的宽度中,我们有一个标签栏,每个选项卡都有导航堆栈。

a:当前最好的方法是构建导航状态模型对象,该对象具有导航状态的规范表示。它可以为您的常规和紧凑型显示器提供专门的程序化绑定。例如,在您的模型中,您有多个路径,每个选项卡都有一个,但是在拆分视图中,只有一个路径的详细信息。使用常见的基础数据源,并将其投影到UI的需求,以便可以对单元进行测试,以确保常规和紧凑型预测之间的一致性。

在Swiftui 4中,紧凑和常规对应于两个不同的控件:NavigationStack和NavigationsPlitView。他们具有完全不同的驾驶模式。开发人员目前正在尝试创建一个可以优雅地提供两种模式路径的模型。阅读有关new navigation system in SwiftUI 4.0的了解以了解它们的差异。

位置偏移的方法和效率

Q:在非线性位置(带2个轴)渲染圆形图像的最佳方法是什么?目前,我正在使用ZSTACK并抵消图像将它们放置在我想要的位置,但是我不确定这是否是最有效的方法。

a:只要性能足以满足您的用例,这就是一种可行的方法。对我来说,这似乎是一个完全合理的实施。如果您遇到性能问题或想显着增加所绘制的图像数量,则可以尝试使用.drawingGroup和Canvas API,这两者都可用于密集图。

在Swiftui中,还有许多方法可以实现偏移,例如偏移,对位,填充,位置等。除了个人喜好外,您还应考虑偏移视图是否会影响周围的观点(ON ON ON布局级别)。有关更多详细信息,请阅读Layout with SwiftUI

NavigationsPlitView的尺寸规则

qï¼嗨!我已经开始使用NavigationsPlitView,我真的很喜欢。在某些情况下,我想根据视图是否崩溃做出决定(例如,在详细信息视图中显示消息是否扩展,或在详细信息视图中显示一个警告或其他指示,即。我可以假设,如果水平大小类是紧凑的,那就崩溃了吗?还是有一种更可靠的方法来确定这一点?

a:compact确实确实对应于崩溃的导航拆分视图。

如何提高包含大量UITEXTFIELD的视图的效率

Q:我有一个包含132个uitextfields的SwiftUi视图。我知道这是一个很大的数字,但它取决于业务逻辑。经过许多与内存泄漏的斗争后,我设法使它起作用。但是,从一个文本字段到下一个字段的过渡感觉不够平稳,每当我在文本字段中输入字母时,我的CPU使用情况似乎飙升至70%100%。此外,在使用Uikit实施相同的功能时,没有性能问题。

a:如果您使用iOS上的UITEXTFIELD遇到性能问题,则可以避免让每个视图成为UITEXTFIELD。默认渲染为文本,然后在单击文本时动态切换到UITEXTFIELD。

跨视图分层共享

Q:当数据来自API响应时,在多个视图之间共享数据的最佳方法是什么?我将EnvironmentObject用作ContentView中所有视图的包装器,并且我使用@environmentObject在每个视图中访问此数据。这是这种情况的最佳方法吗?这种方法的唯一问题是,当我添加新数据时,内存使用率会增加。

a: @EnvironmentObject/EnvironmentObject可能是跨视图层次结构共享相同模型的最佳工具。使用它们只能创建一个实例,该实例可以在儿童视图中读取。这应该不增加内存使用量(如果这样做,请提供反馈)。如果将越来越多的数据附加到模型对象,则可以增加内存使用情况,这是正常的。为了克服这一点,一种技术是在外部存储上保存一些数据,仅保留最相关的数据和标识符,以便可以完全检索其余数据。

任务与onappear

Q:.task和.onappear在同步操作之间是否有任何区别?换句话说,如果我编写color.green.task {self.somestate +=},我可以保证状态在第一次出现之前会改变吗?我之所

a:在我们首次在视图上运行身体之前,请调用on Appear和任务。对于您的用例,它们在行为上是等效的。

阅读Mastering the SwiftUI task Modifier以了解有关任务的更多信息。

窗口组ÅOpenWindowAction

Q:在MacOS上创建新窗口时是否可以附加参数?我正在同一子膜上创建一个新的托管对象,并希望将此对象发送到新窗口。当前,我的方法是将引用对单元中的潜在对象和托管对象保存,然后打开一个带有URL的新窗口,该窗口可以检查单元中的上下文和对象。如果我们可以启动具有自定义参数的新窗口,那就更好了。答:在Macos Ventura中,我们在窗口组上引入了新的API,使您可以在打开时将数据传递到窗口。这也可以与OpenWindowAction结合使用。请注意,您的数据需要是可选的或指定默认值,因为在某些情况下,框架本身会创建窗口(例如,在选择新的窗口菜单项时)。它还可以通过创建一个新场景来在iPados上使用,该场景是2/3或1/3拆分。

在构造函数中初始化@stateObject

Q:有没有办法在SwiftUI中使用视图struct参数初始化@StateObject?

a:是的,您可以通过在init方法中手动初始化@stateObject来实现这一目标。 self._store = stateObject(wrappedvalue:store(id:id))。要澄清,下划线看起来有些怪异,但是访问基础存储并没有错。官方文档主要试图指出最常见的用例,以便人们不要从一开始就直接初始化其物业包装器。顺便说一句,我们过去警告过通过基础存储来初始化@state。不是因为它不起作用,而是因为如果您对@state和身份的工作方式没有深入的了解,它的行为可能会令人困惑。

属性包装器类型由编译器在编译时间翻译,首先查看属性包装器类型的用户定义代码。有关下划线的含义和使用,请参见Adding @Published-like capability to custom property wrapper types

published

旧金山宽度样式

Q:如何使用Swiftui中的SF字体家族添​​加的三种新宽度样式(压缩,凝结,扩展)?

a:您可以使用FontWidth修饰符进行调整。

不幸的是,它仅支持SF,并且对中国人没有任何影响。请阅读文章âHow to change SwiftUI Font Widthâ€

width style

向步进添加快捷方式

问:我们如何在swiftui步进中添加增量和减少快捷方式(在macOS上)?

a:实现类似行为的一种方法是在菜单中使用命令来提供相同的操作。通常,应该有一个列表,让人们知道哪些键盘快捷键可用。但是,如果这不符合您的用例,我们将对有关该领域增强功能的反馈感兴趣。

您可以通过隐藏包含快捷方式的按钮来达到类似的要求。

    struct ContentView: View {
        @State var value = 10
        var body: some View {
            Form {
                Stepper(value: $value, in: 0...100, label: { Text("Value:\(value)") })
                    .background(
                        VStack {
                            Button("+") { value += value < 100 ? 1 : 0 }.keyboardShortcut("+",modifiers: [])
                            Button("-") { value -= value > 1 ? 1 : 0 }.keyboardShortcut("-",modifiers: [])
                        }.frame(width: 0).opacity(0)
                    )
            }
        }
    }

标签

问:有时(错误地)使用标签来提供值的文本说明(例如,帐户余额为10美元),但是一些开发人员并不意识到这种解释无法通过Voiceover读取。除了创建标签值组件外,还有Swiftui提供的其他解决方案吗?

a:Swiftui现在具有LabeledContent视图,您可以用来标记一些内容。 LabeLedContent带有内置格式支持!例如,labeledcontent(“年龄”,Value:person.age,格式:.number)。

读取Mastering LabeledContent in SwiftUI,以获取更多标签的用例。

如果在ViewBuilder中的语句

Q:我知道SwiftUi是基于ResultBuilder。因此,如果语句是通过树结构进行操作并建造的。在Swiftui中使用IF语句时是否有任何考虑?

a:关于/否则,重要的是要考虑它们如何影响观点的身份,这是在WWDC上的一个伟大的Abiaoqian中进行了讨论的。

在某些情况下,使用Lazy View修饰符不仅保持视图身份的稳定性,而且还可以在SwiftUI中进行更多优化。例如,使用.opacity(值<10?1:0.5),而不是值<10 {} else {}

初始化@State

Q:在初始化过程中设置@state var的值的正确方法是什么?我知道@State应该是一个内部价值,但是在某些情况下,我们需要从外部传递一个值,这对于on appear来说似乎是不可行的。以下方法总是出于某种原因而始终可以。

init(id: UUID) {
    self._store = StateObject(wrappedValue: Store(id: id))
}

开发人员可能误解了这个问题,并提供了与上面的状态对象相同的答案。问答器可能需要通过连续修改父视图中的ID参数的值来重新初始化状态的价值。这涉及符合DynamicProperty协议的所有属性包装器的功能:仅在视图寿命期间初始初始化的实例才能与视图创建相关联。请阅读How to Avoid Repeating SwiftUI View Updates以获取更多信息。从母体视图中传递环境值应满足问问者当前需求:父视图可以通过新值传递,并且当前视图也可以在视图范围内更改该值。

概括:

我忽略了未得出结论的问题。我希望以上摘要对您有所帮助。

Buy Me a Coffee

Donate with PAYPAL

我希望本文对您有帮助。欢迎您通过TwitterDiscord channelmy blog的留言板与我交流。