[iOS] Udemy 004: Quiz App

QuizApp demo
Udemy課程截圖
  • 使用None Project下載icons
  • 建立iOS Single View Application project with CoreData
建立Single View Application
  • 將icon放入Assets.xcassets folder
Add icons into project
  • 進入quiz.xcdatamodeld建立entities與Attributes
Attributes of Categories Entities
Attributes of Questions Entities
Attributes of Answers Entities
  • 進入Main.storyboard,點選View Controller:
  1. 加入Navigation Bar:Editor -> Embed In -> Navigation Controller
  2. 新增Table View並填滿整個View:Size inspector -> Arrange -> Fill Container Horizontally/Vertically
  3. 在Table View裡加入一個Table View Cell並設ID爲”cell”:Attribute inspector -> Identifier -> “cell”
  • 進入ViewController.swift
  1. 在class ViewController後面加上UITableViewDelegate和UITableViewDataSource
  2. 在class中加入三個table view的function
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
<#code#>
}
  • 進入Main.storyboard,新增一個View Controller
  1. 新增Label:Size inspector -> Arrange -> Fill Container Horizontally、Attribute inspector -> Lines -> “2”、Size inspector -> Height -> “42”
  2. 新增Button “Give Up!”:Size inspector -> Arrange -> Fill Container Horizontally
  3. 新增一個View放在Button下面:Size inspector -> Arrange -> Fill Container Horizontally、Attribute inspector -> Background -> Group Table View Background Color
  4. 將View Controller的背景設成橘色、Label背景設爲白色
  5. 將Button放進View中:Size inspector -> Arrange -> Fill Container Horizontally/Center Vertically In Container
  • 編輯cell內容
  1. 新增一個UIImageView
  2. 新增Label顯示類別:Attribute inspector -> Font -> System Semibold 17.0
  3. 新增Label顯示題數:Attribute inspector -> Font -> System Light 15.0
  4. 點選cell建立segue:Attribute inspector -> Accessory -> Disclosure Indicator、按住control鍵後將cell拉進ViewController建立segue
  5. 設定ViewController的Title:點選Navigation Item的Attribute inspector -> Title -> “Super Quiz”
  • 建立tableViewCell的class

於class customTableViewCell中建立cell中各元件的IBOutlet:

@IBOutlet weak var cellImage: UIImageView!
@IBOutlet weak var topLabel: UILabel!
@IBOutlet weak var bottomLabel: UILabel!
  • 將Table View拖曳至ViewController建立Data source關聯
  • 進入ViewController.swift完成function: cellForRowAt()
    此時還未設定CoreData,所以先寫入固定的text
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! customTableViewCell
cell.topLabel?.text = "President"
cell.bottomLabel?.text = "3"
cell.cellImage?.image = UIImage(named: "president")
return cell
}
  • 設定AutoLayout
  • 增加4個答題選項的button,並使用StackView來完成AutoLayout:
  1. 每一列(兩個button)設爲一個StackView,共兩列。
  2. 兩列設爲一個StackView,將此StackView四周spacing設爲0(填滿整個橘色部分)。
  3. 進入pin將兩列StackView設爲Equal Height(在外層StackView中平均分配兩個列)。
  4. 將每一列中的兩個button設爲Equal Width(在內層的StackView中平均分配兩個button)。
  • 建立new view controller的class
  • 於detailViewController中建立IBOutlet和IBAction
@IBOutlet weak var questionLabel: UILabel!
@IBOutlet weak var button1: UIButton!
@IBOutlet weak var button2: UIButton!
@IBOutlet weak var button3: UIButton!
@IBOutlet weak var button4: UIButton!
@IBAction func GiveUpButtonClick(_ sender: Any) {
}
@IBAction func button1Click(_ sender: Any) {
}
@IBAction func button2Click(_ sender: Any) {
}
@IBAction func button3Click(_ sender: Any) {
}
@IBAction func button4Click(_ sender: Any) {
}
  • 建立CoreDataManager class (NSObject)
  • 進入quiz.xcdatamodeld,選取三個entity後至上方選單選擇:Editor -> Create NSManagedObject Subclass… -> Next -> Next -> Create
  • 建立完會自動生成*+CoreDataClass.swift和*+CoreDataProperties.swift
  • 將*+CoreDataClass.swift改名爲*.swift
  • 進入每個entity的Data Model inspector: 將Class -> Module設爲“Current Product Module”、Class -> Codegen設爲“Manual/None
  • 進入CoreDataManager.swift建立存取Core Data的function
static func getManagedObject() -> NSManagedObjectContext {...}
static func getData(entityName:String, predicate:NSPredicate?=nil) -> [NSManagedObject]{...}
static func loadCategories(){...}
static func loadPresidentQuestions(){...}
static func loadPresidentAnswers(){...}
  • 進入ViewController.swift,使用CoreDataManager class取得Core Data
  1. 在viewDidLoad()中使用CoreDataManager.getData()取得所有categories並存進陣列categories中。
  2. 使用numberOfRowsInSection()取得列數(使用categories.count取得陣列總數)。
  3. 在cellForRowAt()中使用dequeueReusableCell()建立每列的資料。
  4. 使用willSelectRowAtIndexPath()取得user選取的列並存入變數selectCategoryId中。
  5. 使用prepareForSegue()將變數selectCategoryId傳至DetailViewController(在DetailViewController中要定義一個變數存放selectCategoryId。
  • 進入DetailViewController.swift
  1. 在viewDidLoad()中使用CoreDataManager.getData()取得所有questions/answers並存進陣列questions/answers中。
  2. 新增function: viewDidAppear()
    將ViewController傳入的categoryId指定CoreDataManager.getData()要取得哪個categories的questions;
    call nextQuiz()隨機顯示一題question
    call assignButtonText()顯示各選項
  3. 新增function: isCorrectSelection(),按下button後檢查是否為此題的正確答案。
  4. 新增"Next" button,在viewDidLoad()中使用UIBarButtonItem()在navigation bar的右方建立按鈕,按下後會執行nextQuiz()隨機選取一題question。
  5. 新增以下function:
    nextQuiz()隨機選取一個question並將該題的選項存在answerResults的陣列中;
    assignButtonText()將隨機選取的question所屬的answer顯示在button上;
    createAnswerIds()取得question的answer choices。
    ShuffleArray()確保陣列內容不會重複顯示。
  6. 完成IBAction: GiveUpButtonClick(),將正確答案顯示在button 上。
  • 進入ViewController.swift,修改Navigation Bar的顏色
    在viewDidLoad()中設定navigationController.navigationBar的屬性
  • 其他筆記:
  1. 老師detail view controller放了9個button但教學上只用了4個,所以我直接只放4個button。
  2. 一開始將語法都修改成swift3之後卻一直build failed,設breakpoint找到在fetch的地方會error(error message: Down-casted Array element failed to match the target type),後來比對了老師提供的example發現在xcode自動產生core data的class file中有一行@objc(class),comment out這行code 之後就build成功了= =
  3. 把request增加returnsObjectsAsFaults = false的屬性讓fetch時不會回傳 data<fault>的資料
  4. 在CoreDataManager裡多寫了一個deleteData()的function,因爲一開始一直重複建立data…
  5. 在nextQuiz()取得random number的地方,本來random範圍是題數但一直得到0,後來把random範圍改成10再去mod題數。
  6. 課程只完成president這個category,其他部分未實作。
  7. 首頁tableview顯示各類別有幾個題數的也還沒實作,這部分應該是要建立category和question的relationship。