房貸試算小工具

Wen Lo
彼得潘的 Swift iOS / Flutter App 開發教室
12 min readJan 19, 2022

[iOS App Development]數學運算練習

台灣有些很奇怪的現象我一直很不解,很多鄉民、政客和媒體都說房貸就是造成整個世代不結婚、不生小孩的真兇,還間接導致國安問題;但偏偏還有中南部一些建案根本像在搶五月天票那樣有人漏夜排隊,我想這應該就是蜘蛛人裡面多元宇宙的概念吧!

房貸(Mortgage)其實是來自於中世紀的法文,mort是「死亡」的意思,而gage代表「誓言」,合起來叫做「死亡誓言」,是不是有不寒而慄的感覺呢?每次用這個小工具算完我也真的覺得有冷風從後面吹過的感覺呢!

這個試算小工具功能很單純就是用本息攤還的方式算出房貸相關的金額,欄位和公式參考了內政部和591的網站,包含「房屋總價」、「首付比例」、「貸款期限」、「寬限期」和「年利率」。按下試算按鈕後就會算出「貸款總額」、「利息總額」、「每月平均攤還金額」。

Demo

上面除了寬限期未填會預設代入0之外,其他欄位都是必填,所以試算時檢查到有欄位沒有填寫就會在該欄位下方顯示「必填」。

前置作業

・因為考慮到是表單有多個格式類似的欄位要輸入,所以整個View使用TableViewControll來製作。為了要能使用程式來控制這個TableView,要先自訂一個繼承UITableViewController的controller檔再從裡面去撰寫程式,詳細作法可以參考此篇說明。

・變數宣告:

//貸款總額
var mortgageAmount = Double()
//利息總額
var totalInterest = Double()
//每月平均攤還金額
var monthlyRepayment = Double()

・Outlet建立:

//因為要計算所以勢必要擷取五個輸入欄位的內容
@IBOutlet weak var housePriceTextField: UITextField!
@IBOutlet weak var downPaymentRateTextField: UITextField!
@IBOutlet weak var periodTextField: UITextField!
@IBOutlet weak var gracePeriodTextField: UITextField!
@IBOutlet weak var interestRateTextField: UITextField!
//試算之後顯示計算結果的Label
@IBOutlet weak var mortgageAmountLabel: UILabel!
@IBOutlet weak var interestAmountLabel: UILabel!
@IBOutlet weak var monthleRepaymentLabel: UILabel!
//如果沒有填寫欄位提醒的「必填」Label
@IBOutlet weak var housePriceNoTextLabel: UILabel!
@IBOutlet weak var downRateNoTextLabel: UILabel!
@IBOutlet weak var periodNoTextLabel: UILabel!
@IBOutlet weak var interestRateNoTextLabel: UILabel!
@IBOutlet weak var mortgageAmountTitleLabel: UILabel!
//試算後結果的標題Label和代表顏色圖例的ImageView
@IBOutlet weak var interestTitleLabel: UILabel!
@IBOutlet weak var monthlyRepaymentTitleLabel: UILabel!
@IBOutlet weak var mortgagmeAmountColorImageView: UIImageView!
@IBOutlet weak var interestColorImageView: UIImageView!

//下方顯示計算結果的ContentView,因為之後要在上面畫甜甜圈圖所以必須拉Outlet出來
@IBOutlet weak var resultContentView: UIView!
//試算和重置按鈕,因為位置相同,只是看時機不同來分別顯示/隱藏,所以拉Outlet來控制
@IBOutlet weak var calcButton: UIButton!
@IBOutlet weak var resetButton: UIButton!

・函式功能說明:

  1. setGradientBackground():設定整個TableView的背景漸層顏色。
  2. formatizeNumber(_ num: Double) -> String:讓計算出來的Double可以轉為字串型態給Label顯示。
  3. showResultTitle():顯示結果的數值名稱標籤和圖例。
  4. lockForm():試算之後重置前鎖定TextField不能再更動。
  5. unlockAndRestForm():重置時需要解鎖TextField並清空欄位。
  6. drawDonut(principalRatio: Double, interestRatio: Double):繪製本金和利息總額佔比的甜甜圈圖,包括清掉前次甜甜圈的功能。

試算IBAction

試算 button的Action可以說是整個project的核心,必須做到檢查、擷取資料、轉換資料、判斷條件、計算和將資料再轉換後更新到介面上。以下解說程式邏輯:

