当前位置: 首页 > 知识库问答 >
问题:

在SwiftUI中有条件地使用视图

向弘懿
2023-03-14

我试图找出正确的方法,有条件地包括一个视图与swiftui。我不能直接在视图中使用if,必须使用堆栈视图才能做到这一点。

这是可行的,但似乎会有更干净的方法。

var body: some View {
    HStack() {
        if keychain.get("api-key") != nil {
            TabView()
        } else {
            LoginView()
        }
    }
}

共有3个答案

王楚青
2023-03-14

你没有把它包括在你的问题中,但我猜你在没有堆栈的情况下得到的错误如下?

函数声明了一个不透明的返回类型,但其正文中没有返回语句来推断基础类型

这个错误为您提供了一个很好的提示,但是为了理解它,您需要理解不透明返回类型的概念。这就是如何调用前缀为some关键字的类型。我在WWDC没有看到任何苹果工程师深入研究这个问题(也许我错过了各自的演讲?),这就是为什么我自己做了很多研究,并写了一篇关于这些类型如何工作以及为什么它们在SwiftUI中用作返回类型的文章。

另外还有一个详细的技术说明

如果你想完全理解正在发生的事情,我建议你读这两本书。

这里有一个简单的解释:

具有不透明结果类型(某些Type
的函数或属性必须始终返回相同的具体类型。

在您的示例中,body属性根据条件返回不同的类型:

var body: some View {
    if someConditionIsTrue {
        TabView()
    } else {
        LoginView()
    }
}

如果someConditionIsTrue,它将返回一个TabView,否则返回一个LoginView。这违反了规则,这就是编译器抱怨的原因。

如果将条件包装在堆栈视图中,堆栈视图将在其自身的泛型类型中包含两个条件分支的具体类型:

HStack<ConditionalContent<TabView, LoginView>>

因此,无论实际返回哪个视图,堆栈的结果类型始终相同,因此编译器不会抱怨。

实际上,SwiftUI专门为这个用例提供了一个视图组件,正如您在上面的示例中所看到的,它实际上是堆栈内部使用的组件:

它具有以下泛型类型,泛型占位符将从您的实现中自动推断:

ConditionalContent<TrueContent, FalseContent>

我建议使用该视图容器而不是堆栈,因为它使其他html" target="_blank">开发人员在语义上明确了它的用途。

汪信鸥
2023-03-14

我需要有条件地将一个视图嵌入到另一个视图中,因此我最终创建了一个方便的if函数:

extension View {
   @ViewBuilder
   func `if`<Content: View>(_ conditional: Bool, content: (Self) -> Content) -> some View {
        if conditional {
            content(self)
        } else {
            self
        }
    }
}

这确实会返回一个AnyView,这并不理想,但感觉它在技术上是正确的,因为您在编译时并不真正知道它的结果。

在我的例子中,我需要将视图嵌入到ScrollView中,因此它看起来像这样:

var body: some View {
    VStack() {
        Text("Line 1")
        Text("Line 2")
    }
    .if(someCondition) { content in
        ScrollView(.vertical) { content }
    }
}

但您也可以使用它有条件地应用修饰符:

var body: some View {
    Text("Some text")
    .if(someCondition) { content in
        content.foregroundColor(.red)
    }
}

更新:在使用此选项之前,请阅读使用条件修饰符的缺点:https://www.objc.io/blog/2021/08/24/conditional-view-modifiers/

暨曾笑
2023-03-14

避免使用额外容器(如HStack)的最简单方法是将body属性注释为@ViewBuilder,如下所示:

@ViewBuilder
var body: some View {
    if user.isLoggedIn {
        MainView()
    } else {
        LoginView()
    }
}
 类似资料:
  • 问题内容: 我需要使用’forumChild’类更改标签的CSS 类。它必须每隔foreach循环的3个循环进行一次更改。 有没有一种方法可以从控件内部执行此操作? 提前致谢 问题答案: 凡和是CSS类

  • 我试图翻译这个(简化)代码使用Java-8流: 以下是我尝试过的: 但以上给出了所有

  • 问题内容: 是否可以通过SwiftUI与现有UIKit应用程序并排构建视图? 我有一个用Objective-C编写的现有应用程序。我已经开始迁移到Swift5。我想知道是否可以将SwiftUI与现有的UIKit .xib视图一起使用。 也就是说,我希望在同一应用程序中使用SwiftUI构建一些视图,并使用UIKit构建一些其他视图。当然不要混合两者。 彼此一起工作 问题答案: 编辑05/06/19

  • 我们为应用程序设计了一些自定义控件,如CustomButton、CustomTexView等。这些控件定义应用程序的主题并具有标准大小。现在我们正计划在我们的项目中使用SwiftUI。如何在SwiftUI结构中使用这些自定义控件? 即。 }

  • 问题内容: 我可以做一个静态列表 但是,如何从数组中动态生成元素列表?我尝试了以下操作,但出现错误: 包含控制流语句的闭包不能与函数生成器“ ViewBuilder”一起使用 有什么解决办法吗?我要完成的工作是一个列表,其中包含不是静态输入的动态元素集。 问题答案: 看起来答案与将我的视图包装在其中有关 这样,我现在可以从服务器获取视图数据并进行组合。而且,仅在需要时才实例化它们。

  • 问题内容: 我有大型CSV,我只对这些行的子集感兴趣。特别是,我想读取在满足特定条件之前发生的所有行。 例如,如果将产生数据框: 有什么方法可以读取csv中的所有行,直到col B超过10。在上面的示例中,我想读入: 我知道在读入数据帧后如何将这些行扔掉,但是到现在为止,我已经花了所有的计算来读入它们。在读取csv之前,我无法访问最后一行的索引请不要跳过脚) 问题答案: 您可以分批读取csv。由于