辦公室最愛的活動『訂飲料』
作業:#20 訂飲料 App,上傳資料到後台part 1
我們出力寫App,Peter出錢請喝飲料!
很認真的想要開始寫一個比較完整的專案,就從這個訂飲料作業開始吧!(完成了App還有飲料喝XD),這個App其實蠻實用的,辦公室最愛喝飲料了啊,我想我辦公室一年胖一公斤的小資女生活,一定跟喝飲料脫不了關係(笑)。
ㄧ、萬事起頭難,從簡單的先開始
一開始還沒有方向要怎麼做,於是先做了一個飲料Menu的頁面:
使用功能:
1.scroll view delegate — 分頁和圖片
2.page control 的小圓點顯示目前在第幾頁
新增一個class,命名為IntroScrollViewController,寫入程式碼:
import UIKitclass IntroScrollViewController: UIViewController , UIScrollViewDelegate{@IBOutlet var drinksImageView: [UIImageView]!@IBOutlet var drinksScrollView: UIScrollView!@IBOutlet var drinksPageControl: UIPageControl!@IBOutlet var drinksView: UIView!override func viewDidLoad() {super.viewDidLoad()drinksScrollView.delegate = selfdrinksPageControl.numberOfPages = 13drinksPageControl.currentPage = 0}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {let currentPage = Int(drinksScrollView.contentOffset.x / drinksScrollView.frame.size.width)drinksPageControl.currentPage = currentPage}}
詳細的Scroll view的做法可參考這篇:
另外Page control的做法可參考這篇:
參考文章:
二、設計Storyboard畫面與UI元件的設定
是說我自以為是的拉了auto layout,導致cell裡的文字及picker被壓縮到,但因為想要先研究程式碼,所以這部分之後再處理囉。
飲料清單的說明:
1.先將飲料的品項及價格存成txt檔。
2.再將txt檔拖曳至專案內,記得要選擇Copy與Target,Copy是會複製一份檔案到專案,不會因為拖曳的檔案刪除就無法使用,而Target是在生成App時,會將檔案加進去。
新增一個Swift file去自訂所有array的型別:
// TeaData.swift// HappyOrderDrinks// Created by Julia Wang on 2019/8/20.// Copyright © 2019 Julia Wang. All rights reserved.//import Foundationstruct TeaChoicesData {var name: Stringvar drinks: Stringvar price: Stringvar size: Stringvar sugar: SugarLevelvar ice: IceLevelvar message: Stringvar tapioca:Stringinit() {name = ""drinks = ""price = ""size = ""sugar = .regularice = .regularmessage = ""tapioca = ""}}struct DrinksList {var name: Stringvar price: Int}enum SugarLevel:String{case regular = "正常", lessSuger = "少糖", halfSuger = "半糖", quarterSuger = "微糖", sugerFree = "無糖"}enum IceLevel:String{case regular = "正常", moreIce = "少冰", easyIce = "微冰", iceFree = "去冰", completelyiceFree = "完全去冰", hot = "熱飲"}
新增一個class,命名為OrderDrinksTableViewController,先宣告變數:
var teaorder = TeaChoicesData ()var drinksData : [DrinksList] = []var teaIndex = 0
該拉的IBOulet及IBAction也請記得拉一拉喲~然後要記得有沒有拉好,不然會出現這個我向小王子求救的問題:
在畫面載入的時候讀取飲料清單內的品項及價格:
Function:getTeaMenu()及updatePriceUI()
1.Function:getTeaMenu()
呼叫Bundle.main.url()來開啟txt檔案,在IOS中URL所代表的是位置,可以是設備上的檔案位置,也可以是網路上的檔案位置,當路徑的檔案不存在時會回傳nil,forResource是要讀取的檔案名稱,withExtension就是副檔名。
呼叫init(contentsOf url URL) throws來讀取檔案,因為讀檔不一定會成功,所以會加上try來處理Error Handling,如成功讀取則回傳檔案內的資料。
回傳的資料是靠\n分行的,所以可以利用components來將\n移除,移除後的資料會回傳到一個陣列(Array),再利用for in 將飲料名稱單獨取出儲存。
2.Function:updatePriceUI()
根據teaIndex的數值去顯示價格,teaIndex一開始宣告為0,所以載入金額就從第一個價格開始顯示。
Picker view的說明:
1.選擇Picker View後拖曳到Controller與dataSource及delegate做連結,這樣才能因為遵從UIPickerViewDelegate及UIPickerViewDataSource的Protocol而顯示資料。
2.如第一步所說的顯示資料需要遵從UIPickerViewDelegate與UIPickerViewDataSource的Protocol,所以要在Controller的Class前增加繼承UIPickerViewDelegate與UIPickerViewDataSource。
3.寫Picker View的Function。
4.顯示結果:選擇飲料同時出現對應的飲料價格。
是否加購白玉珍珠的Switch:
如果加購珍珠的Switch開啟,priceLabel顯示的價格再加10(加珍珠加十元喲),反則依據teaIndex選到的數值去顯示對應的價格。
三、重頭戲之上傳訂單的JSON Data到sheetDB
利用SheetDB提供的API將訂單資料傳到Google Sheets(試算表)內
這是我最害怕的部分,研究了三天三夜,練習實作一天終於完成了!
前置作業
1.先新增一份google試算表,並自訂欄位名稱。
2.複製試算表網址,貼到sheetDB去產生API。
3.Create API完成。
取得訂單資料
程式碼:
//訂單內容func getOrder() {guard let name = nameTextField.text, name.count > 0 else{ // 檢查姓名是否輸入return showAlertMessage(title: "忘記輸入你的名字囉!",message: "沒寫名字怎麼知道是誰點的啦XD") // 顯示必須輸入的提示訊息}//姓名資料teaorder.name = nameprint("訂購人:\(name)")//飲料資料teaorder.drinks = drinksData[teaIndex].nameprint("飲料品項:\(teaorder.drinks)")//容量資料if sizeSegmentedControl.selectedSegmentIndex == 0 {teaorder.size = "大杯"}else {teaorder.size = "中杯"}print("容量:\(teaorder.size)")//甜度資料switch sugarSegmentedControl.selectedSegmentIndex {case 0:teaorder.sugar = .regularcase 1:teaorder.sugar = .lessSugercase 2:teaorder.sugar = .halfSugercase 3:teaorder.sugar = .quarterSugercase 4:teaorder.sugar = .sugerFreedefault:break}print("甜度:\(teaorder.sugar.rawValue)")//冰度資料switch iceSegmentedControl.selectedSegmentIndex {case 0:teaorder.ice = .regularcase 1:teaorder.ice = .moreIcecase 2:teaorder.ice = .easyIcecase 3:teaorder.ice = .iceFreecase 4:teaorder.ice = .completelyiceFreecase 5:teaorder.ice = .hotdefault:break}print("冰度:\(teaorder.ice.rawValue)")//是否加珍珠if tapiocaSwitch.isOn {teaorder.tapioca = "要加珍珠"}else {teaorder.tapioca = "不加珍珠"}print("是否加購:\(teaorder.size)")//價格資料if let price = priceLabel.text {let money = (price as NSString).substring(from: 4) //因為顯示時有加上NT. ,所以移除後上傳teaorder.price = money}print("價格:\(teaorder.price)")//留言欄if let message = messageTextField.text {teaorder.message = messageprint("備註:\(message)")}}
AlertController提示訊息
前面取得訂單按下確認鍵後會先檢查姓名欄位使否為空值,如果是,就會呼叫showAlertMessage的Function:
guard let name = nameTextField.text, name.count > 0 else{ // 檢查姓名是否輸入return showAlertMessage(title: "忘記輸入你的名字囉!",message: "沒寫名字怎麼知道是誰點的啦XD") // 顯示必須輸入的提示訊息}
showAlertMessage的Function程式碼:
func showAlertMessage(title: String, message: String) {let inputErrorAlert = UIAlertController(title: title, message: message, preferredStyle: .alert) //產生AlertControllerlet okAction = UIAlertAction(title: "確認", style: .default, handler: nil) // 產生確認按鍵inputErrorAlert.addAction(okAction) // 將確認按鍵加入AlertControllerself.present(inputErrorAlert, animated: true, completion: nil) // 顯示Alert}
上傳訂單資料—JSON格式的Data(POST)
要注意SheetDB的POST API提供的Post功能需要什麼設定與JSON的結構,規範如下:
程式碼:
資料儲存位置url就是前面完成的Create API
『 https://sheetdb.io/api/v1/你的API ID 』
//傳送訂單資料至sheetDBfunc sendDrinksOrderToServer() {//POST的API需要知道上傳的資料是什麼格式,所以依照API Documentation的規定設定let url = URL(string: "https://sheetdb.io/api/v1/co2xognew7ev0")var urlRequest = URLRequest(url: url!)// 上傳資料所以設定為POSTurlRequest.httpMethod = "POST"urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")//post所提供的API,Value為物件的陣列(Array),所以利用Dictionary實作let confirmOrder: [String : String] = ["name": teaorder.name, "drinks": teaorder.drinks, "size": teaorder.size, "sugar": teaorder.sugar.rawValue, "ice": teaorder.ice.rawValue, "tapioca": teaorder.tapioca, "price": teaorder.price, "message": teaorder.message]//Post API 需要在物件(Object)內設定key值為data, value為一個物件的陣列(Array)let postData: [String: Any] = ["data" : confirmOrder]do {let data = try JSONSerialization.data(withJSONObject: postData, options: []) // 將Data轉為JSON格式let task = URLSession.shared.uploadTask(with: urlRequest, from: data) { (retData, res, err) in // 背景上傳資料NotificationCenter.default.post(name: Notification.Name("waitMessage"), object: nil, userInfo: ["message": true])}task.resume()}catch{}}
確認訂單的Button
以上Function都寫好之後就是拉確認訂單按鈕的IBAction,在按下按鈕後呼叫取得訂單及上傳訂單的Function。
@IBAction func confirmButton(_ sender: Any) {getOrder()sendDrinksOrderToServer()}
訂飲料App目前進度就先到這邊啦,還有很多地方需要改善,但希望先把完整的功能做出來再去細修,以上有想進一步了解的步驟歡迎一起討論,我已經盡量把我理解的意思寫出來了,但還是有很多可能會讓人看不懂的地方,畢竟我參考學長姊的文章很多地方也看不懂,看了三、四遍才慢慢理解(還是其實是我自己的問題XD?太笨??)總之還是希望大家可以互相交流討論,一起用心體會(笑)!
如果還有問題請找iOS界小王子-彼得潘蜘蛛人解惑!(名號真的好多XD)。
第二集看這裡
參考文章:
如果有值得大家參考的地方再麻煩大家幫我拍拍手喲,謝謝大家耐心閱讀🙇♀️