SwiftUI 中的条件属性

Ben*_*eil 11 swift swiftui swift5.1

如何根据条件添加附加属性?

使用下面的代码,我得到了错误 Cannot assign value of type 'some View' (result of 'Self.overlay(_:alignment:)') to type 'some View' (result of 'Self.onTapGesture(count:perform:)')

import SwiftUI

struct ConditionalProperty: View {
    @State var overlay: Bool
    var body: some View {
        var view = Image(systemName: "photo")
            .resizable()
            .onTapGesture(count: 2, perform: self.tap)
        if self.overlay {
            view = view.overlay(Circle().foregroundColor(Color.red))
        }
        return view
    }

    func tap() {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

ccw*_*den 18

感谢这篇文章,我找到了一个界面非常简单的选项:

Text("Hello")
    .if(shouldBeRed) { $0.foregroundColor(.red) }
Run Code Online (Sandbox Code Playgroud)

由这个扩展启用:

extension View {
    @ViewBuilder
    func `if`<Transform: View>(_ condition: Bool, transform: (Self) -> Transform) -> some View {
        if condition { transform(self) }
        else { self }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一篇很棒的博客文章,其中包含有关条件修饰符的其他提示:https : //fivestars.blog/swiftui/conditional-modifiers.html


Dam*_*aux 13

Rob Mayoff已经很好地解释了为什么会出现这种行为并提出了两种解决方案(透明叠加或使用AnyView)。第三种更优雅的方法是使用 _ConditionalContent

一种简单的创建方法_ConditionalContentif else在组或堆栈中编写语句。这是使用组的示例:

import SwiftUI

struct ConditionalProperty: View {
    @State var overlay: Bool
    var body: some View {
        Group {
            if overlay {
                base.overlay(Circle().foregroundColor(Color.red))
            } else {
                base
            }
        }
    }
    
    var base: some View {
        Image("photo")
            .resizable()
            .onTapGesture(count: 2, perform: self.tap)
    }

    func tap() {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,默认情况下,SwiftUI 将视图的静态类型视为动画的标识符([Joe Groff 在此解释](https://forums.swift.org/t/why-is-swiftuis-view-a-pat /28029/13?u=mayoff))。在您的解决方案中,两个子视图(有/没有覆盖)因此被视为不相关的视图。如果应用动画,它在您的解决方案中的应用可能与在我的解决方案中的应用不同。这不一定是错误的,只是需要注意的事情。可以使用“.id”修饰符来解决这个问题。 (2认同)

rob*_*off 10

在 SwiftUI 术语中,您不是在添加属性。您正在添加修饰符。

这里的问题是,在 SwiftUI 中,每个修饰符都返回一个取决于它正在修改的内容的类型。

var view = Image(systemName: "photo")
    .resizable()
    .onTapGesture(count: 2, perform: self.tap)

// view has some deduced type like GestureModifier<SizeModifier<Image>>

if self.overlay {
    let newView = view.overlay(Circle().foregroundColor(Color.red))
    // newView has a different type than view, something like
    // OverlayModifier<GestureModifier<SizeModifier<Image>>>
}
Run Code Online (Sandbox Code Playgroud)

由于newView具有与 不同的类型view,因此您不能只分配view = newView.

解决此问题的一种方法是始终使用overlay修饰符,但在您不希望叠加层可见时使其透明:

var body: some View {
    return Image(systemName: "photo")
        .resizable()
        .onTapGesture(count: 2, perform: self.tap)
        .overlay(Circle().foregroundColor(overlay ? .red : .clear))
}
Run Code Online (Sandbox Code Playgroud)

处理它的另一种方法是使用类型橡皮擦AnyView

var body: some View {
    let view = Image(systemName: "photo")
        .resizable()
        .onTapGesture(count: 2, perform: self.tap)
    if overlay {
        return AnyView(view.overlay(Circle().foregroundColor(.red)))
    } else {
        return AnyView(view)
    }
}
Run Code Online (Sandbox Code Playgroud)

我从 Apple 工程师那里看到的建议是避免使用,AnyView因为它比使用完全类型化的视图效率低。例如,这条推文这条推文


vad*_*ian 5

if - else仅当您别无选择时才使用包括表达式的条件修饰符。

条件修饰符的巨大缺点是它们创建新视图,这可能会导致意外行为,例如它们会破坏动画。

几乎所有修饰符都接受不改变nil的值,因此如果可能,请始终使用类似的值

@State var overlay: Bool

var body: some View {
    Image(systemName: "photo")
        .resizable()
        .overlay(overlay ? Circle().foregroundColor(Color.red) : nil)
}
Run Code Online (Sandbox Code Playgroud)