简体   繁体   English

Swift:如何访问枚举的变量元素?

[英]Swift: How to access variable element of an enum?

For hours I've been struggeling with getting an variable element of an enum. 几个小时以来,我一直在努力获取枚举的可变元素。

The " Swifticons " - pod provides me with the following enum: Swifticons-pod为我提供了以下枚举:

public enum WeatherType: Int {
    static var count: Int {
        return weatherIcons.count
    }

    public var text: String? {
        return weatherIcons[rawValue]
    }

    case alien, barometer, celsius, owm300, owm301, owm302, and200moreOfTheseNames
}

private let weatherIcons = ["\u{f075}", "\u{f079}", and202moreOfTheseFontCharacters]

From an external API ( openWeatherMap.org ) I just get an weather code (let's say "300") - and I want to access Icon "owm300". 从一个外部API( openWeatherMap.org ),我仅获得一个天气代码(比如说“ 300”),并且我想访问图标“ owm300”。

But how do I access this element of the enum without knowing the rawValue (which would be - say - 198)? 但是,如何在不知道rawValue的情况下访问枚举的此元素(即-198)?

One of the easiest way I can think of is create some kind of mapping dictionary, where you would keep track of weather response code and WeatherType that it maps to like so, 我想到的最简单的方法之一就是创建某种映射字典,在其中您可以跟踪天气响应代码和它映射到的WeatherType,

let weatherCodeMapping: [Int: WeatherType] = [300: .owm300,
                                              301: .owm301,
                                              302: .owm302]

With this in place, you dont need to know any specific rawValue, you can simply get code by, 有了这个,您就不需要知道任何特定的rawValue,只需通过以下方式获取代码,

 let weatherType = weatherCodeMapping[weatherCode]

And then create some other mapping for your image based on the weatherType. 然后根据weatherType为图像创建其他映射。

let weatherIcon = weatherIconMapping[weatherType]

or create a single mapping directly from weather code to icon. 或直接从天气代码到图标创建单个映射。

Here's the plan: 这是计划:

  1. We need to enumerate all of the enum cases. 我们需要列举所有枚举案例。 We'll do that by iterating over raw values (luckily, WeatherType is backed by Int ). 我们将通过遍历原始值来做到这一点(幸运的是, WeatherTypeInt支持)。
  2. We will store lazily initialized dictionary that maps String to WeatherType . 我们将存储延迟初始化的字典,该字典将String映射到WeatherType
  3. And finally, we declare a static function that returns an optional WeatherType? 最后,我们声明一个静态函数,该函数返回一个可选的WeatherType? because we can encounter an unknown value. 因为我们会遇到一个未知值。

Here's the code: 这是代码:

extension WeatherType {
  // just some little convenience
  private typealias W = WeatherType

  // 1. define the sequence of all cases
  private static func allCases() -> AnySequence<W> {
    return AnySequence { () -> AnyIterator<W> in
      var raw = 0
      return AnyIterator {
        // Iterates while raw value can be converted to an enum case
        if let next = W(rawValue: raw) {
          raw += 1
          return next
        }
        return nil
      }
    }
  }

  // 2. Static properties are lazy so we'll use them to store the dictionary with String to WeatherType mapping
  private static let typeMap = W.allCases().reduce([String: W]()) { acc, next in
    var acc = acc
    acc[String(describing: next)] = next
    return acc
  }

  // 3. Declare the mapping function
  static func from(string: String) -> WeatherType? {
    return W.typeMap[string]
  }
}

Here's a little test: 这是一个小测试:

let str = "301"
let type = WeatherType.from(string: "owm\(str)")
print(type == .owm301)

Swift doesn't currently have enumerable sequences of enum cases. Swift当前没有枚举实例的可枚举序列。 One option that you have is to copy the list of icon names , then search for your icon's name, and use that index as the enum's rawValue: 您拥有的一种选择是复制图标名称列表 ,然后搜索图标的名称,并将该索引用作枚举的rawValue:

let weatherIcons = [...]
let iconName = "owm300"
let possibleIconIndex = weatherIcons.index {
    $0.caseInsensitiveCompare(iconName) == .orderedSame
}
if let iconIndex = possibleIconIndex {
    let weatherIcon = WeatherIcon(rawValue: iconIndex)!
    // ...
} else {
    // graceful fallback for when the weather icon is missing
}

Of course, you need to figure out your own mapping between the data you get from the service and enum names, but that could be as simple as "owm\\(weatherCode)" . 当然,您需要弄清楚从服务获取的数据和枚举名称之间的映射关系,但这可能很简单,例如"owm\\(weatherCode)"

When Swift 4.2 lands, you will be able to make your enums conform to a new protocol called CaseIterable . 当Swift 4.2登陆时,您将能够使您的枚举符合一个名为CaseIterable的新协议。 Enums that conform to it get a synthesized implementation of an allCases static variable. 符合它的枚举将获得allCases静态变量的综合实现。 You will then be able to use that enumeration to build a string-to-enum dictionary automatically: 然后,您将可以使用该枚举自动构建字符串到枚举的字典:

let nameToEnum = WeatherIcon.allCases.map { (String($0), $0) }
let mapping = Dictionary(uniqueKeysWithValues: nameToEnum)

That will however require WeatherIcon to be declared with the CaseEnumerable conformance, as adding it with an extension has no effect. 但是,这将要求WeatherIcon 声明CaseEnumerable一致性,因为添加extension不会起作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM