作業#20 — 點餅乾遊戲(cookie clicker) App

練習 IBOutlet, IBAction、設置 Segmented Control、設置點擊音效。

.作業來源:

Topics

I. 設置 Segmented Control
II. 設置按鈕
III. 設置音效

I. 設置 Segmented Control

  1. 型別:UISegmentedControl
  2. 常用 properties
    > selectedSegmentIndex: 獲取或設置當前選中的選項索引。
    > setTitle (_Name: String, forSegmentAt: Int) : 設置指定索引處的標題。(* 這裡的名稱會覆蓋 main inspector 處的命名。)
    > setImage (image: UIIMage, forSegmentAt: Int) : 設置指定索引處的圖片。
    > selectedSegmentTintColor : 設置控件選中時的顏色。
    > backgroundColor : 控件背景顏色。
  3. 常用 methods
    > insertSegment (withTitle:String, at:Int, animated:Bool ) : 在指定位置插入一個新的選項。
    > setEnabled ( Bool, forSegmentAt: Int ) : 設置指定索引處的選項是否可用。
// properties: 
instrumentSegmentedControl.setTitle("木魚", forSegmentAt: 0)
instrumentSegmentedControl.setTitle("敲鐘", forSegmentAt: 1)
instrumentSegmentedControl.selectedSegmentTintColor = UIColor(red: 1, green: 0, blue: 0, alpha: 1)
instrumentSegmentedControl.backgroundColor = UIColor(red: 0, green: 0, blue: 1, alpha: 1)

//methods:
instrumentSegmentedControl.insertSegment(withTitle: "測試用", at: 1, animated: false )
instrumentSegmentedControl.setEnabled(false, forSegmentAt: 1)
設置 Segment 名稱、顏色、插入 “測試用”segment、設置為不可用 (false)

II. 設置按鈕

  1. 型別:UIButton
  2. 常用 properties
    > tintColor : 設置按鈕顏色。
    > backgroundColor : 設置背景宴色。
    > layer.borderWidth : 設置邊框寬度。
    > layer.borderColor : 設置邊框顏色。
    > titleLabel: UIButton 中的 UILabel,可通過它設置文字顯示樣式,例如字體、顏色、對齊方式等。
    > imageView: UIButton 中的 UIImageView,可通過它設置圖片顯示樣式,例如顯示模式、圖片內容填充方式等。
  3. 常用 methods:
    > setTitle (String, for: status) : 設置按鈕的顯示文字。常用 status 為 .normal 跟 .highlighted。
    > setTitleColor (UIColor, for: status) : 設置按鈕文字顏色。
    *留意若想透過程式碼設置設置文字顏色(setTitleColor ),則一定要使先用程式碼來設置文字以及狀態(setTitle)!
    > setImage (UIimage, for: status) : 設置按鈕的圖片和狀態。也可以設置為 SF symbols,若要調整 symbols 尺寸可見下方範例。
    > sizeToFit() : 根據按鈕的內容調整按鈕的大小。
    > setEnabled (Bool, animated: Bool) : 設置按鈕是否可用,可選擇是否帶動畫
  //properties
resetButton.tintColor = UIColor.lightGray
resetButton.backgroundColor = UIColor.red
resetButton.setTitleColor(UIColor.lightGray, for: .highlighted)

//methods
resetButton.setTitle("完成", for: .normal)
resetButton.setTitleColor(UIColor.black, for: .normal)


// *setImage 設置成 symbol Image 並調整 symbol 大小範例:
let symbolConfiguration = UIImage.SymbolConfiguration(scale: .small)
myButton.setImage(UIImage(systemName: "arrow.uturn.backward", withConfiguration: symbolConfiguration), for: .normal)
文字正常時為黑色、按鈕為灰色、背景色紅色

*所謂的 “常用” properties 及 methods 只是我個人覺得自己比較可能會使用到的,沒有一定標準~

III. 設置音效


//需先導入多媒體設置工具集
import AVFoundation

