【iOS】#3 利用 page control、segmented control、button & gesture 更換內容

這次的主題是陪伴我度過許多時光的歌手,不論是學生時期熬夜做作品;失戀時心情低落;人生第一次看演唱會,都是她的音樂陪伴著我。用我最愛的歌手 — 田馥甄,新專輯裡的三首歌,作為運用 page control、segmented control、button & gesture 更換內容的練習。

先來看看成品:

畫面 Layout

以下畫面 Auto Layout 皆使用 SnapKit

1. 初始化所需元件

let musicVideoImage = UIImageView()
let pageControl = UIPageControl()
let segmentedControl = UISegmentedControl()
let name = UILabel()
let names = ["或是一首歌", "無人知曉", "皆可"]
let lastButton = UIButton()
let nextButton = UIButton()
let lyrics = UITextView()
let songLyrics = [
"""
我把我的靈魂送給你
或是一首歌...
""",
"""
我像是 小數點 第幾位
存在但 能自動 被省略...
""",
"""
可以只睡覺嗎
不做成 什麼夢...
"""
]
let swipeGestureLeft = UISwipeGestureRecognizer()
let swipeGestureRight = UISwipeGestureRecognizer()
var index = 0

2. MV 圖片 — UIImageView

  • 將圖片存進 Assets,圖片名稱命名為歌曲名稱,這樣後續更換圖片及歌曲名稱時,只要使用 names Array 裡的 item 來控制即可
  • 這邊我想將圖片的寬設計為撐滿畫面,圖片寬的實際數值會隨著裝置大小而不同,為了維持圖片正常比例,利用圖片本身的「長寬比」去設定圖片的高(高設置為寬的倍數)
musicVideoImage.backgroundColor = .darkGray
musicVideoImage.image = UIImage(named: names[index])
view.addSubview(musicVideoImage)
musicVideoImage.snp.makeConstraints { make in
make.width.equalToSuperview()
//利用圖片長寬比,將圖片的高設為寬的倍數
make.height.equalTo(musicVideoImage.snp.width).multipliedBy(0.5625)
make.centerX.equalToSuperview()
make.top.equalToSuperview().inset(60)
}
iPhone 15 Pro Max | iPhone 15 | iPhone SE

3. UIPageControl

  • 使用 .numberOfPages 設定頁數(幾個小圓點)
  • 使用 .currentPage 顯示目前在第幾頁
pageControl.numberOfPages = 3
pageControl.currentPage = index
view.addSubview(pageControl)
pageControl.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(musicVideoImage.snp.bottom).offset(10)
}

4. UISegmentedControl

  • 使用 for 迴圈設置 segmentedControl 的 title
  • names.indices 的值為 0 ..< 3
  • 使用 .selectedSegmentIndex 顯示目前點選在第幾個 segment
for index in names.indices {
segmentedControl.insertSegment(withTitle: names[index], at: index, animated: true)
}
segmentedControl.selectedSegmentIndex = index
segmentedControl.backgroundColor = .systemPink
view.addSubview(segmentedControl)
segmentedControl.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(pageControl.snp.bottom).offset(30)
}

5. 歌曲 Title — UILabel & UIButton

整理過的 config 相關 function,最後會在 viewDidLoad 中呼叫

  • lastButton / nextButton 使用 .setImage 設置為相對應的系統圖片
func configName() {
name.text = names[index]
name.font = .systemFont(ofSize: 28)
name.textColor = .systemBackground
view.addSubview(name)
name.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(segmentedControl.snp.bottom).offset(40)
}
}

func configLastButton() {
lastButton.setImage(UIImage(systemName: "arrowtriangle.left.fill"), for: .normal)
lastButton.tintColor = .systemPink
view.addSubview(lastButton)
lastButton.snp.makeConstraints { make in
make.width.equalTo(40)
make.height.equalTo(40)
make.centerY.equalTo(name.snp.centerY)
make.leading.equalToSuperview().inset(50)
}
}

func configNextButton() {
nextButton.setImage(UIImage(systemName: "arrowtriangle.right.fill"), for: .normal)
nextButton.tintColor = .systemPink
view.addSubview(nextButton)
nextButton.snp.makeConstraints { make in
make.width.equalTo(40)
make.height.equalTo(40)
make.centerY.equalTo(name.snp.centerY)
make.trailing.equalToSuperview().inset(50)
}
}

