IOS13 APP『Swift5』實例說明 —算數練習App(基礎篇)

Shin Chao
18 min readDec 23, 2019

--

【 新增字型】

有的時候不知道如何設計App畫面時,不同的字型也是一個很好的方式讓畫面變得更容易吸引使用者,所以來說明一下如何新增字型。

網路上有許多免費的字型,只要google一下免費字型,就會出現一堆,像google也有很多免費的字型提供,如有需要都可以免費下載。

下載後的字型檔,副檔名是ttf,那要如何加入Xcode內呢?

Step 1 : 將字型檔直接拖曳到瀏覽區塊(Navigator area)的專案內,也可以在專案內建立一個專門的目錄,放入你想加入的字型檔,下面我加入了兩個字型檔到FontsGroup的目錄內。加入時記得 將Add to targets 選取,這樣在生成 App 時才會將字型檔包進去。

Step 2 : 加入字型檔後,要選擇專案Targets下的 App,然後再選info,在Info 頁面的表格內點選任何一行,會出現+,然後點選+,新增Fonts provided by application。

Step 3 : 選 Fonts provided by application 旁的三角形就會展開,展開後可以看到預設的 Item 0,在Item 0的Value填入剛剛拉近的字型檔名稱(wp010–08.ttf),因為我們要新增兩個字型檔,所以還需要新增一個Item 1,在Item 1的Value填入另一個字型檔名稱(genkai-mincho.ttf),如果還需要增加字型那就繼續增加新的Item就可。這樣不論是在 storyboard 或是程式中,都可以指定你加入的字型。

最後需要注意的是在 storyboard 或是程式中,並不是顯示字型檔名稱,而是字型名稱,那如何得知字型名稱呢?其實最簡單的方式就是直接安裝到mac中,所以只要雙點擊字型檔,然後選擇Install Font,就會將字型安裝到 Mac上。

此時會跳出 Font Book App 並顯示剛剛安裝完的字型,若之後想找也可以直接開啟 Font Book,選取User,就會顯示你之前所有安裝的字型。旁邊的info就會顯示字型的名稱,而PostScript name與Family就是字型名稱。

而PostScript name是用在程式生成 UIFont的時候,而Family name是用在 Interface Builder 設定字體時,要記得在設定字型時要先選取Custom,才能選你要的字型。

let customFont = UIFont(name: "HanWangKaiMediumChuIn", size: 58)

【 收鍵盤】

在使用者輸入完資訊後,鍵盤需要收起,那如何收鍵盤呢?有幾種方式可以做到。

  • 方法1 : view.endEditing(true)

這方式是最簡單的方式,直接從controller的view呼叫view.endEditing(true)就會收起鍵盤。下面說一下使用的方式。

按下一個按鈕(Button)讓鍵盤消失 :

那就直接從controller的view呼叫view.endEditing(true)就會收起鍵盤,所以在ButtonPress的Action內呼叫view.endEditing(true)讓鍵盤收起。。

@IBAction func confirmButtonPress(_ sender: Any) {    view.endEditing(true)}

點擊空白處讓鍵盤消失 :

複寫 touchesBegan function這function,裡面一樣呼叫view.endEditing(true)讓鍵盤收起。

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {    view.endEditing(true)}
  • 方法2 : TextField.resignFirstResponder()

這方法必需是目前游標所在的text field呼叫才有用,因為這App在輸入計算總和後,需要按下確認的按鈕,所以游標會在AnsTextField上,所以呼叫.resignFirstResponder()就會讓鍵盤收起,因為不只一個TextField,所以程式碼內並未使用這方式。

@IBAction func confirmButtonPress(_ sender: Any) {    ansTextField.resignFirstResponder()}
  • 方法3 : @IBAction func

這方式是按下虛擬鍵盤的return鍵時讓鍵盤收起,而function內部不需要任何的程式碼就會讓鍵盤收起,如果想在按下return時做某些動作可以在這裡面加程式,那如何使用呢?其實要先建立action,如從storyboard中的text field拉一個Action到controller中,但是要記得要選”Did End On Exit”,這樣在按下return時鍵盤就會收起。

@IBAction func userNameTextFieldDismissKeyboar(_ sender: Any) {}

【 判斷是否進入下一個View】

有時候需要判斷是否可以進入下一個畫面,例如輸入帳號密碼時,任一個沒有輸入,因該都無法進入下一個畫面,所以需要做些判斷,看是否能進入下一個畫面,這邊說兩種簡單的方式吧。

