[iOS 10 day by day] Day 7:單位換算

《iOS 10 day by day》是 shinobicontrols 公司編寫的系列博客,介紹開發(fā)者需要了解的 iOS 10 新特性洗贰,每周更新污淋。本系列翻譯(文集地址)已取得官方授權柿隙。目錄點此叶洞。倉薯翻譯,歡迎指正:)

Shinobicontrols 為 iOS 和 Android 開發(fā)者提供高性能禀崖、響應式的 UI 控件 SDK衩辟,尤其是圖表方面的控件。 官網(wǎng) : shinobicontrols.com twitter : @shinobicontrols

本文翻譯時參考了 simpletonking同一篇譯文波附,在此感謝艺晴。Thank you simpletonking!

本周我們來看看新的 Measurement API,這是 Foundation 框架新增的一部分掸屡。這套 API 從表面上看平凡無奇封寞,就是提供了一套單位換算的方法,例如公里與英里互相換算仅财。

不過狈究,仔細想想,我們確實在單位換算上浪費了太多時間盏求。比如谦炒,你有一個角度值,但是用來旋轉(zhuǎn) view 的 API 只接受弧度值风喇。或者缕探,可能你的 app 里距離的計量單位是英里魂莫,但是要為了習慣用公里的用戶轉(zhuǎn)換成公里去顯示。

在 iOS 10 之前爹耗,你可能已經(jīng)自己寫了一些單位換算的方法耙考,或者用了第三方庫。現(xiàn)在蘋果提供了新的 API 潭兽,能解決大部分問題了倦始。我們來看看它具體能做什么吧。

基本概念

本文使用 Swift 3 編寫山卦,在 Xcode 8 GM build 上編譯運行鞋邑。

我們要介紹一個單位空間(dimension)的概念,用 Measurement 的 model 類型把數(shù)值保存為某個單位空間的數(shù)據(jù)。所謂“單位空間”就是一組能互相轉(zhuǎn)換的單位枚碗,比如克可以轉(zhuǎn)換成千克逾一,千克也能轉(zhuǎn)換回來。每個單位空間都有一個基本單位肮雨,其他單位都用這個基本單位來表示(比如遵堵,容積的基本單位是升,而 1 毫升就是 0.001 升)怨规。

建立度量值

先從簡單的開始陌宿,假設我們有一品脫牛奶,想知道一品脫是多少公升波丰。代碼如下:

let milk = Measurement(value: 1, unit: UnitVolume.imperialPints)
milk.converted(to: .liters)
// 輸出 0.568261 L

很簡單吧壳坪!定義出了某個單位的度量值之后,就只能把它換算為同一個單位空間的其他單位呀舔。converted 這一步會自動進行類型檢查弥虐,milk 變量的類型是 Measurement<UnitVolume>,換算成的單位也要屬于 UnitVolume 這個單位空間媚赖。只能在同一個單位空間之內(nèi)互相換算霜瘪,這種限制是顯而易見的——不然,把公升換算成英里怎么換算惧磺?

運算符

Measurement API 支持對度量值使用運算符颖对。

如果我們想要 5 品脫的牛奶,就可以寫:

let fivePints = milk * 5

這樣會創(chuàng)建一個新的度量值磨隘,接下來我們就可以把它換算成另一個單位:

fivePints.converted(to: .cups)
// 輸出 11.8387708333333 cup

可以注意到缤底,把代碼放在 Playground 里,或者把度量值打印出來番捂,末尾會自動帶上它的單位个唧。

當然,能用的運算符不只有乘號设预。還有其他的幾種徙歼,比如雙等號 ==

let kms = Measurement(value: 5, unit: UnitLength.kilometers)
let meters = Measurement(value: 5000, unit: UnitLength.meters)

kms == meters // true

以及加號:

kms + meters // 10000.0 m

Formatter

前面提到過,做本地化的時候我們經(jīng)常需要為不同的 locale 顯示不同的單位鳖枕。

除了新的 Measurement API魄梯,蘋果還提供了 MeasurementFormatter,它能把度量值轉(zhuǎn)化成格式化字符串宾符。

默認情況下酿秸,measurement formatter 使用的是用戶當前的 locale。下面我們手動更改這一點魏烫,更改前后分別打印同一段兩個城市之間的距離辣苏,看看輸出有什么變化:

let newcastleToLondon = Measurement(value: 248, unit: UnitLength.miles)

let formatter = MeasurementFormatter()
formatter.locale = Locale(identifier: "fr")
formatter.string(from: newcastleToLondon) // 輸出 399,166 km

formatter.locale = Locale(identifier: "en_GB")
formatter.string(from: newcastleToLondon) // 輸出 248 mi

