#4 研究UI元件|季節桌布App
結合各種UI元件和程式碼做了可以產生客製化桌布的App。
前半段會先介紹各個UI元件對應的功能,程式碼說明則放在後面。
使用到的UI元件有下面幾種(由上而下)
- Segmented Control 切換季節
- Date Picker 變更日期後會自動改變顯示的桌布季節
- Switch 切換是否顯示上方的季節名稱圖片
- Slider 變更背景圖片的透明度
- Button 按下“Generate”按鈕後產生客製化的全螢幕季節桌布
- Page Control 切換左右頁面切換季節
所以其實Segmented Control、Date Picker和Page Control都是用來切換季節的,只是想練習每個元件的用法。
這邊先針對每個UI元件詳細說明功能。
Segmented Control
可以選擇四個不同的季節,點選後依照季節更換桌布的樣式,這邊選取時會同步更新最下方的Page Control頁面。
Date Picker
選擇日期後會自動判斷指定日期的月份,改變桌布的季節,並同時更新Segmented Control及Page Control的選項。
Switch
開關選擇是否顯示上方的季節名稱圖片。
Slider
滑動改變背景圖片的透明度。
Page Control
由左到右四個頁面分別為春夏秋冬,點選可以切換季節,桌布樣式會隨著季節變更,並同步更新最上方Segmented Control的選項。
Button
點選”Generate” Button後會移動到另外一個View Controller,全螢幕顯示使用者客製化的桌布。因為沒有Navigation Bar的關係,這邊設計成在全螢幕頁面點選任一處即可回到原本的頁面。為了方便截圖使用,在截圖頁面時會隱藏最上方的Status Bar。
程式碼說明
這個專案由兩個ViewController所構成,因此分兩個部分來寫所用到的程式碼。
主畫面ViewController
先介紹存有各個UI元件選項的主畫面。
建立IBOutlet
建立背景圖片及上方季節名稱圖片的IBOutlet
@IBOutlet var backgroundImageView: UIImageView!
@IBOutlet var seasonNameImageView: UIImageView!
建立各個UI元件的IBOutlet
@IBOutlet var segmentedControl: UISegmentedControl!
@IBOutlet var datePicker: UIDatePicker!
@IBOutlet var labelSwitch: UISwitch!
@IBOutlet var transparencySlider: UISlider!
@IBOutlet var generateButton: UIButton!
@IBOutlet var pageControl: UIPageControl!
定義變數
定義seasons變數,以便之後用index來選擇各個季節,切換各個季節的背景圖片及季節名稱圖片。
let seasons = ["spring", "summer", "autumn", "winter"]
定義產生季節桌布會用到的變數,並給予預設值。預設的季節是秋天,背景透明度為0.5,季節名稱圖片預設為顯示。
var season: String = "autumn"
var transparency: CGFloat = 0.5
var showLabel: Bool = true
viewDidLoad()
畫面讀取後會先用checkSeason()
輸入今天日期Date.now
確認今天的季節,在checkSeason()
中會變更變數season
的值來切換季節,再使用seasonChanged()
來變換畫面。
override func viewDidLoad() {
super.viewDidLoad()
checkSeason(date: Date.now)
seasonChanged()
}
指定各個UI元件的IBAction
用sender.selectedSegmentIndex
取得Segmented Control被選擇的index,並將season
變更為使用者所選擇的季節,並執行seasonChanged()
來變更桌布的季節樣式。
@IBAction func segmentedControlChanged(_ sender: UISegmentedControl) {
season = seasons[sender.selectedSegmentIndex]
seasonChanged()
}
用sender.date
取得Date Picker的日期後,用checkSeason()
來變更季節變數,並執行seasonChanged()
來變更桌布的季節樣式。
@IBAction func datePickerChanged(_ sender: UIDatePicker) {
checkSeason(date: sender.date)
seasonChanged()
}
用sender.isOn
來確認Switch的狀態,如果為ON的話會回傳true,OFF則會回傳false。在Switch為ON狀態時將季節名稱圖片的isHidden設為false來顯示圖片,反之則將isHidden設為true來隱藏圖片。
@IBAction func labelSwitchChanged(_ sender: UISwitch) {
showLabel = sender.isOn
seasonNameImageView.isHidden = showLabel ? false: true
}
用sender.value
來取得UISlider的值,這邊是要變更背景的透明度,所以將背景的imageView.alpha
設定為所取得的透明度值(要先變更為CGFloat型態)
@IBAction func transparencySliderChanged(_ sender: UISlider) {
transparency = CGFloat(sender.value)
backgroundImageView.alpha = transparency
}
用sender.currentPage
取得Page Control中使用者所選取的頁面index,並將season變數變更為相對應的季節。
@IBAction func pageControlChanged(_ sender: UIPageControl) {
season = seasons[sender.currentPage]
seasonChanged()
}
以Date來確認季節的checkSeason()
這邊首先取得與日期相對應的英文月份名稱,這邊使用的是DateFormatter()
這個方法並指定格式為"LLLL"。取得月份名稱後,使用switch
來判斷該個月份是哪個季節,並變更season
變數為相對應的季節名稱。
func checkSeason(date: Date) {
// 取得與日期相對應的英文月份名稱
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "LLLL"
let month = dateFormatter.string(from: date)
// 依照不同月份變更season變數
switch month {
case "March", "April", "May": season = "spring"
case "June", "July", "August": season = "summer"
case "September", "October", "November": season = "autumn"
case "December", "January", "February": season = "winter"
default: season = "autumn"
}
}
變更相對應的季節樣式的seasonChanged()
當季節變更時,觸發這個方法。這個方法會:
- 變更背景圖片和季節名稱圖片
- 變更UISwitch、UISlider、UIButton及UIPageControl的顏色以符合各個季節的設計
- 將SegmentedControl及PageControl切換到指定的季節
func seasonChanged() {
// 變更背景圖片及季節名稱圖片
backgroundImageView.image = UIImage(named: "\\(season)-background")
seasonNameImageView.image = UIImage(named: season) // 變更UI元件顏色為季節相對應的配色
labelSwitch.onTintColor = UIColor(named: season)
transparencySlider.minimumTrackTintColor = UIColor(named: season)
generateButton.backgroundColor = UIColor(named: season)
pageControl.backgroundColor = UIColor(named: season) // 將SegmentedControl及PageControl切換到指定的季節
let seasonIndex: [String: Int] = ["spring": 0, "summer": 1, "autumn": 2, "winter": 3]
segmentedControl.selectedSegmentIndex = seasons.firstIndex(of: season)!
pageControl.currentPage = seasonIndex[season]!
}
這邊有設定好各個季節名稱的Color Set了所以可以直接用UIColor(named: season)
呼叫 。
切換到下一頁用的prepare()
這邊設定Segue來傳遞變數到全螢幕顯示季節桌布的ViewController。傳遞的變數有存放季節名稱的season
、決定背景透明度的transparency
的數值和是否顯示季節名稱的showLabel
(Bool)變數。
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let controller = segue.destination as? PhotoViewController
controller?.season = season
controller?.transparency = transparency
controller?.showLabel = showLabel
}
自訂字型
這個專案的UILabel的字型全都直接在Storyboard更改,Segmented Control的字型則使用程式碼變更,在viewDidLoad()加入下列程式碼指定字型。
segmentedControl.setTitleTextAttributes([NSAttributedString.Key.font: UIFont(name: "Mali-Regular", size: 15)!], for: .normal)
顯示季節桌布的PhotoViewController
建立一個顯示客製化完成桌布用的ViewController。
建立IBOutlet
這個畫面其實只由兩張圖片,背景圖片及季節名稱圖片組合而成。
@IBOutlet var backgroundImageView: UIImageView!
@IBOutlet var seasonNameImageView: UIImageView!
設定由主畫面傳遞過來的變數
var season: String = "autumn"
var transparency: CGFloat = 0.5
var showLabel: Bool = true
顯示指定的桌布樣式
由主畫面傳遞過來的變數來指定背景圖片、季節名稱圖片,並設定背景圖片透明度及是否顯示季節名稱圖片。
override func viewDidLoad() {
super.viewDidLoad()
backgroundImageView.image = UIImage(named: "\\(season)-background")
seasonNameImageView.image = UIImage(named: season)
backgroundImageView.alpha = transparency
seasonNameImageView.isHidden = showLabel ? false: true
}
點選螢幕回到上一頁
由於沒有NavigationBar的關係,這邊多設定了一個方法handleTap()的方法來讓使用者可以在點選畫面時回到上一頁。
首先在畫面最上層加入一個背景顏色為clear的View(這邊取名為tapView),目的是要讓使用者點選到這個View時可以觸發回到上一頁的指定。
@IBOutlet var tapView: UIView!
在viewDidLoad()加入下列指令,在畫面被點選時執行handleTap。
let tap = UITapGestureRecognizer(target: self, action: #selector(goBackToPreviousPage(_:)))
tapView.addGestureRecognizer(tap)
當畫面被點選時下面指令會被執行,其中使用到dismiss()的方法來回到上一個畫面。
@objc func goBackToPreviousPage(_ sender: UITapGestureRecognizer) {
if sender.state == .ended {
self.dismiss(animated: true, completion: nil)
}
}
隱藏畫面的Status Bar
由於這個畫面想要顯示使用者設計完成的桌布,並方便截圖下載,因此這邊加入下列程式碼將Status Bar隱藏。
override var prefersStatusBarHidden: Bool {
return true
}
原本只是要練習各種UI元件,後來想說乾脆加上一些功能,雖然只是一個有小小功能的App,但做完之後很有成就感。後來又加上了App Icon及Launch Screen,關於這一部分會寫在下一篇。
附上上一份作業文章 − 研究UI元件