【问题标题】:Determine user's "Temperature Unit" setting on iOS 10 (Celsius / Fahrenheit)在 iOS 10 上确定用户的“温度单位”设置(摄氏度/华氏度)
【发布时间】:2017-02-05 05:28:10
【问题描述】:

iOS 10 增加了用户在“设置”>“通用”>“语言和区域”>“温度单位”下设置“温度单位”选项的功能。

我的应用如何以编程方式确定此设置,以便显示正确的温度单位?我翻遍了 NSLocale.h 并没有看到任何相关内容。

(在 iOS 10 之前,测试语言环境是否使用公制并假设公制用户想要使用摄氏度就足够了。现在不再是这种情况了。)

【问题讨论】:

  • 让我们从使用此设置的位置开始。我玩过 NSDimensionFormatter 类来格式化温度维度,它似乎不支持“设置”应用程序中的“温度单位”设置。您是否发现任何根据此设置更改显示的内容?

标签: ios locale ios10


【解决方案1】:

我最终做了一些类似于 Amir 但更强大的事情,即 Amir 的解决方案有时对我来说是失败的。

我没有通过Self(symbol:) 实例化,而是根据三个选项匹配格式化时返回的符号:

extension UnitTemperature {
  static var current: UnitTemperature {
    let formatter = MeasurementFormatter()
    formatter.locale = Locale.autoupdatingCurrent
    formatter.unitStyle = .medium
    let formatted = formatter.string(from: .init(value: 0, unit: UnitTemperature.celsius))
    let symbol = String(formatted.suffix(2))
    switch (symbol) {
      case UnitTemperature.celsius.symbol:
        return .celsius
      case UnitTemperature.fahrenheit.symbol:
        return .fahrenheit
      default:
        return .kelvin
    }
  }
}