@IBAction func calcMortgageButton(_ sender: UIButton) {
//按下試算按鈕後就結束編輯收回鍵盤
view.endEditing(true)

//檢查必填的欄位有沒有填寫,沒有填寫就要顯示必填label
if housePriceTextField.text == "" {
housePriceNoTextLabel.isHidden = false
}
if downPaymentRateTextField.text == "" {
downRateNoTextLabel.isHidden = false
}
if periodTextField.text == "" {
periodNoTextLabel.isHidden = false
}
//檢查到最後一個年利率欄位,如果沒有填寫就return結束action不繼續計算
if interestRateTextField.text == "" {
interestRateNoTextLabel.isHidden = false
return
}
//如果可以繼續下去表示每個必填欄位都有填寫,先顯示結果的文字標籤和圖例
showResultTitle()
//轉換TextField的字串為Double,方便之後計算
let housePrice = Double(housePriceTextField.text!)!
let downRate = Double(downPaymentRateTextField.text!)!
let period = Double(periodTextField.text!)!
//寬限期如果沒有填寫預設為0.0
let gracePeriod = Double(gracePeriodTextField.text!) ?? 0.0
let yearRate = Double(interestRateTextField.text!)! / 100

//開始計算
//從年利率得出月利率
let monthRate = yearRate / 12
//從貸款期限得出有幾期(月)
let monthlyPeriod = Int(period * 12)
//從房價和首付比例算出貸款總額
mortgageAmount = (housePrice * 10000) * (1 - (downRate / 100))
//將貸款總額轉為字串後顯示到結果Label中
mortgageAmountLabel.text = formatizeNumber(mortgageAmount.rounded() / 10000) + "萬"

//開始計算每月平均攤還金額、利息和本金
//初始化剩餘本金為貸款總額
var principalLeft = mortgageAmount
//分為有寬限期和沒有寬限期的狀況
if gracePeriod > 0 {
//計算寬限期每月的應付利息,加到總利息中
let monthlyGraceInterest = principalLeft * monthRate
totalInterest += (gracePeriod * 12) * monthlyGraceInterest
//寬限期後還款期限還剩多少月
let monthPeriodLeft = Int(Double(monthlyPeriod) - gracePeriod * 12)
//計算寬限期後每月平均攤還率
let avgRepayRate = ((pow( 1 + monthRate, Double(monthPeriodLeft))) * monthRate) / ((pow(1 + monthRate, Double(monthPeriodLeft)) - 1))
//計算寬限期後每月平均還款金額
monthlyRepayment = mortgageAmount * avgRepayRate
//計算寬限期後每月利息並逐一加到總利息中
for _ in 1...monthPeriodLeft {
let monthlyInterest = principalLeft * monthRate
totalInterest += monthlyInterest
principalLeft = principalLeft - (monthlyRepayment - monthlyInterest)
}
//根據算出的總利息和貸款總額繪製甜甜圈圖。
drawDonut(principalRatio: mortgageAmount / (mortgageAmount + totalInterest), interestRatio: totalInterest / (mortgageAmount + totalInterest))
} else {
//沒有寬限期的狀況
//計算每月平均攤還率
let avgRepayRate = ((pow( 1 + monthRate, period * 12)) * monthRate) / ((pow(1 + monthRate, period * 12) - 1))
//計算每月平均還款金額
monthlyRepayment = mortgageAmount * avgRepayRate
for _ in 1...monthlyPeriod {
let monthlyInterest = principalLeft * monthRate
totalInterest += monthlyInterest
principalLeft = principalLeft - (monthlyRepayment - monthlyInterest)
}
drawDonut(principalRatio: mortgageAmount / (mortgageAmount + totalInterest), interestRatio: totalInterest / (mortgageAmount + totalInterest))
}
//算出總利息和每月平均攤還金額後就能顯示在結果label上
interestAmountLabel.text = formatizeNumber(totalInterest.rounded())
monthleRepaymentLabel.text = formatizeNumber(monthlyRepayment.rounded())
//試算後鎖住TextField不能更動,並且試算按鈕換成重置按鈕
lockForm()
sender.isHidden = true
resetButton.isHidden = false
}

重置按鈕不會將前次結果清掉,只是重置相關變數和可以讓使用者重新輸入數字:

@IBAction func resetFormButton(_ sender: UIButton) {
//解鎖並且重設清空表單
unlockAndRestForm()
//將重置按鈕換成顯示試算按鈕
sender.isHidden = true
calcButton.isHidden = false
//重設貸款總額、利息總額和每月平均攤還金額
mortgageAmount = 0
totalInterest = 0
monthlyRepayment = 0
}

Github:

--

--