iPhone Duo 的豎向欄
iPhone Duo 如何把導覽列、工具列和標籤列移到側邊,哪些容器符合條件,以及如何擺放面板和浮動按鈕。
在 iPhone Duo 上,介面框架會移動。導航項、工具列操作和標籤可以離開螢幕的頂部和底部,共用側邊的一列,把高度還給內容。只要用 iOS 27.1 SDK 建置,就會這樣。你仍然用同樣的位置來聲明這些項,由 SwiftUI 和 UIKit 決定它們去哪裡。
本文講的就是這個決定,以及開發者發現系統幫不上忙的那些情況。整體佈局請從為 iPhone Duo 做設計開始;摺痕本身請看摺痕指南。
要點
- 用 iOS 27.1 SDK 重新建置,並把各種欄放在
NavigationStack、NavigationSplitView、TabView或它們對應的 UIKit 控制器裡。這些會移動,你自己搭的欄不會。 - 側邊欄出現在外螢幕上,以及橫拿的展開手機上。展開並豎拿時,各種欄回到頂部和底部。
- 符號按鈕會自己轉過去。文字和自定義檢視會保持橫向,除非你聲明
.axisBehavior(.verticalPreferred)。 - 關閉側邊欄是按呈現單位來選擇的。做一次決定,對整個頁面生效。
- 面板會自己適配,並且可以放在前側、尾側或居中。浮動按鈕是唯一還需要你手動擺放的部分。
只有系統管的欄才會移動
NavigationStack、NavigationSplitView 或 TabView 上的 toolbar 符合條件。UINavigationController 或 UITabBarController 符合條件。你自己建立並塞進檢視層級的 UIToolbar、UINavigationBar 或 UITabBar 則不符合,它們的項永遠不會被考慮放進側邊欄。Apple 的工程師在 9 月的小組實驗室裡也說了同樣的話:自定義標籤列不會自己跳過去,否則你就得自己重新實現那些尺寸規則。
這一列是共用的。導航、工具列操作和標籤共佔一條,而不是三條。它出現在橫向空間充裕、縱向空間緊張的地方:外螢幕,以及橫向的內螢幕。在豎向的內螢幕上,各種欄會回到熟悉的頂部和底部。使用者每次轉動展開的手機都會看到這種變化。順著它來設計,而不是和它對著幹。Apple 的首發合作夥伴已經這麼做了:下面是 Detail,再往下是 Slack。

Sarun W. 根據 Raise the Bar 講座補充了一個在多欄分欄檢視(NavigationSplitView 這類佈局,不是多工的分割畫面)中很容易忽略的限制:只有當欄所在的容器貼著螢幕邊緣時,欄才會移動。詳情欄會拿到豎向欄,其他欄仍然是橫向的,展開的檢查器也是如此,以免與詳情欄爭搶。如果某個按鈕“不肯”移動,在動用關閉選項之前,先看看它在哪一欄裡。