【讨论】:

    【解决方案2】:

    正如其他人提到的,如果您需要知道当前单位,安全的方法是直接使用MeasurementFormatter 进行格式化!我做了这个扩展,它适用于 Swift 5 和更新版本:

    extension UnitTemperature {
        static var current: Self {
            let formatter = MeasurementFormatter()
            formatter.locale = .init(identifier: "en_US")
            formatter.unitStyle = .long
            let formatted = formatter.string(from: .init(value: 0, unit: Self.celsius))
            return Self(symbol: formatted.components(separatedBy: " ").last!)
        }
    }
    

    你可以像这样使用它:

    UnitTemperature.current
    

    注意:它可能无法在模拟器上运行,它可以在设备上运行

    【讨论】:

      【解决方案3】:

      正确答案是从Fabio's commentpedrouan's answer,参考了苹果在论坛上的回复:https://forums.developer.apple.com/thread/70258

      模拟器不遵守温度单位设置,但真实设备确实遵守该设置。在模拟器中使用MeasurementFormatter 将始终使用区域设置的默认单位,但在真实设备上使用MeasurementFormatter 将使用用户选择的温度单位。

      【讨论】:

      • 这简直是一团糟,Apple 没有人清理它。嘘他们。我需要知道用户将其设置为哪个单位。不是用于屏幕格式化目的,而是用于其他处理目的。没有办法做到这一点。除了用measurementformatter转换一些已知值,并根据所有语言的已知字符串(例如'C')检查它。多么失败。
      【解决方案4】:

      我为UnitTemperature 编写了一个小扩展,以便根据Fábio Oliveira 的调查结果选择单元。我的用例是知道用户选择了哪个单位,不一定用它来显示某些东西,我就是这样做的。

      extension UnitTemperature {
        static var current: UnitTemperature {
          let measureFormatter = MeasurementFormatter()
          let measurement = Measurement(value: 0, unit: UnitTemperature.celsius)
          let output = measureFormatter.string(from: measurement)
          return output == "0°C" ? .celsius : .fahrenheit
        }
      }
      

      同样,请记住使用实际设备进行测试,而不是模拟器。

      【讨论】:

      • 如果系统语言未设置为英语,这仍然有效吗?
      • 开尔文呢?
      • @Ixx 在 iOS 中您只能将温度单位设置为摄氏度或华氏度
      • 没有? Measurement(value: 0, unit: UnitTemperature.kelvin)
      • 您是对的,我的意思是作为用户,您只能在设置应用程序中将温度单位设置为摄氏度或华氏度。所以不必担心 Kelvin 是用户选择的可能值之一,因为它根本不是一个选项。
      【解决方案5】:

      这似乎是一个迟到的答案,但我在这里的 cmets 中找到了 @fábio-oliveira 提到的一个巧妙的解决方案。

      苹果原帖:https://forums.developer.apple.com/thread/70258

      我在我的解决方案中尝试做的是将开尔文温度(从 JSON 文件解析,可以是任何温度)转换为由 Systems Locale规定的温度格式的功能> 或由用户在设置 > 常规 > 语言和区域 > 温度单位中设置。但是,我还需要温度在小数点后显示 0 位。

      所以这里有一个 Swift 4 解决方案(开尔文是输入温度单位):

      func temperatureFormatter(kelvinTemp: Double) -> String{
              let mf = MeasurementFormatter()
              mf.numberFormatter.maximumFractionDigits = 0
              let t = Measurement(value: kelvinTemp, unit: UnitTemperature.kelvin)
              return (String(format:"%@", mf.string(from: t)))
          }
      

      您可以在此处调用您的函数来转换值(_currentTemp 是您提取的 JSON 温度值 & convertedTemp em> 是根据上述条件得出的转换温度):

      convertedTemp = temperatureFormatter(kelvinTemp: _currentTemp)
      

      让我知道这个解决方案是否适合您,但它确实适合我。谢谢!

      【讨论】:

        【解决方案6】:

        我发现了 Alexandre Colucci 的这篇文章:http://blog.timac.org/?tag=nslocaletemperatureunit

        首先,公开 NSLocaleTemperatureUnit NSLocaleKey:

        FOUNDATION_EXPORT NSLocaleKey const NSLocaleTemperatureUnit;
        

        然后用这个检查单位:

        temperatureUnit = [[NSLocale currentLocale] objectForKey:NSLocaleTemperatureUnit]
        

        或 Swift (2.3):

        if let temperatureUnit = NSLocale.currentLocale().objectForKey(NSLocaleTemperatureUnit) {
            ...
        }
        

        它会返回一个字符串,要么是“摄氏度”,要么是“华氏度”。

        但是有一个问题:它不向后兼容低于 10 的 iOS 版本。如果你在 iOS 9 或更早的设备上运行你的应用程序,你会在应用程序期间收到错误“dyld: Symbol not found: _NSLocaleTemperatureUnit”启动。

        解决方案是对 NSLocaleTemperatureUnit 变量定义使用弱链接。像这样:

        FOUNDATION_EXPORT NSLocaleKey const NSLocaleTemperatureUnit  __attribute__((weak_import));
        

        这将使应用程序通过 dyld 检查。但是现在你必须在使用 NSLocaleTemperatureUnit 之前检查操作系统版本,否则你的应用程序会崩溃并出现异常。

        if #available(iOS 10,*) {
            if let temperatureUnit = NSLocale.currentLocale().objectForKey(NSLocaleTemperatureUnit) {
                ...
            }
        }
        

        编辑:

        我在我的应用程序中尝试了它,但当我将它上传到 Testflight 时,Apple 拒绝了它。所以他们绝对不希望我们将设置用于我们自己的格式化程序类。我觉得这很烦人。

        【讨论】:

        • 据此(以及我在 iOS 10.2 中的测试),如果您在设备上进行测试,使用 @pedroan 描述的方法可以工作。 forums.developer.apple.com/thread/70258
        • Apple 绝对不会让你使用 NSLocaleTemperatureUnit 符号,因为它是私有的。但我确实想知道他们是否会允许字符串值,即直接传递“kCFLocaleTemperatureUnitKey”的显式值(这是符号的值)。从技术上讲,这并没有引用任何私有 API。如果没有,同样的值也可以从“AppleTemperatureUnit”键下的 NSUserDefaults 中获得,但 Apple 也可以检查该名称,不确定。
        • 哦,它返回的值是“摄氏度”,而不是“摄氏度”。
        【解决方案7】:

        有一个 (NS)MeasurementFormatter 类。它继承自 (NS)Formatter 类。这是一个适用于 iOS 10+ SDK 的新类。

        我不确定是否有必要知道用户在他们的偏好中设置了什么单位。

        使用 Swift 3设置 Measurement

        let formatter = MeasurementFormatter()
        let measurement = Measurement(value: 24.5, unit: UnitTemperature.celsius)
        let temperature = formatter.string(from: measurement)
        print(temperature) // 76.1°F 
        // this value was computed to Fahrenheit value on my locale/preferences
        

        检索 Measurement

        print(measurement.unit) // °C - always celsius as it was set as Celsius
        
        formatter.unitStyle = .long
        
        formatter.locale = Locale.current
        formatter.string(from: measurement.unit) // degrees Celsius - always an original unit
        formatter.string(from: measurement) // 76.1 degrees Fahrenheit - regarding locale/settings
        
        formatter.locale = Locale.init(identifier: "it_IT")
        formatter.string(from: measurement.unit) // gradi Celsius - always an original unit
        formatter.string(from: measurement) // 24,5 gradi Celsius - regarding locale/settings
        

        系统知道我们设置了什么单位。它将处理所有的值转换工作,因为值被设置为一对值和一个测量单位

        对于手动转换:

        measurement.converted(to: UnitTemperature.kelvin).value  // 297.65
        

        斯威夫特:

        https://developer.apple.com/reference/foundation/measurementformatter

        目标-C:

        https://developer.apple.com/reference/foundation/nsmeasurementformatter?language=objc


        请随意纠正语法。

        【讨论】:

        • 不过,这些都不涉及这个问题。 MeasurementFormatter 不会根据 iOS 10 中的“温度单位”设置更改其输出。它的格式基于不同的区域设置。
        • 是的,它没有。但经过扎实的研究,我认为,没有办法通过任何 API 获取此信息,原因是它不需要。
        • 为什么说不需要呢?如果MeasurementFormatter 不支持“设置”应用中新的“温度单位”设置,则必须找到其他方式来支持用户所做的设置。
        • 哪一个什么?我不知道你指的是“哪一个”。我只是指出您的回答实际上并没有尝试回答所提出的问题。这是很好的信息,但与问题无关。
        • 我在运行 iOS 10.2 的设备上测试了这种方法,并且成功了。这是由 Apple 工程师在这里描述的 forums.developer.apple.com/thread/70258
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-02-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多