---
title: william-weng/wwinterfaceorientationcoordinator
framework: Swift Package Catalog
role: article
path: packages/william-weng/wwinterfaceorientationcoordinator
---

# william-weng/wwinterfaceorientationcoordinator

[English](./README.en.md) | [正體中文](./README.md)

## ✨ [主要功能](https://medium.com/@iamCoder/device-orientation-modes-in-ios-c86fb3497ede)

- 觀察 `UITraitHorizontalSizeClass` 變化，適合處理 Duo 外螢幕與內螢幕間的 compact／regular 切換。 - 提供 `UIInterfaceOrientation?`，方便在轉場動畫中判斷目前方向。 - 使用 `weak` 持有 `UIViewController`，避免 coordinator 與 VC 互相強持有。 - 將初始狀態、trait 變化與轉場動畫的資訊統一封裝成 `Information`。 - 適合搭配多個 XIB、`UISplitViewController`、sidebar／detail 或 compact／regular 版面。

## 📦 [安裝方式](https://blakecrosley.com/zh-Hant/blog/designing-for-iphone-duo)

### 使用 Swift Package Manager

在 Xcode 中選擇：

`File` → `Add Package Dependencies...`

接著輸入套件 repository URL，選擇要使用的版本，並將套件加入 App target。

如果這是本地套件，也可以在 Xcode 中使用：

`File` → `Add Package Dependencies...` → `Add Local...`

安裝完成後，將該套件匯入目標後，就可以使用了：

```swift import WWInterfaceOrientationCoordinator ```

## 🧱 [API 總覽](https://developer.apple.com/design/human-interface-guidelines/designing-for-iphone-duo)

### WWInterfaceOrientationCoordinator

| API | 說明 | | --- | --- | | `orientation` | 目前所觀察 view controller 的 interface orientation。若 view controller 尚未加入 window，或無法取得 window scene，則回傳 `nil`。 | | `horizontalClass` | 目前 view controller trait collection 的 horizontal size class。若 view controller 已被釋放，則回傳 `nil`。 | | `register(forHorizontalSizeClassChanges:onChange:)` | 註冊 horizontal size class 變更通知。註冊後會立即以目前狀態呼叫 `initial`，之後每當 `UITraitHorizontalSizeClass` 改變時呼叫 `onChange`。 | | `willTransition(with:animation:completion:)` | 在 view controller 轉場期間，依目前 interface orientation 執行自訂動畫。若無法取得目前方向，則不會呼叫動畫 callback。 |

### WWInterfaceOrientationCoordinator (iOS 27.1)

| API | 說明 | | --- | --- | | `divisionRegions` | 獲取畫面上所有【物理存在】的摺疊線/分割區域。 | | `occlusionRegions` | 獲取畫面上所有【當前正在遮擋像素】的硬體/系統區域。 | | `activeDivisionRegion` | 獲取當前【真正被折起、看得見】的第一個主要摺疊線區域。 | | `crossAxisDimensionRange(with:ratio:minimum:maximum:)` | 依跨軸比例建立 `UISplitArrangement.DimensionRange`。 |

### WWLayoutEnvironmentInformation

| API | 說明 | | --- | --- | | `previousTrait` | 變更前的 Size Class；沒有前值時傳 `nil`。 | | `currentTrait` | 目前的 Size Class；無法取得時傳 `nil`。 | | `orientation` | 目前的介面方向；無法取得時傳 `nil`。 | | `unknown` | 空白／未知的方向資訊。 | | `isLandscape` | 是否為橫向 UI。 | | `isPortrait` | 是否為直向 UI。 | | `isRegularWidth` | 目前的 horizontal size class 是否為 regular。 | | `isCompactWidth` | 目前的 horizontal size class 是否為 compact。 | | `isRegularHeight` | 目前的 vertical size class 是否為 regular。 | | `isCompactHeight` | 目前的 vertical size class 是否為 compact。 | | `didChangeToRegularWidth` | horizontal size class 是否從 compact 變成 regular。 | | `didChangeToCompactWidth` | horizontal size class 是否從 regular 變成 compact。 | | `init(previousHorizontalClass:horizontalClass:orientation:)` | 目前所觀察 view controller 的 interface orientation。若 view controller 尚未加入 window，或無法取得 window scene，則回傳 `nil`。 |

### WWTraitSizeClass

| API | 說明 | | --- | --- | | `horizontalClass` | horizontal size class；無法取得時傳 `nil`。 | | `verticalSizeClass` | vertical size class；無法取得時傳 `nil`。 | | `unknown` | 空白／未知的 Size Class 狀態。 |

## 🧪 [使用範例](https://developer.apple.com/videos/play/wwdc2017/812/)

```swift import UIKit import WWInterfaceOrientationCoordinator

final class ViewController: UIViewController {          @IBOutlet weak var imageView: UIImageView!          private lazy var orientationCoordinator = WWInterfaceOrientationCoordinator(viewController: self)          private let foldBox = UIView()          override func viewDidLoad() {         super.viewDidLoad()         initFolderView()                  orientationCoordinator.register { [weak self] info in             guard let self else { return }             updateLayout(info: info)         } onChange: { [weak self] info in             guard let self else { return }             updateLayout(info: info)         }     }          override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {         super.viewWillTransition(to: size, with: coordinator)                  orientationCoordinator.willTransition(with: coordinator) { [weak self] info in             guard let self else { return }             updateLayout(info: info)         }     }          override func viewDidLayoutSubviews() {         super.viewDidLayoutSubviews()                  if #available(iOS 27.1, *) {             if let region = orientationCoordinator.activeDivisionRegion {                 foldBox.frame = region.frame             } else {                 foldBox.frame = .zero             }         }     } }

private extension ViewController {          func initFolderView() {                  foldBox.layer.borderColor = UIColor.systemRed.cgColor         foldBox.layer.borderWidth = 2         foldBox.backgroundColor = UIColor.systemRed.withAlphaComponent(0.3)         foldBox.isUserInteractionEnabled = false                  view.addSubview(foldBox)     }

func updateLayout(info: WWLayoutEnvironmentInformation) {                  let orientation = info.orientation ?? .portrait         let horizontalClass = info.currentTrait?.horizontalClass ?? .regular                  switch orientation {         case .landscapeLeft, .landscapeRight: imageView.image = (horizontalClass == .regular) ? .innerLandscape : .outterLandscape         case .portrait, .portraitUpsideDown: imageView.image = (horizontalClass == .regular) ?.innerPortrait:.outterPortrait         case .unknown: imageView.image = .outterPortrait         @unknown default: imageView.image = .outterPortrait         }     } } ```

## Design Notes

- **不要只用 orientation 決定 layout。** Duo 的同一個 orientation 可能對應不同螢幕或折疊姿勢；layout 應優先依 horizontal size class 與實際 view bounds 調整。 - **orientation 適合用於動畫與方向相關行為。** 例如旋轉時改變動畫方向、調整過場效果，或處理媒體播放方向。 - **Coordinator 不擁有 VC。** 它以 `weak` reference 觀察 view controller，因此不會造成 `UIViewController` 與 coordinator 的 retain cycle。 - **callback 請避免強持有 VC。** 若你在 `initial`、`onChange`、`animation` 或 `completion` closure 中 capture `self`，建議視情況使用 `[weak self]`。

## Package Metadata

Repository: william-weng/wwinterfaceorientationcoordinator

Default branch: main

README: README.md
