C44.跟著老師動吃動!BMR、TDEE計算機
Published in
18 min readMay 18, 2023
練習成果
練習發想
- 平常有運動的習慣,剛好可藉由這次的練習,來製作一個基礎代謝率(BMR)以及 每日總熱量消耗(TDEE)的計算機。
- 而關於BMR以及TDEE的計算流程方式流程:
男性
與女性
的BMR計算公式不同,因此先將它們兩個的BMR值
計算出來。- 在用BMR的值來計算
TDEE的值
。 - 在
BMR值不變
的狀態下,TDEE 會因為不同運動頻率
的計算公式,使得有不同的值
產生。
練習項目
這次的練習除了下面幾個項目,同時在PETER的建議下,也強調自己在寫程式時去加強註解
,以及print
的使用。
不得不說這次雖然註解相比以往的有點多,但是當我遇到一些程式碼位置的問題,或是修改時,可以更好的去查找釐清出問題!
- 數字與字串間的轉換。
(TextField中的字串轉數字)
- Slider 數值轉換為
整數索引值
。 - if else 的運用,if else在加入 if else。
(條件式判斷運動頻率)
- optional binding的練習,確保 height、weight、age 不為 nil。
(與if else搭配 處理TextField可能為 nil 的值)
- function的使用(使程式碼更整齊)
佈局配置圖
- 紅框:Segmented(性別) / TextField(身高、體重、年紀),紅框內部的部分即可算出BMR。
- 藍框:Slider(調整運動頻率:
不運動、每週運動1–3次、每週運動3–4次、每週運動6–7次、無時無刻都在動
),搭配BMR可算出TDEE的值。 - 黃框:計算、清除鈕。
- 灰框:顯示BMR、TDEE的計算結果。
BMR的計算(IBOutlet)
- 主要需要計算BMR時,所需要提供的資料。
- 性別
SegmentedControl
、身高、體重、年齡TextField
的 IBOutlet。
// 性別選項
@IBOutlet weak var genderSegmentedControl: UISegmentedControl!
// 身高輸入: cm
@IBOutlet weak var heightInputField: UITextField!
// 體重輸入: kg
@IBOutlet weak var weightInputField: UITextField!
// 年齡輸入
@IBOutlet weak var ageInputField: UITextField!
BMR的計算(Button IBAction)
- 在
calculateButtonTapped
中進行計算 - Optional 表示該值可以是一個
非nil
,也可以是nil
。 - 而
Double
和Int
則是非Optional
的數字類型(一定要有值)。
- 使用強制解包 (!) 將
heightInputField.text
、weightInputField.text
和ageInputField.text
的 Optional值 解包為非可選型的值。表示假設這些值不為nil
,並將它們分別指派給heightText
、weightText
和ageText
。但如果這些可選型值為nil
,則會在執行時觸發錯誤。 - 將這些文字值轉換為 Double,以進行計算。
- 如果文字無法轉換為 Double,則會得到 nil。
// 將 UITextField 的 text 屬性的值指派給相應的變數。保證這些 Optional的值 不是nil,以確保它們的值存在。
let heightText = heightInputField.text!
let weightText = weightInputField.text!
let ageText = ageInputField.text!
// 字串轉數字用於計算
let height = Double(heightText)
let weight = Double(weightText)
let age = Double(ageText)
if else 運用 (Optional Binding)
當初在調整時忘記順序,將BMR計算公式放置在if else外部,導致在測試計算時閃退,還好有註解才發現 XD
- 使用 Optional Binding 檢查身高、體重、年紀 的值是否不為
nil
。 - 當先前條件為
不等於nil
時,則進行計算。在此已經確認身高體重年紀皆有值,因此要加入(!),將 Optional 進行解包。 - 我這邊透過
SegmentedControl
來分別辨識男性女性的BMR的計算公式。 - 計算出來後再將BMR值字串化,取小數點後一位。
// 計算BMR之前,先檢查 height、weight 和age 是否為 nil,如果有值(不為nil)則進行計算,selectedSegmentIndex == 0 為男性
if height != nil, weight != nil, age != nil, genderSegmentedControl.selectedSegmentIndex == 0 {
// ● 男性:基礎代謝率 = (10 × 公斤體重) + (6.25 × 公分身高) - (5 × 年齡歲數) + 5
let bmrCalculate = (10 * weight!) + (6.25 * height!) - (5 * age!) + 5
// BMR 數值取小數點第一位,並顯示於 bmrResultLabel 上
bmrResultLabel.text = String(format: "%.1f", bmrCalculate)
// selectedSegmentIndex == 1 為女性
} else if height != nil, weight != nil, age != nil, genderSegmentedControl.selectedSegmentIndex == 1 {
// ● 女性:基礎代謝率 = (10 × 公斤體重) + (6.25 × 公分身高) - (5 × 年齡歲數) – 161
let bmrCalculate = (10 * weight!) + (6.25 * height!) - (5 * age!) - 161
bmrResultLabel.text = String(format: "%.1f", bmrCalculate)
}
運動頻率Slider
- 當初看其他人在做這個作業時都是用
UIPickerView
,但現階段還不太理解,因此使用Slider來進行操作,先將重心放在Optional binding上XD
- 運動頻率以及相對應的計算公式
// 運動頻率以及相對應的計算公式
1.久坐(沒運動)
TDEE = 1.2 x BMR
2.輕量活動量(每周運動1-3天)
TDEE = 1.375 x BMR
3.中度活動量(每周運動3-5天)
TDEE = 1.55 x BMR
4.高度活動量(每周運動6-7天)
TDEE = 1.725 x BMR
5.非常高度活動量(無時無刻都在運動)
TDEE = 1.9 x BMR
- activityLevelLabel:為顯示當前運動頻率的Label,讓使用者知道當Slider滑動到哪個選項。
- exerciseSlider:用於選擇運動頻率的值,並在滑動時觸發
exerciseFrequencySliderChanged
的Slider IBAction。
// 運動頻率
@IBOutlet weak var activityLevelLabel: UILabel!
// 存儲運動頻率 Slider 的值 (TDEE 才會用到)
@IBOutlet weak var exerciseSlider: UISlider!
- 陣列部分:後續會透過
exerciseFrequencySliderChanged
來調用,藉以展示在activityLevelLabel
上。 - 我打算
用Slider的值來當索引
,故先在viewDidLoad()將 exerciseSlider 的最小值0,最大值為4。因為陣列選項有5個。由0開始計算到4,會完整涵蓋5個項目。
// 運動頻率 Array
let exerciseFrequencyArrays = ["Sedentary or No Exercise", "Exercise 1-3 Times / Week", "Intense Exercise 3-4 Times / Week", "Intense Exercise 6-7 Times / Week", "Very Intense Exercise Daily!"]
// 在view載入後執行的初始化操作
override func viewDidLoad() {
super.viewDidLoad()
// exerciseSlider 的最大值最小值、起始值
exerciseSlider.minimumValue = 0
exerciseSlider.maximumValue = 4
exerciseSlider.value = 0
}
TDEE的運動頻率 Slider的IBAction
- 因為Slider的值是浮點數,因此使用rounded()將其四捨五入為整數。
- 將四捨五入後的整數
sliderValue
設置回exerciseSlider
,這樣可以確保滑動條的顯示值與實際值保持一致,即顯示整數而不是小數。
- 使用
Int(sliderValue)
將sliderValue
轉換為Int,並將其作為索引從exerciseFrequencyArrays
中取出對應的運動頻率字串。因此當Slider的值改變時,運動頻率的Label顯示也會相應地更新。
// 運動頻率Slider:
@IBAction func exerciseFrequencySliderChanged(_ sender: UISlider) {
// rounded() 四捨五入 sender.value
let sliderValue = sender.value.rounded()
// 將 sliderValue 轉換為整數並設置回 exerciseSlider,確保Slider條的值一直保持在整數。
exerciseSlider.setValue(sliderValue, animated: false)
/* 使用 sliderValue 作為索引,從 exerciseFrequencyArrays 中取出對應的運動頻率字串,並將其設置為 activityLevelLabel 的文字。實現當 Slider 滑動時,更新運動頻率文字的顯示。 */
activityLevelLabel.text = exerciseFrequencyArrays[Int(sliderValue)]
}
if else 中在加入if else進行另一個條件式(TDEE運動頻率判斷)
- 一樣是在
calculateButtonTapped
中進行計算,我這邊一樣先放男性來做示範。
exerciseFrequencyIndex
:因為是要運用Slider的值來給予TDEE不同運動頻率的計算公式,因此將其轉化成Int,用以進行if else 的條件判斷來給予公式。
- 由於 TDEE 的公式是基於BMR的值來進行計算。
- 因此在原先的 BMR 條件式在加入另一個 if else進行運動頻率判斷。
- 對應
exerciseFrequencySliderChanged
我將其值變為整數,並將其用作更新運動頻率Label
索引值。 - 同樣的我透過
exerciseFrequencyIndex
的值來對應運動頻率的計算公式
,使用者通過Slider選擇的數值會直接影響到 TDEE 的計算結果。
// 將 exerciseSlider 的值轉換為整數,作為運動頻率的索引值。並且用在 if else 條件判斷 TDEE 計算使用。
let exerciseFrequencyIndex = Int(exerciseSlider.value)
// 計算BMR之前,先檢查 height、weight 和age 是否為 nil,如果有值(不為nil)則進行計算,selectedSegmentIndex == 0 為男性
if height != nil, weight != nil, age != nil, genderSegmentedControl.selectedSegmentIndex == 0 {
// 當Slider的值 == 0 時,代表沒有運動習慣時,該條件下所使用的 TDEE計算公式
if exerciseFrequencyIndex == 0 {
// 沒運動習慣:TDEE = 1.2 x BMR
let tdee = bmrCalculate * 1.2
// 數字轉字串: TDEE 數值取小數點第一位,並顯示於 tdeeResultLabel 上
tdeeResultLabel.text = String(format: "%.1f", tdee)
} else if exerciseFrequencyIndex == 1 {
// 輕量活動(每周運動1-3天):TDEE = 1.375 x BMR
let tdee = bmrCalculate * 1.375
tdeeResultLabel.text = String(format: "%.1f", tdee)
} else if exerciseFrequencyIndex == 2 {
// 中度活動量(每周運動3-5天):TDEE = 1.55 x BMR
let tdee = bmrCalculate * 1.55
tdeeResultLabel.text = String(format: "%.1f", tdee)
} else if exerciseFrequencyIndex == 3 {
// 高度活動量(每周運動6-7天):TDEE = 1.725 x BMR
let tdee = bmrCalculate * 1.725
tdeeResultLabel.text = String(format: "%.1f", tdee)
} else if exerciseFrequencyIndex == 4 {
// 非常高度活動量(無時無刻都在運動):TDEE = 1.9 x BMR
let tdee = bmrCalculate * 1.9
tdeeResultLabel.text = String(format: "%.1f", tdee)
}
}
function的運用 (return運用)
- 因為不管男性還是女性,只有在BMR的計算才會有公式上的差異,而TDEE則會依照不同運動頻率來做計算。此外她們分屬不同的 {} 括弧裡,因此在命名上並不會互相影響,都是在各自獨立的括弧裡進行運算。
- 為了使程式
calculateButtonTapped
裡的程式碼相對簡潔,因此另外創立一個funtioncalculateTdee
。 - 定義了一個
calculateTdee
的function,用於計算 TDEE (Total Daily Energy Expenditure)。TDEE 是一個人在一天中所需的總能量消耗量,根據不同的運動頻率進行計算。 - 在
calculateTdee
方法中,有兩個參數 (bmr
、frequencyIndex
)。bmr
是之前計算得到的 BMR (Basal Metabolic Rate) 值,而frequencyIndex
是選定的運動頻率索引值。 - 根據
frequencyIndex
的不同,calculateTdee
使用不同的條件判斷來計算相應的 TDEE 值。每個條件分支對應一個特定的運動頻率,並根據該運動頻率使用相應的計算公式來計算 TDEE 值。 var tdee: Double = 0.0
宣告了一個名為tdee
的變數,並將其初始化為 0.0。在後續的計算中,根據不同的運動頻率和BMR,tdee
的值會被計算出並存儲在這個變數中。- if else 括弧內計算得到的 TDEE 值指派給
var tdee: Double
。 - 接著使用
return
將tdee
的值返回給調用calculateTdee
的地方。使外部的程式碼就可以獲取到計算得到的 TDEE 值,並進一步處理或顯示該值。
func calculateTdee(bmr:Double, frequencyIndex: Int) -> Double{
// 先宣告一個變數來存儲計算得到的 TDEE 值。
var tdee: Double = 0.0
// 根據運動頻率的 Slider 的值,來決定該條件下所使用的 TDEE計算公式
if frequencyIndex == 0 {
// 沒運動習慣:TDEE = 1.2 x BMR
tdee = 1.2 * bmr
} else if frequencyIndex == 1 {
// 輕量活動(每周運動1-3天):TDEE = 1.375 x BMR
tdee = 1.375 * bmr
} else if frequencyIndex == 2 {
// 中度活動量(每周運動3-5天):TDEE = 1.55 x BMR
tdee = 1.55 * bmr
} else if frequencyIndex == 3 {
// 高度活動量(每周運動6-7天):TDEE = 1.725 x BMR
tdee = 1.725 * bmr
} else if frequencyIndex == 4 {
// 非常高度活動量(無時無刻都在運動):TDEE = 1.9 x BMR
tdee = 1.9 * bmr
}
// 返回計算得到的 TDEE 值
return tdee
}
在 calculateButtonTapped 裡面調用 calculateTdee 方法
- 其兩個參數:
bmr
帶入計算出來的BMR值。frequencyIndex
帶入運動頻率的索引值。 - 將使用者的BMR值以及所選的運動頻率,用來進行條件判斷,並且得到TDEE的值。
- 再將計算得到的 TDEE 值格式化後設置到
tdeeResultLabel
中顯示。
// 將 exerciseSlider 的值轉換為整數,作為運動頻率的索引值。並且用在 if else 條件判斷 TDEE 計算使用。
let exerciseFrequencyIndex = Int(exerciseSlider.value)
// 計算BMR之前,先檢查 height、weight 和age 是否為 nil,如果有值(不為nil)則進行計算,selectedSegmentIndex == 0 為男性
if height != nil, weight != nil, age != nil, genderSegmentedControl.selectedSegmentIndex == 0 {
let bmrCalculate = (10 * weight!) + (6.25 * height!) - (5 * age!) + 5
bmrResultLabel.text = String(format: "%.1f", bmrCalculate)
// 根據選定的 運動頻率索引值 和 前面計算得到的 BMR,調用 calculateTdee function 計算 TDEE。
let tdee = calculateTdee(bmr: bmrCalculate, frequencyIndex: exerciseFrequencyIndex)
// 將結果設置到 tdeeResultLabel 中。
tdeeResultLabel.text = String(format: "%.1f", tdee)
}
GitHub
參考