好棒肝箱!不費吹灰之力就完成了。

項目

我們已經(jīng)大略看了一下 API 的基本用法考润,下面來上手玩一下吧狭园。

我們來做一個小風車,轉(zhuǎn)動速度與風力強度成比例糊治,而風力強度可以用一個滑動條來調(diào)節(jié)唱矛。

風車就是一個簡單的 UIView 子類。把它添加到 UIViewController 的 view 上井辜,再加一些其他的基本 UI 控件:一個調(diào)節(jié)風速的滑動條绎谦,還有一個用 米/秒 和 英里/小時 兩種單位顯示風速的 label。如果想看完整的 playground粥脚,歡迎從 GitHub 下載窃肠。

我們把目光集中在用到 Measurement API 的部分上:首先是拖動滑動條的時候,在 label 上顯示風速:

func handleWindSpeedChange(slider: UISlider) {
    let windSpeed = Measurement(value: Double(slider.value), unit: UnitSpeed.metersPerSecond)

    let milesPerHour = windSpeed.converted(to: .milesPerHour)

    windSpeedLabel.text = "Wind speed: \(windSpeed) (\(milesPerHour))"
}

label 的顯示就像下面這樣:

未經(jīng)格式化的 label

哇哦刷允!只是一個簡單的 demo 而已冤留,不需要弄得這么精確。有時候小數(shù)點后的位數(shù)顯示得太多树灶,都看不到后面的單位 m/s 了纤怒。要解決這個問題,我們可以使用上面提過的 MeasurementFormatter天通。

let windSpeed = Measurement(value: Double(slider.value), unit: UnitSpeed.metersPerSecond)

let measurementFormatter: MeasurementFormatter = {
    let formatter = MeasurementFormatter()
    formatter.unitOptions = .providedUnit
    let numberFormatter = NumberFormatter()
    numberFormatter.minimumIntegerDigits = 1
    numberFormatter.minimumFractionDigits = 1
    numberFormatter.maximumFractionDigits = 1
    formatter.numberFormatter = numberFormatter

    return formatter
}()

let metersPerSecond = measurementFormatter.string(from: windSpeed)
let milesPerHour = measurementFormatter.string(from: windSpeed.converted(to: .milesPerHour))

windSpeedLabel.text = "Wind speed: \(metersPerSecond) (\(milesPerHour))"

創(chuàng)建 formatter 的時候泊窘,我們要指定它使用 providedUnit。這是為了防止 formatter 忽略我們指定的單位像寒,用它自己認為合適的格式輸出烘豹。對我們現(xiàn)在的情況來說,如果不設這個 option诺祸,formatter 會對兩個結(jié)果都用 英里/小時 為單位輸出携悯。

MeasurementFormatter 本身包含另一個 formatter(有點像嵌套的 formatter!),內(nèi)層的 formatter 是用來格式化數(shù)字部分的筷笨。我們要求數(shù)字顯示為一位(且僅一位)小數(shù)蚌卤。

最后,我們使用構造好的 formatter奥秆,將 米/秒 和 英里/每小時 的兩種風速度量分別格式化為 string。

格式化后的 label

為了有一點視覺反饋咸灿,我們希望葉片轉(zhuǎn)動的速度能隨著風速改變(要注意构订,下面用到的這些數(shù)值只是為了展示用的,跟風力的物理學基礎沒有任何關系)避矢。

顯示動畫的是 TurbineView悼瘾,不過我們要把每秒鐘葉片轉(zhuǎn)動多少角度的數(shù)值傳給它囊榜。你可能會想到去定義一個屬性,類似這樣:

/// 葉片每秒轉(zhuǎn)動的角度亥宿,用弧度的形式表示
public var bladeRotationPerSecond: Double

這樣定義沒什么問題卸勺,也跟蘋果官方的 API 保持一致,角度是用弧度表示的烫扼。不過曙求,怎么防止使用者無意中傳了角度值而不是弧度值呢?你可能會說:“他們應該好好看看文檔”映企。這句話有一定道理悟狱,不過萬一這個屬性沒有文檔呢?并且堰氓,因為我們平常更習慣用角度值挤渐,所以這也是個容易不小心犯下的錯誤。

那我們能怎么利用上 Measurement 框架双絮,只約束使用者傳過來的是表示角度的值浴麻,具體單位不限呢?這樣使用者想傳弧度或者角度都可以囤攀,反正都會自動轉(zhuǎn)化成我們需要的單位软免。聽起來很棒,我們試試吧:

