本文通過創(chuàng)建一個文章閱讀器應用,將SwiftUI代碼應用到多個平臺上,來實現(xiàn)跨平臺操作。主要了解如何在不同平臺間實現(xiàn)共享資源,以及如何在各自平臺使用特定資源。
主要內(nèi)容:
- 跨平臺應用的創(chuàng)建
- TabBarView和SideBarView的使用
- 不同平臺的特殊處理
0、介紹和演示
我們在一個平臺上編寫SwiftUI代碼,可以將其擴展到許多平臺。 這也是SwiftUI的一個很重要的特點。在本文我們將創(chuàng)建一個iOS應用程序,之后并重用它的一些組件來創(chuàng)建iPad、macOS平臺應用。
主要可以查看如何在平臺之間共享公共資源,比如模型就可以在各個平臺間共享。還可以給每個平臺使用自己特定的其他資源,比如視圖。
擁有平臺特定的視圖讓我們能夠遵循平臺特定的設計方式,并改進所提供的用戶體驗我們的應用程序。
還有其他的比如watchOS,本文不做分析。
本文將創(chuàng)建一個文章閱讀器應用,應用在iPhone、iPad、macOS平臺。
1、創(chuàng)建一個工程

說明:
- 注意這里是在Multiplatform創(chuàng)建APP的。
- 只有在這里創(chuàng)建才可以實現(xiàn)在Mac和iOS上多平臺開發(fā)。
2、創(chuàng)建數(shù)據(jù)模型:
將需要顯示的文章內(nèi)容添加到數(shù)據(jù)模型中。這個數(shù)據(jù)模型是共享的,所以要放到Shared文件夾下。
為了工程文件更條理性,創(chuàng)建一個Group文件夾來存儲我們的數(shù)據(jù)模型。因為模型是在所有平臺上共享的,所以我們將創(chuàng)建在“Shared”文件夾下。右鍵點擊Shared文件夾,選擇“New Group”并命名為Models的文件夾。

在Models文件夾中,創(chuàng)建一個新的Swift文件,命名為Article。在“Target”下,勾選“Cross-Platform(iOS)”,“Cross-Platform(macOS)”,然后選擇“創(chuàng)建”。
文件創(chuàng)建時勾選多個平臺,該文件就會在多個平臺上共享

將下來在Article.swift中,創(chuàng)建一個Article結構體:
import Foundation
struct Article: Identifiable {
var id = UUID()
let title: String
let description:
String let type: String
}
說明:
- 給每一篇文章都創(chuàng)建四個屬性,分別為ID,標題、描述、類型
- 標題和描述是文章內(nèi)容本身
- 類型是將其歸類到哪個分類。
- id用來表示文章,用于后面的判斷
在結構體下面,創(chuàng)建三個數(shù)組,每個數(shù)組代表一個虛擬Article實例的數(shù)據(jù)。當然我們自己可以隨便寫。

3、ArticleView
在SharedFolder中,創(chuàng)建一個ArticleView來顯示一個單獨的文章。 同樣,在“Targets”下,勾選“Cross-Platform(iOS)”,“Cross-Platform(macOS)”,以此讓文件在多個平臺共享。
代碼:
import SwiftUI
struct ArticleView: View {
let article: Article
var body: some View {
VStack(alignment: .leading, spacing: 5) {
Text(article.title)
.font(.title)
Text(article.description)
.font(.headline)
Spacer()
}.padding()
}
}
說明:
- 文章本身只是用來顯示文章的標題和描述的視圖
- 這里為了演示僅做簡單的展示,在實際開發(fā)中可以寫更多的視圖。
4、ArticlesListView
文章選擇列表顯示。
接下來,在Shared中,創(chuàng)建一個名為ArticlesListView的SwiftUI視圖,該視圖可以對文章進行排列。
代碼:
import SwiftUI
struct ArticlesListView: View {
let articles: [Article ]
var body: some View {
#if os(macOS)
return view.frame(minWidth: 400, minHeight: 600)
#else
return view
#endif
}
@ViewBuilder
private var view: some View {
List(articles) {article in
NavigationLink(destination: ArticleView(article: article)) {
ArticleView(article: article)
}
}.navigationTitle("\(articles[0].type)")
}
}
說明:
- 在這里,我們通過編譯器指令#if os(macOS)。檢查應用程序是否編譯為Mac設備使用
- 我們的代碼正在編譯中,構建指令允許我們執(zhí)行某些檢查和自定義邏輯
- 這對于基于不同平臺的自定義編譯的跨平臺代碼庫非常方便
- 因此通過對不同平臺的判斷,我們可以對于擁有更大屏幕的Mac應用程序,使用frame方法調(diào)整視圖畫面的大小
- 在顯示ArticlesListView時,使用了NavigationLink進行跳轉導航。
- 這里的列表顯示有多個文章標題,點擊后就可以跳轉到文章視圖界面。
5、TabBarView實現(xiàn)iPhone分類
接下來要對文章進行分類。
我們有三類文章:科技、商業(yè)和體育。對于iPhone,我們希望這三個類別是在底部的標簽欄中顯示為三個不同的標簽
,對于iPad和Mac,我們希望這三個類別被列在側邊欄,因此要分別進行處理,對于iPhone就需要使用TabBarView視圖進行處理。
在iOS文件夾中,新建一個SwiftUI視圖文件TabBarView.swift。注意,這里我們不需要將“Cross-Platform(macOS)”作為Target,因為它只適用于iOS。 是單獨為iPhone使用的,是特有資源。
**代碼實現(xiàn)如下 **
import SwiftUI
struct TabBarView: View {
var body: some View {
TabView {
ArticlesListView(articles: techArticles)
.tabItem {
Image(systemName: "desktopcomputer")
Text("Tech")
}.tag(0)
ArticlesListView(articles: businessArticles)
.tabItem {
Image(systemName: "dollarsign.circle")
Text("Science")
}.tag(1)
ArticlesListView(articles: sportArticles)
.tabItem {
Image(systemName: "sportscourt")
Text("Design")
}.tag(2)
}.navigationTitle("Articles")
}
}
說明:
- 在TabView內(nèi)使用之前定義的ArticlesListView
- 每個選項卡都填充之前定義好的Article數(shù)組
- 這樣在切換三種選項卡的時候,就可以顯示對應的文章內(nèi)容。
- 使用.tabitem修飾符來設置每個標簽的圖像和文本的樣式。
- 圖片是使用systemName創(chuàng)建的,它允許我們從內(nèi)置的SF Symbols圖標集wh中加載圖片
- 使用tag()來定位它們的制表符順序。
開始運行
在ContentView中添加代碼:
struct ContentView: View {
var body: some View {
NavigationView{
TabBarView()
}
}
}
說明:
- 將TabBarView添加到一個NavigationView中
- 這樣可以確保在ArticlesListView可以使用NavigationLink
- 當您導航到不同的標簽類別時,該類別的文章就會顯示出來
結果:

