飲料訂購 APP IOS SwiftUI — HomeView 設計
HomeView 現在的定位是「首頁入口 + 導航控制」,不是負責每個功能頁面的內容。這樣設計的核心原因是讓每個頁面各自獨立,避免首頁變成什麼都塞的大型 View。
飲料訂購 APP IOS SwiftUI — HomeView 設計

HomeView 現在的定位是「首頁入口 + 導航控制」,不是負責每個功能頁面的內容。這樣設計的核心原因是讓每個頁面各自獨立,避免首頁變成什麼都塞的大型 View。
設計原因
HomeView 使用 NavigationStack 作為整個首頁流程的導航容器。底部五個功能按鈕使用 NavigationLink(value:),點擊後會 push 到對應頁面,例如 MallView、NewsView、DrinkListView、NearbyStoresView、MemberView。
這樣做的好處是每個功能頁面都是獨立 View,進入後會有系統提供的返回鍵,可以自然回到首頁。這比在 HomeView 裡用 selectedTab 切換畫面更符合「首頁跳轉到功能頁」的需求。
架構重點
HomeView 只負責三件事:
- 顯示首頁內容
- 顯示底部五個功能入口
- 根據使用者點擊,導航到對應 View
每個功能頁面都有自己的 View 和 ViewModel:
MallView(viewModel: mallViewModel)
NewsView(viewModel: newsViewModel)
DrinkListView(viewModel: drinkListViewModel, showsNavigationStack: false)
NearbyStoresView(viewModel: nearbyStoresViewModel)
MemberView(viewModel: memberViewModel)
這樣符合 MVVM 的分工:
HomeView
只負責首頁 UI 與導航
MallView / NewsView / NearbyStoresView / MemberView / DrinkListView
各自負責自己的畫面內容
MallViewModel / NewsViewModel / NearbyStoresViewModel / MemberViewModel / DrinkListViewModel
各自負責自己的狀態與資料邏輯
每一個頁面的流程
首頁 -> 點擊功能按鈕 -> 進入功能頁 -> 返回首頁
1.App 啟動後進入 HomeView
WindowGroup {
HomeView()
}
App 啟動後,第一個畫面就是首頁。
2.HomeView 建立首頁用的 NavigationStack
NavigationStack {
HomeLandingView()
.safeAreaInset(edge: .bottom, spacing: 0) {
HomeShortcutBar()
}
.navigationDestination(for: HomeTab.self) { tab in
destination(for: tab)
}
}
重點整理:
- NavigationStack 負責頁面 push / pop
- HomeLandingView 負責首頁內容
- HomeShortcutBar 負責底部五個功能按鈕
- navigationDestination 負責定義每個按鈕要跳去哪個頁面
3. 使用 HomeTab 管理五個功能入口
private enum HomeTab: CaseIterable, Identifiable {
case exchange
case news
case order
case nearby
case member
}
這樣可以把五個功能入口集中管理。
每個 case 對應一個功能:
exchange -> 商城兌換
news -> 最新消息
order -> 點餐
nearby -> 附近門市
member -> 會員專區
好處是:
- 不用在 UI 裡到處寫字串
- 新增功能時只要擴充 enum
- 可以統一管理 title、icon、跳轉目標
4. HomeShortcutBar 用 ForEach 產生五個按鈕
ForEach(HomeTab.allCases) { tab in
NavigationLink(value: tab) {
item(for: tab)
.frame(maxWidth: .infinity)
}
}
這段流程是:
讀取 HomeTab.allCases
-> 產生五個 NavigationLink
-> 每個按鈕綁定一個 HomeTab
-> 點擊後交給 navigationDestination 處理
5. 按鈕使用 NavigationLink
用 NavigationLink(value:),代表這個按鈕本身就是導航行為
使用者點擊按鈕
-> NavigationLink 傳出 HomeTab
-> NavigationStack 接收
-> navigationDestination 找到對應 View
-> push 到新頁面
6. navigationDestination 定義跳轉規則
.navigationDestination(for: HomeTab.self) { tab in
destination(for: tab)
}
使用者點擊某個 NavigationLink(value:) 時,SwiftUI 會把 HomeTab 傳給這裡。
然後 destination(for:) 根據 tab 回傳不同頁面:
private func destination(for tab: HomeTab) -> some View {
switch tab {
case .exchange:
MallView(viewModel: mallViewModel)
case .news:
NewsView(viewModel: newsViewModel)
case .order:
DrinkListView(viewModel: drinkListViewModel, showsNavigationStack: false)
case .nearby:
NearbyStoresView(viewModel: nearbyStoresViewModel)
case .member:
MemberView(viewModel: memberViewModel)
}
}
這樣導覽邏輯集中在同一個地方,之後很好維護。
7.每個功能頁拆成獨立 View
目前拆成:
MallView
NewsView
DrinkListView
NearbyStoresView
MemberView
這樣符合單一職責原則,每個 View 只處理自己的 UI。
8. 每個頁面都有自己的 ViewModel
@StateObject private var mallViewModel = MallViewModel(title: "商城兌換")
@StateObject private var newsViewModel = NewsViewModel(title: "最新消息")
@StateObject private var drinkListViewModel = DrinkListViewModel()
@StateObject private var nearbyStoresViewModel = NearbyStoresViewModel(title: "附近門市")
@StateObject private var memberViewModel = MemberViewModel(title: "會員專區")
這些 ViewModel 由 HomeView 持有,原因是:
- 首頁負責建立要傳入功能頁的狀態物件
- 當使用者進出頁面時,資料狀態可以保留
- 不會每次 push 頁面都重新建立資料
例如點餐頁如果有購物車資料,回首頁再進去時,不會因為重建 ViewModel 而消失。
這樣未來可以各自擴充資料邏輯,例如:
MallViewModel -> 載入兌換商品、點數資料
NewsViewModel -> 載入最新消息列表
DrinkListViewModel -> 載入飲品、購物車、送出訂單
NearbyStoresViewModel -> 載入門市、定位、距離排序
MemberViewModel -> 登入、註冊、會員資料
9. 子頁面使用 @ObservedObject 接收 ViewModel
例如
struct MallView: View {
@ObservedObject var viewModel: MallViewModel
}
原因是:
HomeView 擁有 ViewModel
子頁面只是觀察這個 ViewModel
所以子頁面使用 @ObservedObject,不是 @StateObject。
自己建立並持有物件 -> @StateObject
外部傳進來的物件 -> @ObservedObject
MVVM 資料流
HomeView 建立 ViewModel
-> NavigationLink 觸發跳轉
-> navigationDestination 建立對應 View
-> 將 ViewModel 傳入子 View
-> 子 View 使用 @ObservedObject 觀察狀態
10.首頁底部 bar 使用 safeAreaInset
.safeAreaInset(edge: .bottom, spacing: 0) {
HomeShortcutBar()
}
這樣底部選單會貼在安全區域底部,不會被 iPhone Home Indicator 擋住
比直接用 VStack { Spacer(); HomeShortcutBar() } 更適合做固定底部工具列。
11. 底部五個入口用 HStack
HStack(alignment: .bottom, spacing: 0)
原因是這個設計不是標準系統 Tab Bar,而是客製化底部入口。
使用 HStack 的好處:
- 可以自訂五個按鈕寬度
- 可以讓每個按鈕平均分配
- 可以特別處理中間「點餐」凸起按鈕
- 可以自訂 icon、文字、顏色、陰影
12.中間點餐按鈕獨立設計
if tab == .order {
// 中間凸起按鈕
} else {
// 一般按鈕
}
這是因為中間按鈕視覺上和其他按鈕不同,技術上是同一個 NavigationLink,只是 label 長得不一樣。
메타데이터
- post_id
- fd565fc68435
- slug
- 飲料訂購-app-ios-homeview-設計-fd565fc68435
- url
- https://medium.com/@croc0909/%E9%A3%B2%E6%96%99%E8%A8%82%E8%B3%BC-app-ios-homeview-%E8%A8%AD%E8%A8%88-fd565fc68435
- canonical_url
- https://medium.com/@croc0909/%E9%A3%B2%E6%96%99%E8%A8%82%E8%B3%BC-app-ios-homeview-%E8%A8%AD%E8%A8%88-fd565fc68435
- author_url
- https://medium.com/@croc0909
- status
- ok
- fetched_at
- 2026-06-12 18:14:10