// TurbineView 的屬性
public var bladeRotationPerSecond: Measurement<UnitAngle> = Measurement(value: 0, unit: UnitAngle.degrees) {
    didSet {
        rotate()
    }
}

在 viewController 里抚岗,我們可以用下面這段代碼來計算一秒旋轉(zhuǎn)多少角度或杠。

func calculateTurbineRotation() {
    // 假設滑動條拖到最快時,轉(zhuǎn)速達到每秒 1 圈
    let ratio = windSpeedSlider.value / windSpeedSlider.maximumValue

    let fullRotation = Measurement(value: 360, unit: UnitAngle.degrees)

    let rotationAnglePerSecond = fullRotation * Double(ratio)

    turbine.bladeRotationPerSecond = rotationAnglePerSecond
}

參數(shù)想傳角度或者弧度都可以——我選擇了角度宣蔚。然后根據(jù)當前風速來計算旋轉(zhuǎn)角度(如果滑動條的 value 為 0向抢,ratio 就是 0 / 40 = 0;拖到最快的一端胚委,滑動條的 value 是 40挟鸠,ratio 就是 40 / 40 = 1),用到了乘法操作符亩冬,非常方便艘希。

下面來欣賞我們美麗的風車吧:

根據(jù)風速旋轉(zhuǎn)的風車

擴展閱讀

本文中我們用到了蘋果提供的幾組單位,不過這些只是冰山一角硅急;蘋果一共提供了 170 多種不同的單位覆享。你需要用的單位大概率就在其中,不過营袜,如果真的沒有撒顿,也可以自己創(chuàng)建一個。想知道怎么創(chuàng)建(還有其他內(nèi)容<园濉)凤壁,請看這部 WWDC 視頻吩屹。

原文地址:iOS 10 Day by Day :: Day 7 :: Measurement

原作者:Sam Burnstone @sam_burnstone

ShinobiControls 官網(wǎng):ShinobiControls.com twitter : @shinobicontrols

文集地址:iOS 10 day by day 倉薯翻譯

譯者:戴倉薯,本文翻譯時參考了 simpletonking同一篇譯文拧抖,非常感謝~

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末煤搜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子唧席,更是在濱河造成了極大的恐慌擦盾,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袱吆,死亡現(xiàn)場離奇詭異厌衙,居然都是意外死亡,警方通過查閱死者的電腦和手機绞绒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門婶希,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蓬衡,你說我怎么就攤上這事喻杈。” “怎么了狰晚?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵筒饰,是天一觀的道長。 經(jīng)常有香客問我壁晒,道長瓷们,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任秒咐,我火速辦了婚禮谬晕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘携取。我一直安慰自己攒钳,他們只是感情好,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布雷滋。 她就那樣靜靜地躺著不撑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晤斩。 梳的紋絲不亂的頭發(fā)上焕檬,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機與錄音澳泵,去河邊找鬼实愚。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的爆侣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼幢妄,長吁一口氣:“原來是場噩夢啊……” “哼兔仰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蕉鸳,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤乎赴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后潮尝,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體榕吼,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年勉失,在試婚紗的時候發(fā)現(xiàn)自己被綠了羹蚣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡乱凿,死狀恐怖顽素,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情徒蟆,我是刑警寧澤胁出,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站段审,受9級特大地震影響全蝶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寺枉,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一抑淫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧型凳,春花似錦丈冬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至疏唾,卻和暖如春蓄氧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背槐脏。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工喉童, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人顿天。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓堂氯,卻偏偏與公主長得像蔑担,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子咽白,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 在iPhone7發(fā)布之后啤握,令人興奮的事情已經(jīng)塵埃落定,現(xiàn)在是時候繼續(xù)看看iOS10提供的新特性這一周我們將要看看新...
    simpletonking閱讀 824評論 0 1
  • 作者:Ole Begemann晶框,原文鏈接排抬,原文日期:2016-07-28譯者:粉紅星云;校對:saitjr授段;定稿:...
    梁杰_numbbbbb閱讀 639評論 1 2
  • 國家電網(wǎng)公司企業(yè)標準(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 11,005評論 6 13
  • 晚安 ――羅詩默 蒼白的世界 我站在你中央 燈紅酒綠 穿梭的車輛 縱橫著飛翔 遺落的傳單 漫天飛揚 熙熙攘...
    羅詩默閱讀 202評論 2 4
  • 一直以來蹲蒲,看電影都不敢看情節(jié)轉(zhuǎn)折的地方,不敢面對接下去的殘酷侵贵,哪怕知道一定會有峰回路轉(zhuǎn)的時候届搁,可是現(xiàn)在就是不想看...
    如沐小姐閱讀 457評論 0 1