我一次又一次地为此苦苦挣扎,所以我想我错过了一些东西。我需要做数学、进行设置、分配值或任何一系列简单操作来响应某些用户操作,例如此处显示的示例,而 SwiftUI 需要一个视图,而我不需要视图。必须有一种方法可以绕过 ViewBuilder 的规则。我通过创建一个不必要的视图并在视图的 init() 内执行我需要的代码来解决这个问题,但这看起来非常尴尬。
import SwiftUI
struct ContentView: View
{
@State var showStuff = false
var body: some View
{
VStack
{
Toggle(isOn: $showStuff)
{
Text("Label")
}
if showStuff
{
UserDefaults.standard.set(true, forKey: "Something")
}
}
}
}
Run Code Online (Sandbox Code Playgroud) 我正在尝试创建一个简单的struct,它接受一组视图并返回一个VStack包含这些视图的普通视图,除了它们都是对角堆叠的。
代码:
struct LeaningTower<Content: View>: View {
var views: [Content]
var body: some View {
VStack {
ForEach(0..<views.count) { index in
self.views[index]
.offset(x: CGFloat(index * 30))
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在这很好用,但每当我不得不调用它时,我总是很生气:
LeaningTower(views: [Text("Something 1"), Text("Something 2"), Text("Something 3")])
Run Code Online (Sandbox Code Playgroud)
在这样的数组中列出视图对我来说似乎非常奇怪,所以我想知道是否有一种方法可以像这样@ViewBuilder调用我的LeaningTower结构:
LeaningTower { // Same way how you would create a regular VStack
Text("Something 1")
Text("Something 2")
Text("Something 3")
// And then have all of these Text's in my 'views' array
}
Run Code Online (Sandbox Code Playgroud)
如果有一种方法可以@ViewBuilder …
所以我试图创建一个视图,它接受 viewBuilder 内容,循环内容的视图并在每个视图和另一个视图之间添加分隔符
struct BoxWithDividerView<Content: View>: View {
let content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
var body: some View {
VStack(alignment: .center, spacing: 0) {
// here
}
.background(Color.black)
.cornerRadius(14)
}
}
Run Code Online (Sandbox Code Playgroud)
所以在我写“here”的地方,如果有意义的话,我想遍历内容的视图。我将编写一个不起作用的代码,但它解释了我想要实现的目标:
ForEach(content.subviews) { view in
view
Divider()
}
Run Code Online (Sandbox Code Playgroud)
怎么做?
我的好奇心促使我将View类型作为参数传递给@ViewBuilder. 将模型/基元类型作为参数传递@ViewBuilder是完全有效的。
如下代码所示。
struct TestView<Content: View>: View {
let content: (String) -> Content
init(@ViewBuilder content: @escaping (String) -> Content) {
self.content = content
}
var body: some View {
content("Some text")
}
}
struct ContentTestView: View {
var body: some View {
TestView {
Text("\($0)")
}
}
}
Run Code Online (Sandbox Code Playgroud)
代替String在
let content: (String) -> Content
Run Code Online (Sandbox Code Playgroud)
如果我尝试传递 SwiftUIView类型,则编译器对此不满意。
let content: (View) -> Content
Run Code Online (Sandbox Code Playgroud)
尽管 params for@ViewBuilder接受自定义协议类型Searchable,但不接受View协议。 …
在 SwiftUI 中,您可以将多个视图作为一个参数传递给另一个视图,这要归功于@ViewBuilder. 每个子视图都是单独处理的,因此在传递后,可以包含在 等中VStack。HStack
我的问题是:这个容器视图是否可以单独修改传递给它的每个子视图?例如,通过Spacer在每个子视图之间添加一个。
这是我正在寻找的等效功能:
struct SomeContainerView<Content: View> {
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content()
}
let content: Content
var body: some View {
HStack {
ForEach(content.childViews, id: \.id) { child in
// Give each child view a background
// Add a Spacer in between each child view
// Conditional formatting
// Etc.
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我相信这是可能的,因为标准 SwiftUI 容器视图可以影响传递给它们的子视图的外观和布局。除非这是通过 SwiftUI 的内部功能实现的?
解决这个问题的一种方法可能是传递多个视图参数而不是一个,尽管如果需要传递许多视图,这会不灵活且混乱。视图数组似乎也没有意义,因为我在 SwiftUI 中的任何地方都没有注意到这种模式。
代码:
import SwiftUI
public struct Snackbar<Content>: View where Content: View {
private var content: Content
// Works OK
public init(@ViewBuilder content: () -> Content) {
self.content = content()
}
init(_ text: String) {
self.init {
Text(text) // cannot convert value of type 'Text' to closure result type 'Content'
.font(.subheadline)
.foregroundColor(.white)
.multilineTextAlignment(.leading)
}
}
public var body: some View {
HStack {
VStack(alignment: .leading, spacing: 4) {
content
}
Spacer()
}
.frame(maxWidth: .infinity,
minHeight: 26)
.padding(.fullPadding)
.background(Color.black)
.clipShape(RoundedRectangle(cornerRadius: .defaultCornerRadius))
.shadow(color: Color.black.opacity(0.125), …Run Code Online (Sandbox Code Playgroud) 我制作了一个按钮样式来在我的应用程序中创建圆形同形按钮,如下所示:
struct NeumorphicCircleButtonStyle: ButtonStyle {
var startPoint: UnitPoint
var endPoint: UnitPoint
var padding: CGFloat?
var bgColor: Color?
var bgColorOffset: Color?
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(padding ?? 30)
.contentShape(Circle())
.background(
Group{
if configuration.isPressed {
Circle()
.fill(bgColor ?? Color.backgroundColor)
.overlay(
Circle()
.stroke(Color.black.opacity(0.7), lineWidth: 4)
.blur(radius: 4)
.offset(x: 2, y: 2)
.mask(Circle().fill(LinearGradient(colors: [Color.black, Color.clear], startPoint: startPoint, endPoint: endPoint)))
)
.overlay(
Circle()
.stroke(bgColorOffset ?? Color.backgroundColorOffset, lineWidth: 8)
.blur(radius: 4)
.offset(x: -2, y: -2)
.mask(Circle().fill(LinearGradient(colors: [Color.clear, Color.black], startPoint: startPoint, endPoint: endPoint))) …Run Code Online (Sandbox Code Playgroud) 我正在尝试使用 SwiftUI@ViewBuilder来允许我的包的用户动态指定视图的正文内容。但是,我试图将可用作输入的可能视图限制为我实现的几个默认视图(出于简化目的)。
这是一个例子:
public protocol PopupBodyView: View { } // every one of my default views conforms to this protocol
func generateChildView(@ViewBuilder items: () -> PopupBodyView)
-> some PopupBodyView {
return menuItems()
}
Run Code Online (Sandbox Code Playgroud)
如果我现在调用generateChildView,我可以正确指定协议类型的一种PopupBodyView视图- 但是,如果我在结果生成器主体中指定多个视图,则会收到以下错误:
Instance method 'contextMenu(items:)' requires that 'TupleView<(Text, Text)>' conform to 'PopupBodyView'
extension Text: PopupBodyView { } // add protocol conformance
contextMenu {
Text("Test")
Text("Test") // Instance method 'contextMenu(items:)' requires that 'TupleView<(Text, Text)>' conform to 'PopupBodyView'
}
Run Code Online (Sandbox Code Playgroud)
我现在如何调整默认值@ViewBuilder以仅接受符合我的自定义 …
我已经研究了几天,搜索了 Swift 和 SwiftUI 文档、SO、论坛等,但似乎找不到答案。
这是问题所在;
我有一个 SwiftUI 自定义视图,它对远程资源的自定义 API 请求类进行一些状态确定。View 处理显示加载状态和失败状态,以及它的主体内容通过 ViewBuilder 传递,这样如果来自 API 的状态成功并且资源数据被加载,它将显示页面的内容。
问题是,当子类 ObservedObject 更新时,ViewBuilder 内容不会重新渲染。对象更新以响应 UI(当按下按钮时等),但 UI 永远不会重新渲染/更新以反映子类 ObservedObject 内的更改,例如,子类 ObservedObject 中数组后面的 ForEach 在以下情况下不会刷新数组内容改变。如果我将它移出自定义视图,ForEach 将按预期工作。
我可以确认代码编译并运行。Observers 和debugPrint()'s 始终显示ApiObject正在正确更新状态,并且视图ApiState完全正确地反映了更改。它只是ContentViewBuilder 的。我假设是因为 ViewBuilder 只会被调用一次。
编辑:上面的段落应该是提示,ApiState更新正确,但是在将大量日志记录到应用程序之后,UI 没有监听子类 ObservedObject 的发布。属性在变化,状态也在变化,但 UI 并没有对其做出反应。另外,下一句被证明是错误的,我在 VStack 中再次测试,组件仍然没有重新渲染,这意味着我找错了地方!
如果是这种情况,VStack其他此类元素如何解决此问题?还是因为我ApiObjectView在状态更改时被重新渲染,导致子视图“重置”?尽管在这种情况下,我希望它能够接受新数据并按预期工作,但它永远不会重新渲染。
有问题的代码是在CustomDataList.swift和ApiObjectView.swift下方。我已经发表评论指出正确的方向。
这是示例代码;
// ApiState.swift
// Stores the API state for where the request and data parse …Run Code Online (Sandbox Code Playgroud) swiftui ×9
viewbuilder ×9
ios ×6
swift ×6
arrays ×1
custom-view ×1
generics ×1
initializer ×1
struct ×1
xcode ×1