stack view + scroll view 製作分頁 tutorial (懶得寫程式版)

在開發 iOS App 時,我們時常以表格實現上下捲動的畫面。那如果是水平左右捲動的畫面呢 ? 左右捲動一般而言比較麻煩,往往要搭配程式,使用 scroll view ,collection view 或 page view controller 實現。

但如果只想做個 App 第一次打開時看到的教學介紹頁面,一個四五頁捲動的 tutorial 頁面,其實是可以在 storyboard 利用 stack view,scroll view 和 auto layout,完全不寫程式實現的 !

接下來,我們就以此生必讀的經典 Peter Pan 為例,製作一個瀏覽 Peter Pan 書籍封面的頁面吧。

將 5 張 peter pan 的圖片加到 Assets.xcassets

加入佔滿螢幕的 scroll view

選取 scroll view & view(可利用 cmd 鍵多選),然後設定上下左右對齊。(ps: 如果不想 scroll view 被美麗瀏海檔到,也可以設定 scroll view & Safe Area 上下左右對齊。)

此時將出現紅色錯誤,因為 scroll view 覺得頭很大,他不知道捲動的範圍。

待會我們將設定 auto layout 條件,由 content layout guide 決定捲動的範圍,對 scroll view 如何決定捲動範圍不熟悉的朋友可先參考以下連結。

將顯示 Peter Pan 圖片的 1 個 image view 加到 scroll view

先加 1 個 image view 就好,其它的 image view 等下再加。

為了讓圖片佔滿畫面,Content Mode 記得設為 Aspect Fill。(ps: 若想要圖片維持比例完整顯示,則可設為 Aspect Fit )

將 image view 加到 stack view

設定 stack view

我們將在 stack view 放 5 個 image view,想要每張圖一樣大,然後搭配 scroll view 左右滑動,因此請將 Axis 設為 Horizontal,Alignment 設為 Fill,Distribution 設為 Fill Equally, Spacing 設為 0。

將 stack view 的上下左右間距設為 0

讓 scroll view 被 stack view 填滿,由 stack view 的大小決定 scroll view content layout guide(捲動範圍)的大小。

如下圖所示,請選取 stack view & content layout guide(可按住 cmd 鍵多選),然後設定上下左右對齊的條件讓 stack view 和 content layout guide 的間距為 0。

此時 stack view 的大小將由裡面的 image view 決定,而每個 image view 的大小則由當初圖片的大小決定。

讓圖片的寬高等於螢幕的寬高

設定 image view 和 Frame Layout Guide Equal Widths & Equal Heights。

為什麼要讓 image view 和 Frame Layout Guide一樣大呢? 因為它代表 scroll view 本身 frame 的框框,當我們設定圖片跟它一樣大時,由於 scroll view 的大小等於螢幕的大小,圖片的大小也將等於螢幕的大小。

從下圖我們可以更清楚看出 Frame Layout Guide & Content Layout Guide 的差異,Frame Layout Guide是比較小的黑色框框,Content Layout Guide的黃色框框則包含了整個捲動的內容。

Cool,現在第一頁的 peter pan 圖片完美地填滿螢幕,不過因為只有一張圖,所以還不能滑動。

加入其它圖片

利用 cmd + c & cmd + v 複製貼上其它四個 image view,然後將圖片依序改成 peter2,peter3,peter4 & peter5。

stack view 塞了 5 個 image view 後,寬度成為原本一個 image view 寬度的 5 倍。

點選 Content Layout Guide 也可查看 scroll view 捲動範圍的大小。

完整的 Auto Layout 條件截圖畫面如下。

完成以上步驟後,現在我們的 scroll view 已經可以捲動了,甚至不用跑模擬器,在 Interface Builder 也可以捲動。而且當我們點選 stack view 裡的圖片時,它還會自動捲動顯示對應的圖片呢。

讓 scroll view 分頁捲動。

勾選 scroll view 的 Paging Enabled,讓 scroll view 捲動時以它本身的大小為單位分頁,讓 scroll view 捲動停下時都能剛好看到完整的圖片。

之後如果想再增加圖片也很簡單,只要將 image view 加到 stack view 裡,即可多一頁分頁。

範例連結

不過如果要呈現的內容很多的話,建議改從程式搭配 collection view 或 scroll view 的寫法比較省記憶體。剛剛完全從 storyboard 製作的方法一開始就會生成每個頁面的內容,而不是捲動到相關頁面才生成,因此只適合頁面數量固定,而且不會太多頁的情境,比方 App 第一次打開時看到的教學介紹頁面,或是選擇分類時,列出一排十個 icon 選項的情境。

以程式實現水平滑動的分頁效果

剛剛以 storyboard 實現的畫面也可以用程式實現,程式如下。

import UIKit

class ViewController: UIViewController {

private let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.isPagingEnabled = true
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.clipsToBounds = true
return scrollView
}()

private let stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .fillEqually
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()

private let imageNames = ["peter1", "peter2", "peter3", "peter4", "peter5"]

override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}

private func setupUI() {
view.backgroundColor = .systemBackground

view.addSubview(scrollView)
scrollView.addSubview(stackView)

NSLayoutConstraint.activate([
// Constraint 1: scrollView top to view top
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
// Constraint 2: scrollView leading to view leading
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
// Constraint 3: scrollView trailing to view trailing
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
// Constraint 4: scrollView bottom to view bottom
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),

// Constraint 5: stackView top to scrollView contentLayoutGuide top
stackView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
// Constraint 6: stackView leading to scrollView contentLayoutGuide leading
stackView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
// Constraint 7: stackView trailing to scrollView contentLayoutGuide trailing
stackView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
// Constraint 8: stackView bottom to scrollView contentLayoutGuide bottom
stackView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
])

for (index, imageName) in imageNames.enumerated() {
let imageView = UIImageView(image: UIImage(named: imageName))
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
stackView.addArrangedSubview(imageView)

if index == 0 {
// Constraint 9: imageView width equal to scrollView frameLayoutGuide width
imageView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor).isActive = true
// Constraint 10: first imageView height equal to scrollView frameLayoutGuide height
imageView.heightAnchor.constraint(equalTo: scrollView.frameLayoutGuide.heightAnchor).isActive = true
}
}
}

}

進階題

模仿 NETFLIX App,滑動的內容包含圖片跟文字。

提示

stack view 裡裝的成員是 view,代表每個分頁。view 裡裝 image view & label。

One more thing,從程式動態加 view 到 stack view & 隨著換頁更新的 page control 小圓點

作品集

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com