关于swift:将所有枚举值作为数组获取

Get all enum values as an array

我有以下枚举。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
enum EstimateItemStatus: Printable {
    case Pending
    case OnHold
    case Done

    var description: String {
        switch self {
        case .Pending: return"Pending"
        case .OnHold: return"On Hold"
        case .Done: return"Done"
        }
    }

    init?(id : Int) {
        switch id {
        case 1:
            self = .Pending
        case 2:
            self = .OnHold
        case 3:
            self = .Done
        default:
            return nil
        }
    }
}

我需要将所有原始值作为字符串数组(如["Pending","On Hold","Done"])获取。

我将此方法添加到枚举中。

1
2
3
4
5
6
7
8
func toArray() -> [String] {
    var n = 1
    return Array(
        GeneratorOf<EstimateItemStatus> {
            return EstimateItemStatus(id: n++)!.description
        }
    )
}

但我得到以下错误。

找不到类型"Generatorof"的初始值设定项,该类型接受类型"(()->"

我不知道怎么解决这个问题。有什么帮助吗?或者请告诉我有没有更容易/更好/更优雅的方法来做这件事。

谢谢您。


对于Swift 4.2(Xcode 10)及更高版本

有一个CaseIterable协议:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enum EstimateItemStatus: String, CaseIterable {
    case pending ="Pending"
    case onHold ="OnHold"
    case done ="Done"

    init?(id : Int) {
        switch id {
        case 1: self = .pending
        case 2: self = .onHold
        case 3: self = .done
        default: return nil
        }
    }
}

for value in EstimateItemStatus.allCases {
    print(value)
}

斯威夫特<4.2

不,不能查询enum中包含的值。请参阅本文。必须定义一个列有所有值的数组。还可以查看Frank Valbuena的巧妙解决方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum EstimateItemStatus: String {
    case Pending ="Pending"
    case OnHold ="OnHold"
    case Done ="Done"

    static let allValues = [Pending, OnHold, Done]

    init?(id : Int) {
        switch id {
        case 1:
            self = .Pending
        case 2:
            self = .OnHold
        case 3:
            self = .Done
        default:
            return nil
        }
    }
}

for value in EstimateItemStatus.allValues {
    print(value)
}


Swift 4.2引入了一个名为CaseIterable的新协议。

1
2
3
enum Fruit : CaseIterable {
    case apple , apricot , orange, lemon
}

当您符合时,您可以从这样的enum案例中获得一个数组

1
2
3
for fruit in Fruit.allCases {
    print("I like eating \(fruit).")
}


还有另一种方法至少在编译时是安全的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
enum MyEnum {
    case case1
    case case2
    case case3
}

extension MyEnum {
    static var allValues: [MyEnum] {
        var allValues: [MyEnum] = []
        switch (MyEnum.case1) {
        case .case1: allValues.append(.case1); fallthrough
        case .case2: allValues.append(.case2); fallthrough
        case .case3: allValues.append(.case3)
        }
        return allValues
    }
}

请注意,这适用于任何枚举类型(rawrepresentable或not),并且如果您添加了一个新的案例,那么您将得到一个很好的编译器错误,因为这将强制您更新它。


我在某个地方找到了这个密码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protocol EnumCollection : Hashable {}


extension EnumCollection {

    static func cases() -> AnySequence<Self> {
        typealias S = Self
        return AnySequence { () -> AnyIterator<S> in
            var raw = 0
            return AnyIterator {
                let current : Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee }
                }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }
    }
}

用途:

1
2
3
enum YourEnum: EnumCollection { //code }

YourEnum.cases()

返回您的案例列表


将casiterable协议添加到枚举:

1
2
3
4
5
enum EstimateItemStatus: String, CaseIterable {
    case pending ="Pending"
    case onHold ="OnHold"
    case done ="Done"
}

用途:

1
2
let values: [String] = EstimateItemStatus.allCases.map { $0.rawValue }
//["Pending","OnHold","Done"]

要获取用于函数目的的列表,请使用返回数组的表达式EnumName.allCases,例如

1
EnumName.allCases.map{$0.rawValue}

会给你一个字符串列表

注:用allCases代替AllCases()


你可以使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum Status: Int{
    case a
    case b
    case c

}

extension RawRepresentable where Self.RawValue == Int {

    static var values: [Self] {
        var values: [Self] = []
        var index = 1
        while let element = self.init(rawValue: index) {
            values.append(element)
            index += 1
        }
        return values
    }
}


Status.values.forEach { (st) in
    print(st)
}


从一系列的尝试和几个小时的错误中获得灵感。我终于在Xcode 9.1上找到了这条舒适漂亮的四路快跑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
protocol EnumSequenceElement: Strideable {
    var rawValue: Int { get }
    init?(rawValue: Int)
}

extension EnumSequenceElement {
    func distance(to other: Self) -> Int {
        return other.rawValue - rawValue
    }

    func advanced(by n: Int) -> Self {
        return Self(rawValue: n + rawValue) ?? self
    }
}

struct EnumSequence<T: EnumSequenceElement>: Sequence, IteratorProtocol {
    typealias Element = T

    var current: Element? = T.init(rawValue: 0)

    mutating func next() -> Element? {
        defer {
            if let current = current {
                self.current = T.init(rawValue: current.rawValue + 1)
            }
        }
        return current
    }
}

用途:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum EstimateItemStatus: Int, EnumSequenceElement, CustomStringConvertible {
    case Pending
    case OnHold
    case Done

    var description: String {
        switch self {
        case .Pending:
            return"Pending"
        case .OnHold:
            return"On Hold"
        case .Done:
            return"Done"
        }
    }
}

for status in EnumSequence<EstimateItemStatus>() {
    print(status)
}
// Or by countable range iteration
for status: EstimateItemStatus in .Pending ... .Done {
    print(status)
}

输出:

1
2
3
Pending
On Hold
Done

斯威夫特2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Found http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type
func iterateEnum<T where T: Hashable, T: RawRepresentable>(_: T.Type) -> AnyGenerator<T> {
    var i = 0
    return AnyGenerator {
        let next = withUnsafePointer(&i) {
            UnsafePointer<T>($0).memory
        }
        if next.hashValue == i {
            i += 1
            return next
        } else {
            return nil
        }
    }
}

func arrayEnum<T where T: Hashable, T: RawRepresentable>(type: T.Type) -> [T]{
    return Array(iterateEnum(type))
}

使用它:

1
arrayEnum(MyEnumClass.self)


如果枚举是增量的并且与数字关联,则可以使用映射到枚举值的数字范围,如:

1
2
3
4
5
6
7
8
// Swift 3
enum EstimateItemStatus: Int {
    case pending = 1,
    onHold
    done
}

let estimateItemStatusValues: [EstimateItemStatus?] = (EstimateItemStatus.pending.rawValue...EstimateItemStatus.done.rawValue).map { EstimateItemStatus(rawValue: $0) }

这不太适用于与字符串或数字以外的任何事物相关联的枚举,但如果是这样的话,它会很好地工作!