在 SwiftUI 中动态隐藏视图
Dynamically hiding view in SwiftUI
我正在尝试有条件地在 SwiftUI 中隐藏 DatePicker
。但是,我遇到了类型不匹配的问题:
var datePicker = DatePicker($datePickerDate)
if self.showDatePicker {
datePicker = datePicker.hidden()
}
在这种情况下,datePicker
是 DatePicker<EmptyView>
类型,而 datePicker.hidden()
是 _ModifiedContent<DatePicker<EmptyView>, _HiddenModifier>
类型。所以我无法将 datePicker.hidden()
分配给 datePicker
。我已经尝试过这种方法的变体,但似乎找不到可行的方法。有什么想法吗?
更新
您可以解包 _ModifiedContent
类型以使用它的 content
属性 获取基础类型。但是,这并没有解决根本问题。 content
属性 似乎只是原始的、未修改的日期选择器。
我发现我可以通过这种方式隐藏或显示日期选择器,而不是动态设置变量并在我的视图中使用它:
struct ContentView : View {
@State var showDatePicker = true
@State var datePickerDate: Date = Date()
var body: some View {
VStack {
if self.showDatePicker {
DatePicker($datePickerDate)
} else {
DatePicker($datePickerDate).hidden()
}
}
}
}
或者,可选地,不包括日期选择器而不是隐藏它:
struct ContentView : View {
@State var showDatePicker = true
@State var datePickerDate: Date = Date()
var body: some View {
VStack {
if self.showDatePicker {
DatePicker($datePickerDate)
}
}
}
}
✅ 正确最简单的方法:
您可以改为设置 alpha,这也会保留视图的布局 space,并且不会像其他答案那样强制您添加虚拟视图:
.opacity(isHidden ? 0 : 1)
演示
更清洁的方式! - 扩展原始 hidden
修饰符:
此外,您可以实现自定义函数以获取可见性状态作为参数:
extension View {
func hidden(_ shouldHide: Bool) -> some View {
opacity(shouldHide ? 0 : 1)
}
}
现在只需将 bool
传递给修饰符:
DatePicker($datePickerDate)
.hidden(showDatePicker)
请注意, 与 hidden
修饰符的原始行为不同,这两种方法都保留了隐藏视图的框架。
⛔️不要用不好的做法!!!
所有其他答案(包括@Jake 接受的答案)使用分支而不是导致性能下降的依赖代码。
分支示例:
✅ 依赖代码示例:
返回 logical SAME view 导致 SwiftUI 渲染引擎重新渲染并再次初始化视图并导致性能命中! (查看更多信息 this WWDC session)
按住 Command 并单击有问题的视图,然后 select Beta 5 中的 Make Conditional 选项。我在我的一个视图 (LiftsCollectionView) 上执行了此操作,并生成了以下内容:
if suggestedLayout.size.height > 150 {
LiftsCollectionView()
} else {
EmptyView()
}
隐藏视图的最简单和最常用的方法如下:
struct ContentView: View {
@State private var showText = true
var body: some View {
VStack {
Button("Toggle text") {
showText.toggle()
}
if showText {
Text("Hello World!")
}
}
}
}
这会在 showText
等于 false
时从层次结构中删除 Text
视图。如果您希望有一个选项来保留 space 或希望将其作为修饰符,请参见下文。
我创建了一个扩展,所以你可以使用修饰符,像这样隐藏视图:
Text("Hello World!")
.isHidden(true)
或完全删除:
Text("Label")
.isHidden(true, remove: true)
如果您想使用 Swift 软件包,下面的扩展也可以在 GitHub 上获得:GeorgeElsham/HidingViews。
下面是创建 View
修饰符的代码:
我建议您在自己的文件中使用此代码(记得 import SwiftUI
):
extension View {
/// Hide or show the view based on a boolean value.
///
/// Example for visibility:
///
/// Text("Label")
/// .isHidden(true)
///
/// Example for complete removal:
///
/// Text("Label")
/// .isHidden(true, remove: true)
///
/// - Parameters:
/// - hidden: Set to `false` to show the view. Set to `true` to hide the view.
/// - remove: Boolean value indicating whether or not to remove the view.
@ViewBuilder func isHidden(_ hidden: Bool, remove: Bool = false) -> some View {
if hidden {
if !remove {
self.hidden()
}
} else {
self
}
}
}
您还可以在任何 View
:
上使用 opacity
修饰符
ActivityIndicator(tint: .black)
.opacity(self.isLoading ? 1.0 : 0.0)
编辑 2021 年 11 月 4 日
我现在更喜欢另一种方法,而不是我原来的答案(如下)中的方法:
有两种可能的解决方案,具体取决于您是要保留原始 space 还是让其他视图占用隐藏视图的 space。
保持 space
DatePicker("Choose date", selection: $datePickerDate)
.opacity(showDatePicker ? 1 : 0)
即使我们在这里只调整不透明度,触摸 DatePicker
隐藏时应该出现的 space 也不会打开日历。
不要保留 space
if showDatePicker {
DatePicker("Choose date", selection: $datePickerDate)
}
原回答
对于将来需要它的人,我创建了一个 ViewModifier
,它将 Bool
作为参数,因此您可以绑定一个布尔值以通过设置您的 showDatePicker: Bool
变量。
所有代码片段都需要 import SwiftUI
.
ViewModifier
:
struct Show: ViewModifier {
let isVisible: Bool
@ViewBuilder
func body(content: Content) -> some View {
if isVisible {
content
} else {
content.hidden()
}
}
}
函数:
extension View {
func show(isVisible: Bool) -> some View {
ModifiedContent(content: self, modifier: Show(isVisible: isVisible))
}
}
你可以这样使用它:
var datePicker = DatePicker($datePickerDate)
.show(isVisible: showDatePicker)
这是 Show/Hide 在 SwiftUI 中查看的简单方法。
添加@State
变量:
@State var showLogo = false
添加条件如下:
VStack {
if showLogo == true {
Image(systemName: "house.fill")
.resizable()
.frame(width: 100, height: 100, alignment: .center)
.foregroundColor(Color("LightGreyFont"))
.padding(.bottom, 20)
}
Text("Real State App")
.font(Font.custom("Montserrat-Regular", size: 30))
}.padding(.vertical, 25)
将 @State 变量的状态更改为 Show/Hide 如下所示的视图:
Button(action: {
withAnimation{
self.showLogo.toggle()
}
}, label: {
Text("Login").font(.system(size: 20, weight: .medium, design: .default))
.frame(minWidth: 0, maxWidth: .infinity, maxHeight: 50)
.foregroundColor(Color("BlackFont"))
.cornerRadius(10)
})
即使没有占位符视图或调用 hidden (iOS13.1 and Swift 5)
struct Foo: View {
@State var condition: Bool
var body: some View {
if self.condition {
Text("Hello")
}
}
}
如果不查看 @ViewBuilder
实现很难准确知道,但在评估条件时,如果默认情况下失败,我们似乎会得到一个 EmptyView
。
所以这相当于这里的一些答案,但更简单。
以下自定义修饰符的工作方式与 .hidden() 一样,既隐藏视图又禁止与其交互。
ViewModifier 和 View 扩展函数 -
import SwiftUI
fileprivate struct HiddenIfModifier: ViewModifier {
var isHidden: Bool
init(condition: Bool) {
self.isHidden = condition
}
func body(content: Content) -> some View {
content
// Conditionally changing the parameters of modifiers
// is more efficient than conditionally applying a modifier
// (as in Cristina's ViewModifier implementation).
.opacity(isHidden ? 0 : 1)
.disabled(isHidden)
}
}
extension View {
/// Hides a view conditionally.
/// - Parameters:
/// - condition: Decides if `View` is hidden.
/// - Returns: The `View`, hidden if `condition` is `true`.
func hidden(if condition: Bool) -> some View {
modifier(HiddenIfModifier(condition: condition))
}
}
使用-
DatePicker($datePickerDate)
.hidden(if: !self.showDatePicker)
注-
有条件地应用修饰符是低效的,因为 swift 将未修改和修改的视图视为不同的类型。这会导致视图(及其状态)在每次条件更改时被销毁和重建。对于像 List 这样的数据密集型视图,这可能会成为一个问题。有条件地更改修饰符的参数不会导致此问题。
我正在尝试有条件地在 SwiftUI 中隐藏 DatePicker
。但是,我遇到了类型不匹配的问题:
var datePicker = DatePicker($datePickerDate)
if self.showDatePicker {
datePicker = datePicker.hidden()
}
在这种情况下,datePicker
是 DatePicker<EmptyView>
类型,而 datePicker.hidden()
是 _ModifiedContent<DatePicker<EmptyView>, _HiddenModifier>
类型。所以我无法将 datePicker.hidden()
分配给 datePicker
。我已经尝试过这种方法的变体,但似乎找不到可行的方法。有什么想法吗?
更新
您可以解包 _ModifiedContent
类型以使用它的 content
属性 获取基础类型。但是,这并没有解决根本问题。 content
属性 似乎只是原始的、未修改的日期选择器。
我发现我可以通过这种方式隐藏或显示日期选择器,而不是动态设置变量并在我的视图中使用它:
struct ContentView : View {
@State var showDatePicker = true
@State var datePickerDate: Date = Date()
var body: some View {
VStack {
if self.showDatePicker {
DatePicker($datePickerDate)
} else {
DatePicker($datePickerDate).hidden()
}
}
}
}
或者,可选地,不包括日期选择器而不是隐藏它:
struct ContentView : View {
@State var showDatePicker = true
@State var datePickerDate: Date = Date()
var body: some View {
VStack {
if self.showDatePicker {
DatePicker($datePickerDate)
}
}
}
}
✅ 正确最简单的方法:
您可以改为设置 alpha,这也会保留视图的布局 space,并且不会像其他答案那样强制您添加虚拟视图:
.opacity(isHidden ? 0 : 1)
演示
更清洁的方式! - 扩展原始 hidden
修饰符:
此外,您可以实现自定义函数以获取可见性状态作为参数:
extension View {
func hidden(_ shouldHide: Bool) -> some View {
opacity(shouldHide ? 0 : 1)
}
}
现在只需将 bool
传递给修饰符:
DatePicker($datePickerDate)
.hidden(showDatePicker)
请注意, 与 hidden
修饰符的原始行为不同,这两种方法都保留了隐藏视图的框架。
⛔️不要用不好的做法!!!
所有其他答案(包括@Jake 接受的答案)使用分支而不是导致性能下降的依赖代码。
分支示例:
✅ 依赖代码示例:
返回 logical SAME view 导致 SwiftUI 渲染引擎重新渲染并再次初始化视图并导致性能命中! (查看更多信息 this WWDC session)
按住 Command 并单击有问题的视图,然后 select Beta 5 中的 Make Conditional 选项。我在我的一个视图 (LiftsCollectionView) 上执行了此操作,并生成了以下内容:
if suggestedLayout.size.height > 150 {
LiftsCollectionView()
} else {
EmptyView()
}
隐藏视图的最简单和最常用的方法如下:
struct ContentView: View {
@State private var showText = true
var body: some View {
VStack {
Button("Toggle text") {
showText.toggle()
}
if showText {
Text("Hello World!")
}
}
}
}
这会在 showText
等于 false
时从层次结构中删除 Text
视图。如果您希望有一个选项来保留 space 或希望将其作为修饰符,请参见下文。
我创建了一个扩展,所以你可以使用修饰符,像这样隐藏视图:
Text("Hello World!")
.isHidden(true)
或完全删除:
Text("Label")
.isHidden(true, remove: true)
如果您想使用 Swift 软件包,下面的扩展也可以在 GitHub 上获得:GeorgeElsham/HidingViews。
下面是创建 View
修饰符的代码:
我建议您在自己的文件中使用此代码(记得 import SwiftUI
):
extension View {
/// Hide or show the view based on a boolean value.
///
/// Example for visibility:
///
/// Text("Label")
/// .isHidden(true)
///
/// Example for complete removal:
///
/// Text("Label")
/// .isHidden(true, remove: true)
///
/// - Parameters:
/// - hidden: Set to `false` to show the view. Set to `true` to hide the view.
/// - remove: Boolean value indicating whether or not to remove the view.
@ViewBuilder func isHidden(_ hidden: Bool, remove: Bool = false) -> some View {
if hidden {
if !remove {
self.hidden()
}
} else {
self
}
}
}
您还可以在任何 View
:
opacity
修饰符
ActivityIndicator(tint: .black)
.opacity(self.isLoading ? 1.0 : 0.0)
编辑 2021 年 11 月 4 日
我现在更喜欢另一种方法,而不是我原来的答案(如下)中的方法:
有两种可能的解决方案,具体取决于您是要保留原始 space 还是让其他视图占用隐藏视图的 space。
保持 space
DatePicker("Choose date", selection: $datePickerDate)
.opacity(showDatePicker ? 1 : 0)
即使我们在这里只调整不透明度,触摸 DatePicker
隐藏时应该出现的 space 也不会打开日历。
不要保留 space
if showDatePicker {
DatePicker("Choose date", selection: $datePickerDate)
}
原回答
对于将来需要它的人,我创建了一个 ViewModifier
,它将 Bool
作为参数,因此您可以绑定一个布尔值以通过设置您的 showDatePicker: Bool
变量。
所有代码片段都需要 import SwiftUI
.
ViewModifier
:
struct Show: ViewModifier {
let isVisible: Bool
@ViewBuilder
func body(content: Content) -> some View {
if isVisible {
content
} else {
content.hidden()
}
}
}
函数:
extension View {
func show(isVisible: Bool) -> some View {
ModifiedContent(content: self, modifier: Show(isVisible: isVisible))
}
}
你可以这样使用它:
var datePicker = DatePicker($datePickerDate)
.show(isVisible: showDatePicker)
这是 Show/Hide 在 SwiftUI 中查看的简单方法。
添加
@State
变量:@State var showLogo = false
添加条件如下:
VStack { if showLogo == true { Image(systemName: "house.fill") .resizable() .frame(width: 100, height: 100, alignment: .center) .foregroundColor(Color("LightGreyFont")) .padding(.bottom, 20) } Text("Real State App") .font(Font.custom("Montserrat-Regular", size: 30)) }.padding(.vertical, 25)
将 @State 变量的状态更改为 Show/Hide 如下所示的视图:
Button(action: { withAnimation{ self.showLogo.toggle() } }, label: { Text("Login").font(.system(size: 20, weight: .medium, design: .default)) .frame(minWidth: 0, maxWidth: .infinity, maxHeight: 50) .foregroundColor(Color("BlackFont")) .cornerRadius(10) })
即使没有占位符视图或调用 hidden (iOS13.1 and Swift 5)
struct Foo: View {
@State var condition: Bool
var body: some View {
if self.condition {
Text("Hello")
}
}
}
如果不查看 @ViewBuilder
实现很难准确知道,但在评估条件时,如果默认情况下失败,我们似乎会得到一个 EmptyView
。
所以这相当于这里的一些答案,但更简单。
以下自定义修饰符的工作方式与 .hidden() 一样,既隐藏视图又禁止与其交互。
ViewModifier 和 View 扩展函数 -
import SwiftUI
fileprivate struct HiddenIfModifier: ViewModifier {
var isHidden: Bool
init(condition: Bool) {
self.isHidden = condition
}
func body(content: Content) -> some View {
content
// Conditionally changing the parameters of modifiers
// is more efficient than conditionally applying a modifier
// (as in Cristina's ViewModifier implementation).
.opacity(isHidden ? 0 : 1)
.disabled(isHidden)
}
}
extension View {
/// Hides a view conditionally.
/// - Parameters:
/// - condition: Decides if `View` is hidden.
/// - Returns: The `View`, hidden if `condition` is `true`.
func hidden(if condition: Bool) -> some View {
modifier(HiddenIfModifier(condition: condition))
}
}
使用-
DatePicker($datePickerDate)
.hidden(if: !self.showDatePicker)
注- 有条件地应用修饰符是低效的,因为 swift 将未修改和修改的视图视为不同的类型。这会导致视图(及其状态)在每次条件更改时被销毁和重建。对于像 List 这样的数据密集型视图,这可能会成为一个问题。有条件地更改修饰符的参数不会导致此问题。