SwiftUI的技術不斷演進,每次更新都讓 iOS 應用程式開發變得更加便捷。隨著 iOS 18 Beta 的推出,SwiftUI 引入了多個令人興奮的新功能,使開發者僅需幾行程式碼即可實現出色的效果。
本教學文章旨在探索這個版本中的幾項主要改進,幫助你了解如何運用這些新功能。
浮動標籤列 (Floating Tab Bar)
SwiftUI中的標籤視圖(Tab View)已大大增強,新增了浮動標籤列。此新功能可以輕鬆轉換為側邊欄,為用戶提供訪問應用完整功能的直觀方式。
在iPad上,用戶可以通過點擊標籤列上的側邊欄按鈕將標籤列轉換為側邊欄。對於開發者來說,如果你想支持這個功能,只需一行程式碼。你只需將標籤視圖樣式設定為 .sidebarAdaptable
:
struct ContentView: View {
@State var customization = TabViewCustomization()
var body: some View {
TabView {
Tab("Home", systemImage: "house.fill") {
}
Tab("Bookmark", systemImage: "bookmark.circle.fill") {
}
Tab("Videos", systemImage: "video.circle.fill") {
}
Tab("Profile", systemImage: "person.crop.circle") {
}
Tab("Settings", systemImage: "gear") {
}
}
.tint(.yellow)
.tabViewStyle(.sidebarAdaptable)
.tabViewCustomization($customization)
}
}
設置選項後,用戶可以輕鬆在側邊欄和標籤列之間切換,增強了導航靈活性。此外,新的標籤列提供了廣泛的自定義。通過將 .tabViewCustomization
修飾符附加到標籤視圖,用戶可以定制標籤列的菜單項目。
活頁式呈現尺寸控制 (Sheet Presentation Sizing)
活頁式呈現尺寸現在在各平台上始終如一並且簡化。通過使用 .presentationSizing
修飾符,你可以使用如 .form
或 .page
等預設輕鬆創建具有理想尺寸的活頁,甚至可以自定義尺寸。以下是示例:
struct PresentationSizingDemo: View {
@State private var showSheet = false
var body: some View {
Button {
showSheet.toggle()
} label: {
Text("Show sheet")
}
.sheet(isPresented: $showSheet) {
Text("This is a quick demo of presentation sizing.")
.presentationSizing(.form)
}
}
}
在iPad上,.form
預設會顯示較小的活頁,而 .page
則較大。然後在iPhone上,兩者沒有尺寸差異。
色彩網格漸變 (Color Mesh Gradients)
SwiftUI現在提供了對色彩網格漸變的廣泛支持。新的 MeshGradient
功能允許你使用顏色陣列創建二維漸變效果。通過結合控制點和顏色,你可以設計各種漸變效果。
以下示範了使用 MeshGradient
創建的一些漸變效果:
struct ColorMeshDemo: View {
var body: some View {
VStack {
MeshGradient(
width: 3,
height: 3,
points: [
.init(0, 0), .init(0.5, 0), .init(1, 0),
.init(0, 0.5), .init(0.3, 0.5), .init(1, 0.5),
.init(0, 1), .init(0.5, 1), .init(1, 1)
],
colors: [
.gray, .purple, .indigo,
.orange, .cyan, .blue,
.yellow, .green, .teal
]
)
MeshGradient(
width: 2,
height: 2,
points: [
.init(0, 0), .init(1, 0),
.init(0, 1), .init(1, 1)
],
colors: [
.red, .purple,
.yellow, .green
]
)
}
.ignoresSafeArea()
}
}
縮放轉換 (Zoom Transition)
SwiftUI現在內建支持縮放轉換。你可以使用 .matchedTransitionSource
修飾符輕鬆實現縮放轉換效果。
如果你熟悉使用 matchedGeometryEffect
,你會發現 matchedTransitionSource
相當類似。以下是我們編寫的示例程式碼,用於創建上述的縮放轉換:
struct ZoomTransitionDemo: View {
let samplePhotos = (1...20).map { Photo(name: "coffee-\($0)") }
@Namespace() var namespace
var body: some View {
NavigationStack {
ScrollView {
LazyVGrid(columns: [ GridItem(.adaptive(minimum: 150)) ]) {
ForEach(samplePhotos) { photo in
NavigationLink {
Image(photo.name)
.resizable()
.navigationTransition(.zoom(sourceID: photo.id, in: namespace))
} label: {
Image(photo.name)
.resizable()
.scaledToFill()
.frame(minWidth: 0, maxWidth: .infinity)
.frame(height: 150)
.cornerRadius(30.0)
}
.matchedTransitionSource(id: photo.id, in: namespace)
}
}
}
}
.padding()
}
}
matchedTransitionSource修飾符應用於
NavigationLink,具有特定的照片ID,使該視圖成為導航轉換的來源。對於目標視圖(也是一個
Image視圖),使用了
navigationTransition` 修飾符來渲染縮放轉換。
SF Symbols 6支援更多動畫效果
iOS 17 引入了一個出色的SF Symbols動畫集合。開發者可以通過新的 symbolEffect
修飾符使用這些動畫。iOS 18 將SF Symbols推進到第6版,為開發者提供了更多種類的動畫符號。
下面是一段用於新 rotate
動畫的示例程式碼:
Image(systemName: "ellipsis.message")
.font(.system(size: 300))
.symbolRenderingMode(.palette)
.foregroundStyle(.purple, .gray)
.symbolEffect(.rotate, value: animate)
.onTapGesture {
animate.toggle()
}
除了 rotate
動畫,SF Symbols 6還提供了兩種其他類型的動畫,包括 .wiggle
和 .breathe
。
SwiftUI圖表的改進
SwiftUI圖表框架現在支持向量化和函數圖。例如,假設你想為以下函數繪製圖表:
y = x^2
你可以使用 LinePlot
來繪製該函數圖形:
Chart {
LinePlot(x: "x", y: "y") { x in
return pow(x, 2)
}
.foregroundStyle(.green)
.lineStyle(.init(lineWidth: 10))
}
.chartXScale(domain: -4...4)
.chartYScale(domain: -4...4)
.chartXAxis {
AxisMarks(values: .automatic(desiredCount: 10))
}
.chartYAxis {
AxisMarks(values: .automatic(desiredCount: 10))
}
.chartPlotStyle { plotArea in
plotArea
.background(.yellow.opacity(0.02))
}
你可以簡單地提供函數給 LinePlot
來繪製圖形。
滾動視圖提供更多控制
新版本的SwiftUI提供了一組強大的新API,讓開發者可以精確控制滾動視圖。引入的 onScrollGeometryChange
修飾符允許你跟蹤滾動視圖的狀態。這項新功能使你可以有效地回應滾動視圖內容偏移、內容大小和其他滾動相關屬性的變化。
以下是一段示例代碼,演示如何使用這個修飾符在用戶滾動列表後顯示「回到頂部」按鈕:
struct ScrollViewDemo: View {
let samplePhotos = (1...20).map { Photo(name: "coffee-\($0)") }
@State private var showScrollToTop = false
var body: some View {
ScrollView {
VStack {
ForEach(samplePhotos) { photo in
Image(photo.name)
.resizable()
.scaledToFill()
.frame(height: 200)
.clipShape(RoundedRectangle(cornerRadius: 15))
}
}
}
.padding(.horizontal)
.overlay(alignment: .bottom) {
if showScrollToTop {
Button("Scroll to top") {
}
.controlSize(.extraLarge)
.buttonStyle(.borderedProminent)
.tint(.green)
}
}
.onScrollGeometryChange(for: Bool.self) { geometry in
geometry.contentOffset.y < geometry.contentInsets.bottom + 200
} action: { oldValue, newValue in
withAnimation {
showScrollToTop = !newValue
}
}
}
}
滾動視圖的幾何形狀在滾動時經常變化。我們可以利用 onScrollGeometryChange
修飾符來捕捉更新,並相應地顯示「回到頂部」按鈕。
SwiftUI還引入了 onScrollVisibilityChange
修飾符,用於滾動視圖內的視圖。這個修飾符允許你檢測特定視圖何時變得可見並執行特定操作。
假設我們在滾動視圖的末尾有一個 Rectangle
視圖,我們希望在該視圖進入視野時觸發顏色變換動畫。我們可以使用 onScrollVisibilityChange
修飾符來檢測視圖的可見性變化。
Rectangle()
.fill(color)
.frame(height: 100)
.onScrollVisibilityChange(threshold: 0.9) { visible in
withAnimation(.linear(duration: 5)) {
color = visible ? .green : .blue
}
}
控制中心中的小工具 (Widgets in Control Center)
你現在可以設計可調整大小的自定義控制,如按鈕和開關,這些控制可以放置在控制中心或鎖定屏幕上。控制是一種新的小工具類型,使用App Intents可以輕鬆構建。
要在控制中心創建控制小工具,你需要採用 ControlWidget
協議並提供實現。以下是Apple提供的示例程式碼:
struct StartPartyControl: ControlWidget {
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(
kind: "com.apple.karaoke_start_party"
) {
ControlWidgetButton(action: StartPartyIntent()) {
Label("Start the Party!", systemImage: "music.mic")
Text(PartyManager.shared.nextParty.name)
}
}
}
}
我們將在單獨的教程中進一步研究控制小工具。
混合顏色修飾符
你現在可以使用新的 mix
修飾符將兩種不同顏色混合以創建所需的色調。以下是一個示例:
VStack {
Color.purple.mix(with: .green, by: 0.3)
.frame(height: 100)
Color.purple.mix(with: .green, by: 0.5)
.frame(height: 100)
Color.purple.mix(with: .green, by: 0.8)
.frame(height: 100)
}
你只要提供混合的顏色和混合比例,SwiftUI就會根據這些參數生成新的顏色。
文本的視覺效果
你現在可以通過實現 TextRenderer
來為SwiftUI文本視圖擴展自定義渲染效果。以下是示例:
struct CustomTextRenderer: TextRenderer {
func draw(layout: Text.Layout, in context: inout GraphicsContext) {
for line in layout {
for (index, slice) in runs.enumerated() {
context.opacity = (index % 2 == 0) ? 0.4 : 1.0
context.translateBy(x: 0, y: index % 2 != 0 ? -15 : 15)
context.draw(slice)
}
}
}
}
struct TextAnimationDemo: View {
var body: some View {
Text("What's New in SwiftUI")
.font(.system(size: 100))
.textRenderer(CustomTextRenderer())
}
}
通過實現 draw
方法,你可以自定義每個字符的視覺效果。
總結
iOS 18更新為SwiftUI引入了許多顯著的強化功能。這篇教程提供了一些新功能的簡短介紹。對於更複雜的功能,我們將創建詳細的專題教程,深入探討它們的應用和優勢。請繼續關注這些即將推出的教程。