SwiftUI 框架

iOS 18更新:SwiftUI 新功能介紹

iOS 18更新:SwiftUI 新功能介紹
iOS 18更新:SwiftUI 新功能介紹
In: SwiftUI 框架

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引入了許多顯著的強化功能。這篇教程提供了一些新功能的簡短介紹。對於更複雜的功能,我們將創建詳細的專題教程,深入探討它們的應用和優勢。請繼續關注這些即將推出的教程。

作者
Simon Ng
軟體工程師,AppCoda 創辦人。著有《iOS 17 App 程式設計實戰心法》、《iOS 17 App程式設計進階攻略》以及《精通SwiftUI》。曾任職於HSBC, FedEx等跨國企業,專責軟體開發、系統設計。2012年創立AppCoda技術部落格,定期發表iOS程式教學文章。現時專注發展AppCoda業務,致力於iOS程式教學、產品設計及開發。你可以到推特與我聯絡。
評論
更多來自 AppCoda 中文版
如何使用 Swift 整合 Google Gemini AI
SwiftUI 框架

如何使用 Swift 整合 Google Gemini AI

在即將到來的 WWDC,Apple 預計將會發佈一個本地端的大型語言模型 (LLM)。 接下來的 iOS SDK 版本將讓開發者更輕易地整合 AI 功能至他們的應用程式中。然而,當我們正在等待 Apple 推出自家的生成 AI 模型時,其他公司(如 OpenAI
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。