SwiftUI - 为不同的 Picker 使用 Enum 的所有值,但只有一个值
Posted
技术标签:
【中文标题】SwiftUI - 为不同的 Picker 使用 Enum 的所有值,但只有一个值【英文标题】:SwiftUI - using all but one values of Enum for different Pickers 【发布时间】:2021-04-28 14:40:30 【问题描述】:在我的应用程序中,我需要在两个不同的地方使用 Picker
。在一个地方,它将一个属性设置为三个值之一:A、B 或 C。但是,在另一个地方,我想使用选择器过滤一组项目以显示所有项目(无过滤器)或仅 A、B 或 C 项。
所以,我的第一次尝试是像这样创建一个enum
:
enum Category
case all
case A
case B
case C
使用它,我可以成功过滤我的列表。但是,当我尝试在数组中创建一个新项目时,我不希望 all
成为一个选项。用户应该只能看到带有 A、B 和 C 的选择器。
我可以试试:
enum Category
case A
case B
case C
但是我该如何添加过滤器选择器的all
选项?
(为了解决下面的一些问题,这里有一些关于我试图完成的具体事情的更多细节......
锦标赛可以是男子、女子或混合锦标赛。在我列出锦标赛的屏幕上,我希望能够显示所有锦标赛或仅显示男子锦标赛、仅显示女子锦标赛或仅显示混合锦标赛。所以,我有一个横跨 iPhone 宽度的选择器。外观和效果都很棒。
显然,当向列表中添加新项目时,我必须指定其类别,而不是“全部”。也是一个选择器。
因此,在一种情况下,我需要三个值,在另一种情况下,我需要相同的三个值,并在开头添加“All”。)
【问题讨论】:
【参考方案1】:您应该定义您的 enum
而不使用 all
大小写,因为 all
不是项目的有效 Category
。 (这是一个称为“make illegal states unrepresentable”的编程指南。)
enum Category: Hashable, CaseIterable
case a
case b
case c
使用该定义,用于设置项目属性的Picker
可以如下所示:
Picker("Category", selection: $category)
ForEach(Category.allCases, id: \.self) category in
Text(verbatim: "\(category)")
.tag(category)
那么您应该认识到您的过滤器是可选的。您可以按项目类别过滤,也可以不执行过滤。所以你的过滤器属性应该被声明为可选的:
@Binding var categoryFilter: Category?
用于设置过滤器的Picker
则需要小心使用可选标签:
Picker("Category Filter", selection: $categoryFilter)
Text("None")
.tag(Category?.none)
ForEach(Category.allCases, id: \.self) category in
Text(verbatim: "\(category)")
.tag(Category?.some(category))
【讨论】:
哦,这看起来也不错。那么,我可以将Text
项目添加到 Picker
?而且,当我选择“无”选项时,categoryFilter 将设置为 nil?
是的,您使用Text
将项目添加到Picker
。是的,如果您选择None
,它会将categoryFilter
设置为nil
。【参考方案2】:
你可以这样创建枚举
enum Category
case all
case A
case B
case C
static let categories: [Category] = [.A, .B, .C]
然后在需要从三个中选择时使用类别数组。
【讨论】:
这太完美了!快速、简单、有效。谢谢。【参考方案3】:您可以将枚举设为CaseIterable
,然后当您想显示所有枚举值时可以使用allCases
属性,当您只想显示某些情况时使用filter
。
注意:在我的示例代码中,您需要将 selection: .constant(Category.a)
替换为 @Binding
struct Test
enum Category: String, CaseIterable
case all = "All"
case a = "A"
case b = "B"
case c = "C"
struct TestView: View
var body: some View
VStack
Picker("Picker Title", selection: .constant(Category.a))
ForEach(Category.allCases, id: \.self) category in
Text(category.rawValue).tag(category)
Picker("Picker Title", selection: .constant(Category.a))
ForEach(Category.allCases.filter( $0 != .all ), id: \.self) category in
Text(category.rawValue).tag(category)
【讨论】:
将“all”排除在枚举之外,然后将 ForEach 置于 ["All"] + Category.allCases 之上,而不是过滤器不是更好吗? 通过阅读原帖but then how do I add the all option for the filter picker?
,有一种情况需要呈现“All”。就个人而言,从 UI 的角度来看,这可能不是允许用户选择这些“类别”的最佳方式。也许是可用类别(A、B、C)的列表和自动选择的“全选”按钮。
@Jack 我添加了一些关于我正在做的原因的更多细节。有了这些额外的信息,我的问题是否更有意义?
@Shadowrun 好问题。也许归结为 4 选项版本与 3 选项版本使用了多少次?
@rob-mayoff 是对的 - all 不是一个类别 - 您可以将 4 路枚举视为直接表示 UI 中的行,那么 All 将是一个那些。但是不要将这样的枚举称为“类别”,因为 All 不是类别,而且无论如何您都可能需要模型层中的 3 路类别枚举,因此与其有 2 个相似的枚举,不如代表您安排的行的模型与 Category.allCases +“所有”行【参考方案4】:
如果我理解正确,请回答您的问题。
修改你的枚举来实现 CaseIterable 看看下面。
enum Category:CaseIterable
case all
case A
case B
case C
过滤掉您的类别与所有情况匹配的位置。下面的例子。
let preferredcategory = Category.allCases.filter return $0 != Category.all
要测试我们的结果,请参见下面的代码。
print(preferredcategory.count)
for catname in preferredcategory
print("Hello, \(catname)!")
您可以在各个页面上阅读更多内容。
https://developer.apple.com/documentation/swift/caseiterable
https://developer.apple.com/documentation/swift/caseiterable/2994869-allcases
https://www.hackingwithswift.com/example-code/language/how-to-list-all-cases-in-an-enum-using-caseiterable
谢谢。
因此,您可以使用preferredCategory 变量向用户显示您不希望全部显示的类别。您还可以使用相同的过滤器来仅显示枚举中包含所有大小写的部分。
另外,好处是您可以保留枚举并在应用程序的任何地方重用它,而无需对值进行硬编码。如果你只想显示所有你只需要过滤掉剩余的情况。
有些人可能想在没有正当理由的情况下投反对票,但如果您认为该方法是错误的,请随时编辑或发表评论,说明如何改进解决方案。
【讨论】:
我确信这是解决问题的方法,无需在应用程序中的任何地方硬编码值,即使有人在没有正当理由的情况下投了反对票,我也不会感到难过。以上是关于SwiftUI - 为不同的 Picker 使用 Enum 的所有值,但只有一个值的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI Segmented Picker 元素为灰色且禁用
SwiftUI Picker,将 String 转换为 Double