//在使用 AVAudioPlayer 播放音頻時,我們需要創建一個 AVAudioPlayer instance。
class ViewController: UIViewController {

var myAudioPlayer: AVAudioPlayer?

override func viewDidLoad() {
super.viewDidLoad()

/*
所有的應用程式資源文件 (例如圖片、音頻文件等) 都包含在應用程式的 Bundle 中。
Bundle.main 表示應用程式的主 Bundle,也就是包含所有應用程式資源的 Bundle。可以使用 Bundle.main.url(forResource:withExtension:) 方法來獲取應用程式 Bundle 中指定資源的 URL。
*/

//"myAudioFile" 是指要獲取的音頻文件的文件名 (不包含擴展名)。"mp3" 是指音頻文件的擴展名。
let myUrl = Bundle.main.url(forResource: "myAudioFile", withExtension: "mp3")!

/*
do { try } catch {} 是 Swift 中用來處理錯誤的結構,通常稱為錯誤處理機制。
在 do 區塊中,我們可以使用 try 關鍵字來調用可能會引發錯誤的函數、方法或操作。如果這些操作沒有出現錯誤,程式就會繼續往下執行。
如果出現錯誤,就會自動跳轉到 catch 區塊中,進行錯誤處理。
*/
do {
myAudioPlayer = try AVAudioPlayer(contentsOf: myUrl)
//這裡可以順便調整音量(如果需要的話)
myAudioPlayer?.volume = 0.8
} catch {
print("播放音效檔案出現錯誤:\(error)")
}
}

// IBAction 連結按鈕功能。當按下按鈕時,會開始播放音頻,而且每次按下按鈕都會從頭開始播放。
@IBAction func clickToPlayAudioButton (_ sender: Any) {
//啟動播放音頻
myAudioPlayer?.play()
//停止播放音頻
myAudioPlayer?.stop()
//音頻播放器的當前時間,以秒為單位。可以使用這個屬性來設置播放位置或查詢當前的播放位置。
myAudioPlayer?.currentTime = 0
myAudioPlayer?.play()
}
}

}

*要留意把音頻導入專案中時,不要加到 Assets 裡面、而是直接拉到跟 Assets/ Main 同一個列就好! 我在設置時誤把音頻夾到 Assets 裡,導致一直出現 錯誤”Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value”🥲

Steps

  1. Segmented Control
//segmented control IBOutlet 連結到 View Controller (建議放在 viewDidLoad 前)    
@IBOutlet weak var instrumentSegmentedControl: UISegmentedControl!

@IBOutlet weak var instrumentImageView: UIImageView!

//segmented control IBAction 連結到 View Controller(建議放在 viewDidLoad 後)
@IBAction func instrumentPageChange(_ sender: Any) {

// 設置 segmented control 可切換的頁面常數
let instrumentPage = instrumentSegmentedControl.selectedSegmentIndex

// 設置在某頁數時,顯示的項目
if instrumentPage == 0 {
instrumentImageView.image = UIImage(named: "muyu")
knockTimes.text = ("敲擊次數:\(muyuCount)")
} else if instrumentPage == 1 { instrumentImageView.image = UIImage(named: "bell")
knockTimes.text = ("敲擊次數:\(bellCount)")
}
}

*如果只把畫面設置在 segmented control 下,要是沒有點擊切換過 segmented control 頁面,則 Page 0 的內容很可能無法正確顯示。故也要將 page 0 的內容放到 viewDidLoad 內,使其可以在初始畫面載入後直接顯示並運作。

override func viewDidLoad() {
super.viewDidLoad()

//最初始的畫面設定
instrumentImageView.image = UIImage(named: "muyu")
knockTimes.text = ("敲擊次數:\(muyuCount)")
audioPlayerMuyu?.play()
audioPlayerMuyu?.stop()
audioPlayerMuyu?.currentTime = 0
audioPlayerMuyu?.play()

}

2. 設置按鈕


//設置點擊按鈕計數
var muyuCount = 0
var bellCount = 0

//敲擊次數Label、圖片上的隱形按鈕、結束按鈕先 IBOutlet 連結到 View Controller (建議將連結到 viewDidLoad 前)
@IBOutlet weak var instrumentClick: UIButton!
@IBOutlet weak var knockTimes: UILabel!
@IBOutlet weak var resetButton: UIButton!

...

//圖片上的隱形按鈕 IBAction 連結到 View Controller(建議放在 viewDidLoad 後)
@IBAction func knockOnInstrument(_ sender: Any) {
if instrumentImageView.image == UIImage(named: "muyu") {
muyuCount += 1
knockTimes.text = ("敲擊次數:\(muyuCount)")
} else if instrumentImageView.image == UIImage(named: "bell") {
bellCount += 1
print(bellCount)
knockTimes.text = ("敲擊次數:\(bellCount)")
}
}


//結束按鈕 IBAction 連結到 View Controller(建議放在 viewDidLoad 後)
@IBAction func reset(_ sender: Any) {
if instrumentImageView.image == UIImage(named: "muyu") {
muyuCount = 0
knockTimes.text = ("敲擊次數:\(muyuCount)")

} else if instrumentImageView.image == UIImage(named: "bell") {
bellCount = 0
print(bellCount)
knockTimes.text = ("敲擊次數:\(bellCount)")
}
}

3. 設置音效

//先導入音頻和視頻的處理框架
import AVFoundation

//設置接下來會使用到的兩種音效播放器
var audioPlayerMuyu: AVAudioPlayer?
var audioPlayerBell: AVAudioPlayer?