一個就是由程式觸發segue,那可以在按下按鈕時將判斷加入,所以需要由Controller拉Segue到下一個畫面,再由程式判斷是那一個Button執行。這可以參考之前寫的下面這篇文章。

在這App中有特別使用,但是要記得需要從controller中拉segue到下一個ViewController與加上segue ID才行。如何加segue ID呢?其實就是點選要設定的Segue『如下圖示步驟1』之後,選擇Show The Attributes inspector『如下圖示步驟2』,之後在Storyboard Segue裡面設定Identifier『如下圖示步驟3』

程式部分就可以在按下Button的@IBAction func內加入判斷後,是否要進入下一個畫面。

@IBAction func confirmButtonPress(_ sender: Any) {
view.endEditing(true)
if userNameTextField.text! != ""{
performSegue(withIdentifier: "selectViewSegueId", sender: sender)
}
}

第二個方式則是使用shouldPerformSegue(),在這func內加入判斷的程式之後,使用回傳true或是false來,來決定是否要跳到下一個view controller,如果是true則進入下一個view controller,false則繼續停留在目前的view controller。像範例中會判斷是否有輸入3種水果的個數與總和,若缺少一個則不會進入結果頁面。

override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if( ansTextField.text! != "" && firstFruitTextField.text! != "" && secondFruitTextField.text! != "" && threeFruitTextField.text! != ""){
return true
}else{
return false
}
}

【 view controller顯示與消失呼叫方法的順序】

這邊主要是說明,當畫面顯示時,要在哪裡呼叫自己所寫的程式,來達到想要顯示的畫面,那你就必須了解下面6個方法(method),那就來介紹一下這六個方法分別是做什麼的。

  • loadView():載入ViewController,並初始化view。
  • viewDidLoad():
    controller的畫面完成載入時,也就是當 view 被載入到記憶體之後,但是畫面還未顯示以前,只會呼叫一次,適合在此做初始化的工作。
  • viewWillAppear():
    這是每次畫面產生時都會被呼叫,這方法是在 view 將要顯示現在的畫面,但是畫面還沒有出現。這時可以修改view上相關的物件,如button是否要隱藏,還是label的文字等等…。但是要注意如果做太多事會將畫面卡住,所以最好就是只更新物件就好。
  • viewDidAppear():
    這也是每次畫面產生時都會被呼叫,只不過是在畫面出現後才會被呼叫,所以在 viewWillAppear 結束之後顯示出畫面,就會載入這個方法。
  • viewWillDisappear():
    這個方法是當 view 將要被移除,在畫面消失前呼叫的。
  • viewDidDisappear():
    這是當 view 被移除之後,畫面已經消失後,會被呼叫的。

在這次開發的App中,因為計算結果的畫面(ShoppingAnsViewController),回到計算畫面(ShoppingViewController)時,每次要重新產生水果數量與種類,所以要在viewWillAppear()的時候呼叫計算與顯示水果的func,因為計算畫面的viewDidLoad()不會再從計算結果的畫面返回時重新載入,所以必須在viewWillAppear()時處理。所以這邊要知道若是view controller沒被移除,則viewDidLoad()不會被在呼叫。

所以用下圖說明一下,簡單的說法就是當第1個view controller未被除,再次回到第1個view controller時loadView()與viewDidLoad()都不會再被呼叫,但是像第2個view controller則每次進去,都會再次呼叫loadView()與viewDidLoad()

這邊要注意一下,因為在 iOS 13之後的版本,新的畫面如果沒設定會堆疊在舊的畫面上,所以前一個畫面並未被Disapper,所以也不會如上所說的樣子從第2個view controller返回時,再呼叫第1個view controller的viewWillAppear(),因此必須要讓present modally顯示全螢幕,才如上面所說。

【 IOS 13之後的present modally如何變全螢幕】

在 iOS 13之後的版本,present modally 變成漂亮的卡片設計,也就是在IOS13版本之前,新的頁面是全螢幕,但是在IOS13之後的版本,是用卡片式的設計,這時新的頁面不會佔滿整個螢幕,而是像張卡片般疊在之前的頁面上,卡片式的堆疊只會顯示2張,並不會一直堆疊上去。

但是卡片式的效果,也不是每個是每個設計都適用,在這次寫的App就不適合了,因為像顯示計算畫面時像一個卡片貼在之前的畫面上看起來就會很奇怪。那如何顯示成之前的全螢幕呢?

方法1 : 點選要設定的controller『如下圖示步驟1』之後,點選Show The Attributes inspector『如下圖示步驟2』,之後在View Controller裡面設定Presentation『如下圖示步驟3』設成 Full Screen。