6. 歌詞 — UITextView

  • 使用 .textAlignment 設定文字的對齊模式
lyrics.text = songLyrics[index]
lyrics.textAlignment = NSTextAlignment.center
lyrics.font = .systemFont(ofSize: 20)
lyrics.backgroundColor = .clear
lyrics.textColor = .systemBackground
lyrics.isEditable = false
view.addSubview(lyrics)
lyrics.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(name.snp.bottom).offset(30)
make.width.equalToSuperview().multipliedBy(0.8)
make.bottom.equalToSuperview().inset(60)
}

7. 加入左右滑動手勢操作 — UISwipeGestureRecognizer

  • 使用 .direction 設定滑動方向
view.addGestureRecognizer(swipeGestureLeft)
swipeGestureLeft.direction = .left
view.addGestureRecognizer(swipeGestureRight)
swipeGestureRight.direction = .right

功能實作

1. UIKit 元件連結至對應的function

pageControl.addTarget(self, action: #selector(selectPage), for: .valueChanged)
segmentedControl.addTarget(self, action: #selector(selectSegment), for: .valueChanged)
lastButton.addTarget(self, action: #selector(lastButtonPressed), for: .touchUpInside)
nextButton.addTarget(self, action: #selector(nextButtonPressed), for: .touchUpInside)
swipeGestureLeft.addTarget(self, action: #selector(handleSwipeGesture))
swipeGestureRight.addTarget(self, action: #selector(handleSwipeGesture))

2. 更新畫面

  • 做一個 updateUI function,在每次點擊更換內容的元件後執行
  • 使用變數 index 控制顯示內容
  • 當有元件變更 index 時,呼叫 updateUI function 重新分配一次目前的 index 給各個元件去做顯示

ex : 點選 pageControl(小圓點)時,MV 圖片、segmentedControl、歌曲名稱 label 及歌詞都要一起更新

func updateUI() {
musicVideoImage.image = UIImage(named: names[index])
pageControl.currentPage = index
segmentedControl.selectedSegmentIndex = index
name.text = names[index]
lyrics.text = songLyrics[index]
}

3. 點選 pageControl

  • 將 currentPage 的值存進 index
  • 更新畫面
@objc func selectPage(_ sender: UIPageControl) {
index = pageControl.currentPage
updateUI()
}

4. 點選 segmentedControl

  • 將 indexselectedSegmentIndex 的值存進 index
  • 更新畫面
@objc func selectSegment(_ sender: UISegmentedControl) {
index = segmentedControl.selectedSegmentIndex
updateUI()
}

5. 點擊向右的 button

  • 在最後一頁點選向右 button 將顯示第一頁的內容
  • 每次點擊向右 button,index 加一,為了不要讓 index 的值無限增加,利用 % 運算子取餘數,每次的結果都會是 0~2 之間的數字
  • 更新畫面
@objc func nextButtonPressed(_ sender: UIButton) {
index = (index + 1) % names.count
updateUI()
}

6. 點擊向左的 button

  • 在第一頁點選向左 button 將顯示最後一頁的內容
  • 每次點擊向左 button,index 減一,為了不要讓 index 的值無限減少,利用 % 運算子取餘數,並且加上循環一次的個數(names.count),避免 index 的值變成負的,每次的結果都會是 0~2 之間的數字
  • 更新畫面
@objc func lastButtonPressed(_ sender: UIButton) {
index = (index - 1 + names.count) % names.count
updateUI()
}

7. swipe gesture (左滑右滑)

  • 做一個判斷手勢的 handleSwipeGesture function,傳入UISwipeGestureRecognizer 當參數
  • 當滑動方向為左時,依照點擊下一頁的方式計算 index 後,更新畫面
  • 當滑動方向為右時,依照點擊上一頁的方式計算 index 後,更新畫面
@objc func handleSwipeGesture(_ gesture: UISwipeGestureRecognizer) {
if gesture.direction == .left {
index = (index + 1) % names.count
updateUI()
} else if gesture.direction == .right {
index = (index - 1 + names.count) % names.count
updateUI()
}
}

完整 APP 操作展示:

下一次更新目標:新增播放音樂的功能,做成一個簡易音樂播放器!

這樣就能夠聽到馥甄的歌聲啦❤️️

--

--