利用 scroll view & container view 水平滑動切換頁面
我們時常在 iOS App 上見到類似 Instagram 的畫面設計,點選 button 或水平滑動可切換頁面。
有很多方法可實現這樣的畫面,比方透過以下步驟實現。
- 以 scroll view 實現水平捲動。
- 在 scroll view 加入水平排列的 stack view,stack view 將裝著每一個分頁。
- 在 stack view 加入多個 container view,每個 container view 連接的 view controller 負責不同的分頁。
接下來我們將以實現 iTunes 上精彩的電影和音樂為例,示範如何透過 scroll view & container view 水平滑動切換頁面。
加入切換分頁的 segment control
分別顯示電影和音樂頁面。
加入 scroll view
在 segmented control 下加入 scroll view。
勾選 scroll view 的 Paging Enabled。
Auto Layout 條件的設定如下。
在 scroll view 裡加入 horizontal stack view
設定 stack view 上下左右對齊 content layout guide, stack view 的 height 等於 frame layout guide 的 height。
在 stack view 裡加入兩個 container view
設定第一個 container view 的 width 等於 frame layout guide 的 width。
將 container view 預設連接的 view controller 刪除,等下我們會另外將 container view 各自連到顯示電影和音樂列表的 view controller。
結果
設定 stack view 的 Alignment & Distribution
Alignment 設為 Fill,Distribution 設為 Fill Equally。
加入呈現電影和音樂頁面的 table view controller,將 container view 連接到 table view controller
從 container view 拉線到 table view controller 時記得要選 Embed。
將 2 個 table view controller 的 cell reuse ID 都設為 cell。
將裝著 container view 的 view controller 類別設為自訂的 HomeViewController(繼承 UIViewController)
將 2 個 table view controller 的類別分別設為自訂的 MovieTableViewController & MusicTableViewController (繼承 UITableViewController)
定義抓取 iTunes 資料的網路程式,在 MovieTableViewController & MusicTableViewController 呼叫抓資料的 function
參考範例的 ItunesService.swift,ItunesFeed.swift,MovieTableViewController & MusicTableViewController。
結果
現在我們的 App 已經可以水平滑動切換電影和音樂的分頁,只是還存在以下 2 個令人不安的缺點,讓我們一個個解決吧。
- 水平滑動切換分頁時不會更新 segmented control。
- 點選 segmented control 無法切換分頁。
水平滑動切換分頁時更新 segmented control
- 設定 scroll view 的 delegate 為 HomeViewController。
- 連接 segmented control 的 outlet (HomeViewController.swift)。
@IBOutlet weak var segmentedControl: UISegmentedControl!
- 滑動停止時計算目前所在的分頁位置,更新 segmented control 的 selectedSegmentIndex。(HomeViewController.swift)
extension HomeViewController: UIScrollViewDelegate {
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let index = Int(scrollView.contentOffset.x / scrollView.bounds.width)
segmentedControl.selectedSegmentIndex = index
}
}
點選 segmented control 切換分頁
- 連接 segmented control 的 action (HomeViewController.swift)。
@IBAction func changePage(_ sender: UISegmentedControl) {
}
- 連接 scroll view 的 outlet (HomeViewController.swift)。
@IBOutlet weak var scrollView: UIScrollView!
- 點選 segmented control 時捲動 scroll view (HomeViewController.swift)。
@IBAction func changePage(_ sender: UISegmentedControl) {
let x = CGFloat(sender.selectedSegmentIndex) * scrollView.bounds.width
let offset = CGPoint(x: x, y: 0)
scrollView.setContentOffset(offset, animated: true)
}
結果
現在我們的 segmented control & scroll view 終於能連動了 ~。
One more thing,點選 cell 時顯示照片
當我們在 container view 的 controller 操作時,能不能通知裝著 container view 的 controller 呢 ? 比方在 MovieTableViewController & MusicTableViewController 的 cell 點選時,在 HomeViewController 顯示對應的照片。
當然可以,實現此功能的步驟如下:
- 在 HomeViewController 加入 image view,outlet 取名為 imageView。
@IBOutlet weak var imageView: UIImageView!
- 加入抓圖的套件 Kingfisher
- 點選 cell 時更新圖片。(MovieTableViewController & MusicTableViewController)
當我們在 HomeViewController 上以 container view 容納 MovieTableViewController & MusicTableViewController 時,將產生剪不斷的親子關係。
- MovieTableViewController & MusicTableViewController 的 parent 是 HomeViewController。
- HomeViewController 的 children 是 MovieTableViewController & MusicTableViewController。
因此在 MovieTableViewController & MusicTableViewController 的程式裡我們可透過 parent 讀取 HomeViewController。(ps: 在 HomeViewController 也可透過 children 讀取 MovieTableViewController & MusicTableViewController)
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let controller = parent as? HomeViewController
let result = results[indexPath.row]
controller?.imageView.kf.setImage(with: result.artworkUrl100)
}
結果