方法2 : 另一種方式就是從程式設定,將新頁面 controller 的 modalPresentationStyle 設為 fullScreen就可以。

【 如何在@IBAction func判斷多個Button】

在Storyboard可以將多個Button連接的一個@IBAction func,那要如何知道是哪一個Button被按下呢?其實只要在每個Button中設定不同的Tag就可。步驟如下:

Step 1 : 點選要設定的Button『如下圖示步驟1』之後,選擇Show The Attributes inspector『如下圖示步驟2』,之後在View裡面設定Tag『如下圖示步驟3』。

Step2 : 將所有的Button拉到同一個@IBAction func之中。方法就是先用一個Button建立一個@IBAction func,之後再將其他的Button拉到這個@IBAction func就可。

Step 3 : 當Button按下時,會呼叫這@IBAction的func,這時候sender.tag就可以用來判斷是哪一個Button,而傳入的sender.tag就是之前在Storyboard內設定的Tag,所以就可以使用這判斷是哪一個Button被按下。

【 使用UIAlertController來顯示警告訊息】

當textfield未輸入時,不能進入下一個畫面,這時候就需要跳出通知或是警告畫面,告訴使用者還有哪個欄位必須要輸入資訊才能進入下一個畫面,所以這時候就需要使用UIAlertController來完成這功能,那就來介紹一下UIAlertController如何使用吧。

UIAlertController(title: String?, message: String?, preferredStyle: UIAlertController.Style)

UIAlertController的參數有3個,分別為下:

  • title : 警告視窗的標題。
  • message : 要通知會是警告的訊息。
  • preferredStyle : 這邊有兩種樣式,一種就是.alert,這是中央視窗跳出警告視窗。另一種是.actionSheet,就是由下而向上彈出的視窗。

如果想在視窗是顯示text filed讓使用者輸入,那就需要在UIAlertController生成的controller上面加上.addTextField,這樣就會在視窗上出現text filed。

let controller = UIAlertController(title: "不要急!", message: "輸入姓名", preferredStyle: .alert)
controller.addTextField { (textField) in
textField.placeholder = "姓名"
textField.keyboardType = UIKeyboardType.default
}

雖然使用了UIAlertController產生了警告視窗,這時候還需要使用 UIAlertAction 來生成視窗上顯示的按鈕。不然警告視窗無法關閉,所以至少要提供一個按紐,這樣才能點選按紐關掉視窗。

UIAlertAction(title: String?, style: UIAlertAction.Style, handler: ((UIAlertAction) -> Void)?)

UIAlertAction的參數有3個,分別為下:

  • title : 按鈕顯示的文字。
  • style : 控制文字的樣式顏色。
  • handler : 點選後是否要做其他的事,如果有需要,則在這邊加入closure的程式 。如果只是要關閉視窗,那就填入nil就好。
var alertMessage = ""
if ansTextField.text! == ""{
alertMessage = "你忘了輸入計算結果"
}else if firstFruitTextField.text! == ""{
alertMessage = "你忘了輸入\(fruits[0])有多少個"
}else if secondFruitTextField.text! == ""{
alertMessage = "你忘了輸入\(fruits[1])有多少個"
}else{
alertMessage = "你忘了輸入\(fruits[2])有多少個"
}
let controller = UIAlertController(title: "不要急!", message: alertMessage, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
controller.addAction(okAction)
present(controller, animated: true, completion: nil)

如果需要第二個按鈕,那就是呼叫兩次UIAlertAction,來建立第二個按鈕或是多個按鈕。剛剛還有說到可以在視窗輸入textField,那輸入完的資訊,可以從 UIAlertController 的 textFields 中讀取 text filed的內容,會在controller.textFields?[0].text內,那為什麼是在[0],因為可以有很多的text filed,所以只有一個的時候就是在0。

【 UIButton邊框顏色】

這邊有一個需要注意的地方,就是邊框可以在程式內設定,也可以在Storyboard中設定,但是邊框的顏色.layer.borderColor只能在程式中設定,邊框的顏色在Storyboard內設定並沒有效果。

.layer.borderColor = UIColor.blue.cgColor

但是圓角(.layer.cornerRadius)與邊框寬度(.layer.borderWidth)可以在程式內或是Storyboard設定都可以。

.layer.cornerRadius = 20.layer.borderWidth = 1

在Storyboard中點選要設定的Button『如下圖示步驟1』之後,選擇Show The identity inspector『如下圖示步驟2』,之後在Key Path裡面新增layer.cornerRadius與layer.borderWidth,Type都是選擇Number『如下圖示步驟3』。

--

--