使用 #Preview macro 定義預覽畫面

透過 Xcode 的 preview 功能,我們可以方便地從 preview 預覽和操作畫面,不用啟動模擬器,而且修改程式後 preview 也會立即更新,馬上就能測試畫面和功能是否正確。

從 Xcode 15 開始,幫畫面加上 preview 變得更方便了。它有以下三大改進。

  • preview 的程式變得更精簡了。

由於 Swift 5.9 macro 語法的發明,現在只要用 #Preview { } 即可定義 preview,比方以下是 ContentView 的 preview。

#Preview {
ContentView()
}

舊版的 preview 寫法如下,寫法冗長許多。

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
  • SwiftUI 的 view 和 UIKit 的 view 和 view controller 都能快速加上 preview。

從前 UIKit 的 view 和 view controller 也能加上 preview,但是需要撰寫麻煩的 UIViewRepresentable 和 UIViewControllerRepresentable。現在有了 #Preview macro,寫法變得簡單許多。

  • 方便預覽 widget 不同時間點的樣子。

以下我們將重點放在第二點,示範如何利用 #Preview macro 幫以下幾種畫面加上 preview。

  • SwiftUI 的 view。
  • 從 storyboard 設計的 view controller 畫面。
  • 從程式製作的 view controller 畫面。
  • 從 xib 製作的 view controller 畫面。
  • 從程式製作的 UIKit view。
  • 從 xib 製作的 UIKit view。

SwiftUI 的 view

struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
}
}

在 #Preview { } 裡回傳 SwiftUI view。

#Preview {
ContentView()
}

從 storyboard 設計的 view controller 畫面

以 Apple Develop in Swift Explorations 電子書的 RPS 為例。

在 #Preview { } 裡回傳從 storyboard 生成的 view controller。

  • 寫法 1: 生成的 controller 是 initial view controller。
#Preview {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
return storyboard.instantiateInitialViewController()!
}
  • 寫法 2: 生成指定 id 的 view controller。
#Preview {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
return storyboard.instantiateViewController(withIdentifier: "ViewController")
}

在 Storyboard ID 欄位設定 controller 的名字,下圖設定 ViewController。

透過以上寫法,現在我們從 preview 就能操作畫面,開心地跟電腦玩剪刀石頭布。

我們也可以一邊修改程式,一邊從 preview 測試功能。以下我們將機器人換成吸血鬼,背景色換成橘色。

從程式製作的 view controller 畫面

以 freeCodeCamp.org 的 Netflix App 為例。

HomeViewController 的畫面完全從程式製作。

加上 preview。

#Preview {
UINavigationController(rootViewController: HomeViewController())
}

preview 順利地呈現 HomeViewController 從網路抓取的資料。

當我們修改字型大小和顏色時,preview 也會立即更新。

func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
guard let header = view as? UITableViewHeaderFooterView else {return}
header.textLabel?.font = .systemFont(ofSize: 25, weight: .semibold)
header.textLabel?.frame = CGRect(x: header.bounds.origin.x + 20, y: header.bounds.origin.y, width: 100, height: header.bounds.height)
header.textLabel?.textColor = .red
header.textLabel?.text = header.textLabel?.text?.capitalizeFirstLetter()
}

preview 的 Trending movies 變大了,字也變成了紅色。

preview 不只能顯示 #Preview { } 裡回傳的畫面,點選電影還會跳到下一個畫面。

從 xib 製作的 view controller 畫面

class DemoViewController: UIViewController {

@IBOutlet var diceImageViews: [UIImageView]!

override func viewDidLoad() {
super.viewDidLoad()

// Do any additional setup after loading the view.
}

@IBAction func play(_ sender: Any) {
for diceImageViwe in diceImageViews {
let number = Int.random(in: 1...6)
diceImageViwe.image = UIImage(systemName: "die.face.\(number).fill")
}
}

}

加上 preview。

#Preview {
DemoViewController()
}

我們可以直接在 preview 玩骰子改變點數。

從程式製作的 UIKit view

以 freeCodeCamp.org 的 Netflix App 為例,以下為 table view cell 的設計畫面。

加上 preview,參數 traits 傳入 fixedLayout 設定 cell 的大小。

#Preview(traits: .fixedLayout(width: 393, height: 140), body: {
let model = TitleViewModel(titleName: "Spider-Man: Across the Spider", posterURL: "/8Vt6mWEReuy4Of61Lnj5Xj704m8.jpg")
let cell = TitleTableViewCell()
cell.configure(with: model)
return cell
})

從 xib 製作的 UIKit view

class DemoView: UIView {

@IBOutlet weak var imageView: UIImageView!
}

加上 preview,參數 traits 傳入 fixedLayout 設定 cell 的大小。

#Preview(traits: .fixedLayout(width: 100, height: 100), body: {
if let demoView = Bundle.main.loadNibNamed("DemoView", owner: nil)?.first as? DemoView {
demoView.imageView.image = UIImage(systemName: "apple")
return demoView
} else {
return UIView()
}
})

相容舊版的 iOS

舊版的 iOS 也可以使用 #Preview。不過當專案的 Minimum Deployments 小於 iOS 17 時,在某些情況會遇到版本問題,相關說明可參考以下連結。

利用 Code Snippet & Predictive Code Completion 快速建立預覽 UIKit controller 的 preview

參考連結

--

--

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

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