//在 viewDidLoad 中進行播放設置
override func viewDidLoad() {
super.viewDidLoad()

let muyuUrl = Bundle.main.url(forResource: "muyuSound", withExtension: "mp3")!
do {
audioPlayerMuyu = try AVAudioPlayer(contentsOf: muyuUrl)
} catch {
print("播放音效檔案出現錯誤:\(error)")
}

let bellUrl = Bundle.main.url(forResource: "bellSound", withExtension: "mp3")!
do {
audioPlayerBell = try AVAudioPlayer(contentsOf: bellUrl)
audioPlayerBell?.volume = 0.8
} catch {
print("播放音效檔案出現錯誤:\(error)")
}
}

//在圖片點擊案牛中加入播放 method
@IBAction func knockOnInstrument(_ sender: Any) {
if instrumentImageView.image == UIImage(named: "muyu") {
muyuCount += 1
knockTimes.text = ("敲擊次數:\(muyuCount)")
audioPlayerMuyu?.play()
audioPlayerMuyu?.stop()
audioPlayerMuyu?.currentTime = 0
audioPlayerMuyu?.play()
} else if instrumentImageView.image == UIImage(named: "bell") {
bellCount += 1
print(bellCount)
knockTimes.text = ("敲擊次數:\(bellCount)")
audioPlayerBell?.play()
audioPlayerBell?.stop()
audioPlayerBell?.currentTime = 0
audioPlayerBell?.play()
}
}

4. 設置 Launch Screen

直接在 LaunchScreen 裡加入圖片即可。只是很想把這句話放進 App 🤣

操作影片

完整程式碼:

//
// ViewController.swift
// Clicker & Segmented Control
//
// Created by 陳佩琪 on 2023/4/26.
//

import UIKit
import AVFoundation

class ViewController: UIViewController {

var audioPlayerMuyu: AVAudioPlayer?
var audioPlayerBell: AVAudioPlayer?

var muyuCount = 0
var bellCount = 0

// 木魚 & 鐘圖片
@IBOutlet weak var instrumentImageView: UIImageView!

// segmented Control
@IBOutlet weak var instrumentSegmentedControl: UISegmentedControl!

// 圖片上的隱形按鈕
@IBOutlet weak var instrumentClick: UIButton!

// 敲擊次數顯示
@IBOutlet weak var knockTimes: UILabel!

// 結束按鈕
@IBOutlet weak var resetButton: UIButton!


override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.

let muyuUrl = Bundle.main.url(forResource: "muyuSound", withExtension: "mp3")!
do {
audioPlayerMuyu = try AVAudioPlayer(contentsOf: muyuUrl)
} catch {
print("播放音效檔案出現錯誤:\(error)")
}

let bellUrl = Bundle.main.url(forResource: "bellSound", withExtension: "mp3")!
do {
audioPlayerBell = try AVAudioPlayer(contentsOf: bellUrl)
audioPlayerBell?.volume = 0.8
} catch {
print("播放音效檔案出現錯誤:\(error)")
}

//最初始的畫面設定
instrumentImageView.image = UIImage(named: "muyu")
knockTimes.text = ("敲擊次數:\(muyuCount)")
audioPlayerMuyu?.play()
audioPlayerMuyu?.stop()
audioPlayerMuyu?.currentTime = 0
audioPlayerMuyu?.play()


}

// segmented control 頁面切換
@IBAction func instrumentPageChange(_ sender: Any) {
let instrumentPage = instrumentSegmentedControl.selectedSegmentIndex
if instrumentPage == 0 {
instrumentImageView.image = UIImage(named: "muyu")
knockTimes.text = ("敲擊次數:\(muyuCount)")
} else if instrumentPage == 1 { instrumentImageView.image = UIImage(named: "bell")
knockTimes.text = ("敲擊次數:\(bellCount)")
}
}

// 點擊圖片按鈕
@IBAction func knockOnInstrument(_ sender: Any) {
if instrumentImageView.image == UIImage(named: "muyu") {
muyuCount += 1
knockTimes.text = ("敲擊次數:\(muyuCount)")
audioPlayerMuyu?.play()
audioPlayerMuyu?.stop()
audioPlayerMuyu?.currentTime = 0
audioPlayerMuyu?.play()
} else if instrumentImageView.image == UIImage(named: "bell") {
bellCount += 1
print(bellCount)
knockTimes.text = ("敲擊次數:\(bellCount)")
audioPlayerBell?.play()
audioPlayerBell?.stop()
audioPlayerBell?.currentTime = 0
audioPlayerBell?.play()
}
}

// 結束按鈕
@IBAction func reset(_ sender: Any) {
if instrumentImageView.image == UIImage(named: "muyu") {
muyuCount = 0
knockTimes.text = ("敲擊次數:\(muyuCount)")

} else if instrumentImageView.image == UIImage(named: "bell") {
bellCount = 0
print(bellCount)
knockTimes.text = ("敲擊次數:\(bellCount)")
}
}
}

--

--