6、SideBarView實現(xiàn)Mac
側邊欄導航是一個三列導航,提供對頂級集合的快速訪問。 需要使用SideBarView來實現(xiàn)。
在macOS文件夾下,創(chuàng)建一個新的SwiftUI視圖文件SideBarView.swift。 在創(chuàng)建時,同時選擇iOS和Mac版本,雖然iPhone不使用SideBarView,但是我們還需要將這個文件作用于iPad。所以需要在多個平臺進行使用。是共享資源。
import SwiftUI
struct SideBarView: View {
@ViewBuilder
var body: some View {
List() {
NavigationLink(
destination: ArticlesListView(articles: techArticles),
label: {
Label("Tech", systemImage: "desktopcomputer")
}
)
NavigationLink(
destination: ArticlesListView(articles: businessArticles) ,
label: { Label("Business", systemImage: "dollarsign.circle")
}
)
NavigationLink(
destination: ArticlesListView(articles: sportArticles),
label: { Label("Sport", systemImage: "sportscourt") }
)
}
.navigationTitle("Articles")
.listStyle(SidebarListStyle())
}
}
- 在實現(xiàn)上使用了iOS 14和macOS Big Sur上新的SideBarListStyle列表樣式。
- 其他的點擊跳轉實現(xiàn)和iPhone上的一樣。
- 通過這種新的樣式就可以顯示出側邊欄使用List列出文章類型的三種類型的效果
- 注意一下listStyle
運行:
在ContentView中,注釋掉TabBarView并添加SideBarView:
struct ContentView: View {
var body: some View {
NavigationView{
//TabBarView()
SideBarView()
}
}
}
修改后,在Mac上運行這個應用程序時,就會在Mac上得到一個三級導航文章閱讀應用程序,如下圖所示:

7、iPad實現(xiàn)
上面我們知道Mac和iPad使用SideBarView來顯示,iPhone使用TabBarView來顯示。而iPad又屬于iOS,所以我們需要在ContentView視圖中進行判斷這三種情況.
使用#if來檢測應用程序在哪個操作系統(tǒng)上運行,同時使用horizontalSizeClass屬性來檢測設備是iPad還是iPhone。
代碼:
import SwiftUI
struct ContentView: View {
#if os(iOS)
@Environment(\.horizontalSizeClass) var horizontalSizeClass:UserInterfaceSizeClass?
#endif
@ViewBuilder
var body: some View {
NavigationView {
#if os(iOS)
if horizontalSizeClass == .compact {
TabBarView()
} else {
SideBarView()
}
#else
SideBarView()
#endif
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
說明:
- 創(chuàng)建一個@Environment對象來存儲size類的值。
- 通過檢查size類的值是否為"compact"size類來判斷shi否為iPad或iPhone。
- 因為iphone的尺寸是“compact”,而ipad的尺寸是“compact”。
- 因此,如果size為compact (iPhone),我們將顯示TabBarView。否則是iPad或Mac,我們顯示SideBarView。
如果我們運行在iPad上,將會顯示為

8、總結
- 跨平臺實現(xiàn)的實現(xiàn)需要在Multiplatform下創(chuàng)建工程
- 可以在創(chuàng)建文件時選擇不同的平臺,來決定是否是多平臺共享資源
- Mac和iOS可以通過編譯器指令#if os(macOS)來判斷
- iPhone和iPad可以通過尺寸大小來判斷