這一列屬於硬體,而不屬於閱讀方向。在從右到左書寫的語言中,它仍然在裝置的同一側,由內容圍繞它來適配。在列內,從上到下保持 Apple 規定的順序:先是導航(比如“返回”或“關閉”),再是主要操作(比如“完成”),然後是其他所有項,按原來的分組排列。
橫向欄的高度固定,項的寬度靈活。豎向欄正好相反:寬度固定,項的高度靈活,所以它需要的是符號,而不是句子。
告訴系統哪些項可以轉向
標準的符號按鈕可以自己加入側邊欄。純文字項,以及系統無法調整大小的自定義檢視,會保持橫向。分段控制元件或太寬的文字按鈕,即使周圍所有東西都移走了,也會留在橫向欄裡。如果你已經通過 Label 或對應的 UIBarButtonItem 屬性給按鈕同時提供了標題和影像,那你已經完成了大半,系統會挑選合適的形式。
想要不同的結果時,就明確設定。
.toolbar {
ToolbarItem(placement: .bottomBar) {
Compass()
}
.axisBehavior(.verticalPreferred)
ToolbarItem {
SelectOrDoneButton()
}
.axisBehavior(.horizontalOnly)
}
.verticalPreferred 是讓自定義檢視加入的方式。.horizontalOnly 即使項帶有圖示也讓它保持橫向,這正適合那種會在符號和“完成”二字之間切換的控制元件。Khoa Pham 記下了同一場講座裡的第三個技巧:一個混合了圖示和數字的自定義檢視,可以使用 badge(),讓數字變成標記,圖示就能加入側邊欄。
在自定義檢視內部,讀取你最終落在了哪裡。
struct Compass: View {
@Environment(\.toolbarVerticalEdge) private var edge
var body: some View {
let layout = edge == nil ? AnyLayout(HStackLayout()) : AnyLayout(VStackLayout())
layout {
Image(systemName: "location.north")
Text("N")
}
}
}
toolbarVerticalEdge 的值為 leading、trailing 或 nil。nil 表示此刻各種欄是橫向的,所以要繪製橫向版本。UIKit 用 traitCollection.verticalBarEdge 提供同樣的資訊。如果你設定了 .horizontalOnly,即使環境裡有邊緣值,也要保持橫向繪製,因為這個項並不在側邊欄裡。
有幾個位置值得記住名字。cancellationAction 會停留在側邊欄頂部。在 UIKit 裡,這對應一個前側項組,並把 leftItemsSupplementBackButton 設為 false,這樣你的關閉按鈕會取代返回按鈕的位置,而不是挨著它。topBarPinnedTrailing 以及 UIKit 中的固定尾側組,能讓“分享”這類主要操作不被擠進溢位選單。visibilityPriority 決定側邊欄空間不夠時誰能留下。預設情況下,項會從下往上溢位。
當側邊欄空間不夠時
標籤和工具列項共用這一條,而空間常常不夠。SwiftUI 可以把工具列項擠進溢位選單,或者把標籤收成一個切換器。toolbarVerticalCompressionBehavior 決定先發生哪一種。
在 iOS 上,預設偏向標籤列:工具列項溢位,標籤保持可見。你可以用 .prefersTabBar 明確要求這樣,或者在操作才是核心、標籤可以收起時,用 .prefersToolbarItems 反過來。Natalia Panferova 關於工具列的文章在同一個編輯器上展示了兩種做法。要選擇具體哪些項能在擠壓中留下,就給它們設定 visibilityPriority。
如果你有自己的溢位操作,用 ToolbarOverflowMenu 把它們併入系統選單,而不是再畫一個選單。UIKit 的對應版本是 additionalOverflowItems。
關閉側邊欄
.toolbarVerticalBehavior(.disabled) 會把各種欄送回頂部和底部,並恢復橫向的狀態列。UIKit 的等價寫法是讓 preferredVerticalBarBehavior 返回 .disabled。
當側邊欄佔用了你確實需要的寬度時,就用它:影片播放器、相機、面板上唯一的一個關閉按鈕,或者像設計指南裡翁天信的相框那樣的佈局。Apple 的建議(Natalia 也重申了)是,大多數介面都保留豎向欄。使用者會在其他 App 裡習慣這一列,而固定不動的欄會讓你的 App 看起來像是沒跟上更新。
如果確實要關閉,就對整個呈現單位關閉,並且保持不變。這個偏好在視窗一級生效,或者在最近的呈現單位(比如一個面板)生效。在一個視窗內,SwiftUI 會從 NavigationStack 的頂層檢視、TabView 當前選中的標籤,或 NavigationSplitView 的尾側欄讀取它,所以在使用者推入新頁面時改變它,會讓介面框架在操作進行到一半時突然翻轉。Artem Novichkov 在模擬器裡也看到了同樣的現象:這個設定會一路上傳到視窗或最近的呈現單位,這就是為什麼 Artem 的示例把那個頁面以全螢幕方式呈現。
在面板內部使用這個修飾符,只會影響面板本身,下面的頁面可以保持預設。
面板
面板無需額外程式碼就能獲得同樣的適配,此外還多了一個位置選項。
在外螢幕上,面板的控制元件會移到側邊。在內螢幕上,居中的面板保持橫向工具列。當手機半折時,系統會把面板滑離摺痕。你不用盯著轉軸就能做到這一點。
iOS 27 新增的 presentationPlacement 可取 automatic、center、leading 或 trailing。相簿可以在使用者點按的縮圖那一側打開面板。位置也會改變工具列。Apple 關於準備 App 的文件說,在內螢幕上,居中和前側的面板使用橫向工具列,尾側的面板使用豎向工具列。
在 iOS 27.1 測試版中,Natalia 發現尾側面板在平放和半折兩種姿態下都沒有用上那個豎向工具列,關閉按鈕可能會落在狀態顯示區和鏡頭下面。在 Natalia 的測試中,一行程式碼就解決了:
.sheet(item: $selection) { item in
DetailSheet(item: item)
.presentationPlacement(.trailing)
.presentationDetents([.large])
}
Natalia 明確表示,這個 detent 是針對這一測試版的臨時解決辦法,並不是位置設定的必要條件。在照抄之前,先在自己的建置版本里檢查尾側面板,SDK 更新後再檢查一次。檢查清單裡包含了這一項。
Rob Swift 遇到了面板的另一種問題:自定義的高度沒有落在安全區域內。如果你手動設定高度,上線之前在展開、合上和半折三種狀態下都看一看。
浮動按鈕由你來擺放
這是每個適配真實 App 的人都會碰上的缺口。Harshil 的展開佈局表現得像 iPad,合上時的佈局主要是處理新的水平內邊距。剩下的那個控制元件是一個浮動分享按鈕,懸而未決的問題是它需要多少內邊距才能和工具列對齊。
The only tricky thing left is how to handle floating UI like this share button It's tricky to figure out some heuristics for how much padding those need to have to align with toolbars
Rob Swift 的截圖展示了同一類 bug。兩個尾側欄項排得很奇怪。一個用自定義檢視畫的鈴鐺,把本該在頂部的按鈕擠到了它下面。一個浮動操作按鈕位置偏高,因為即使工具列已經離開了底邊,底部安全區域依然很高。Matthaus Woolard 回覆 Harshil 時建議,把那個分享按鈕放進 safeAreaBar(edge: .bottom),這樣滾動檢視會自動加上內邊距,欄會在區域性模糊,內邊距也由系統提供。Matthaus 還認為,在有空間時,Apple 更希望你把“分享”移進側邊欄。
I use size classes and I'm still finding the Duo to be completely different animal than iPhone or iPad. Here you see 2 right bar button items laying out very oddly on the Duo.
如果底部內邊距仍在為一個已經不在底部的工具列預留空間,就把它當作 bug 提交回饋,Matthaus 在那條貼文裡也是這樣建議的。然後再想想這個按鈕是否應該做成工具列項。一個往列表裡新增內容的加號按鈕,更多時候應該作為工具列操作跟著那個列表,而不是一個在 ZStack 裡定位的圓形按鈕。
Sahil AK 對 TimeWave 的適配是一個簡短的例子:原生工具列 API 完成了工作,剩下的是那個浮動加號。貼文見設計指南。
速查表
| 你想要 | SwiftUI | UIKit |
|---|---|---|
| 讓自定義項加入側邊欄 | 在項上使用 .axisBehavior(.verticalPreferred) |
|
| 即使有圖示也讓項保持橫向 | .axisBehavior(.horizontalOnly) |
|
| 為側邊欄繪製自定義檢視 | 讀取 @Environment(\.toolbarVerticalEdge) |
traitCollection.verticalBarEdge |
| 把關閉按鈕放在側邊欄頂部 | .cancellationAction 位置 |
前側項組,leftItemsSupplementBackButton = false |
| 不讓主要操作進入溢位選單 | .topBarPinnedTrailing |
navigationItem.pinnedTrailingGroup |
| 把“圖示加數字”的檢視變成圖示和標記 | .badge() |
UIBarButtonItem.badge |
| 選擇先收起標籤還是操作 | .toolbarVerticalCompressionBehavior(.prefersTabBar) 或 .prefersToolbarItems |
|
| 選擇哪些項能在擠壓中留下 | .visibilityPriority() |
|
| 把自己的操作併入系統溢位選單 | ToolbarOverflowMenu |
additionalOverflowItems |
| 把各種欄送回頂部和底部 | .toolbarVerticalBehavior(.disabled) |
preferredVerticalBarBehavior 返回 .disabled |
| 設定面板位置 | .presentationPlacement(.leading / .center / .trailing) |
UIKit 列留空的,是上面這些資料只在 SwiftUI 中介紹過的功能。在認定沒有對應功能之前,先查一下 Apple 的 UIKit 文件。
繼續閱讀

iPhone Duo App 創意:開發者正在用摺疊螢幕做的 6 件事
300 多個 iPhone Duo 示範、概念和原型,歸納為反覆出現的六種模式,從半折的筆記型電腦形態到把轉軸當控制器。如果你正在考慮做點什麼,可以從這裡開始。

iPhone Duo 能執行 iPad App 嗎?iPhone Duo 與 iPad mini
iPhone Duo 的內螢幕幾乎有 iPad mini 那麼大,但它執行的是 iOS,不是 iPadOS。這對僅限 iPad 的 App 意味著什麼、為什麼很多 Duo App 看起來會像它們的 iPad 版,以及 Duo 從 iPad 借來了什麼。

iPhone Duo 遊戲:即將到來的作品,以及摺疊螢幕給遊戲帶來了什麼
哪些遊戲已在 iPhone Duo 上亮相、未更新的遊戲在展開的螢幕上是什麼樣子,以及開發者正在用轉軸、半折和兩塊螢幕做出哪些新玩法。