前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >SwiftUI 新容器视图 API 深度解析:轻松构建自定义布局

SwiftUI 新容器视图 API 深度解析:轻松构建自定义布局

原创
作者头像
Swift社区
发布于 2024-09-30 03:17:18
发布于 2024-09-30 03:17:18
4110
举报
文章被收录于专栏:Swift社区Swift社区

前言

自 SwiftUI 的第一个版本发布以来,它就拥有了几种容器视图。最常用的有 HStack、VStack、List 等。今年,Apple 引入了新的 API,使我们能够以全新的方式构建自定义容器视图。本周,我们将学习 SwiftUI 新的分解 API 的优势。

容器视图

容器视图就是一个可以包含其他视图的视图。我们可以使用 @ViewBuilder 闭包轻松定义一个容器视图。以下是一个示例:

代码语言:swift
AI代码解释
复制
struct Card<Content: View>: View {
    @ViewBuilder var content: Content
    
    var body: some View {
        VStack {
            content
        }
        .padding()
        .background(Material.regular, in: .rect(cornerRadius: 8))
        .shadow(radius: 4)
    }
}

如上面的例子所示,我们创建了 Card 视图,它是一个用于容纳任何 SwiftUI 视图的容器视图。它使用 @ViewBuilder 闭包包裹了内容,并添加了一个圆角背景和阴影。

代码语言:swift
AI代码解释
复制
struct ContentView: View {
    var body: some View {
        Card {
            Text("Hello, World!")
            Text("My name is Majid Jabrayilov")
        }
    }
}

这个 Card 类型使用起来非常简单。你只需创建一个 Card,并使用闭包提供内容。通过在 Card 容器视图内嵌入不同的视图,你可以在应用的多个屏幕中复用它。

这是使用容器视图的主要优势之一:你可以通过将共享的功能封装在容器视图中,在应用的不同地方重复使用它们。

想了解更多关于 @ViewBuilder 闭包的内容,可以查看我关于 “SwiftUI 中 @ViewBuilder 的强大功能” 的文章。

使用 ViewBuilder

@ViewBuilder 闭包让我们可以轻松地组合多个视图,并将一个视图嵌入到另一个视图中。但是如何从 @ViewBuilder 闭包中提取子视图呢?SwiftUI 引入了新的 API,允许我们重新组合视图。例如,我们可以从通过 @ViewBuilder 闭包构建的内容视图中提取子视图,并根据需要将它们放置。

代码语言:swift
AI代码解释
复制
struct Carousel<Content: View>: View {
    @ViewBuilder var content: Content
    
    var body: some View {
        ScrollView(.horizontal) {
            LazyHStack {
                ForEach(subviews: content) { subview in
                    subview
                        .containerRelativeFrame(.horizontal)
                }
            }
            .scrollTargetLayout()
        }
        .scrollTargetBehavior(.viewAligned)
        .contentMargins(16)
    }
}

如上面的示例所示,我们使用了带有 subviews 参数的 ForEach 视图,这使我们能够提取内容视图的子视图并对它们进行迭代。

代码语言:swift
AI代码解释
复制
struct ContentView: View {
    var body: some View {
        Carousel {
            Color.yellow
            Color.orange
            Color.red
            Color.blue
            Color.green
        }
    }
}

SwiftUI 使用特定的 Subview 类型来公开提取视图的实例。它符合 View 协议,因此我们仍然可以附加额外的 SwiftUI 视图修饰符。它还为我们提供了 id 属性,这是一个唯一标识符,以及与特定视图关联的容器值。我们将在接下来的文章中更多讨论容器值。

访问子视图

另一种新的 API 允许我们通过索引访问子视图,而不是使用 ForEach 视图进行迭代。

代码语言:swift
AI代码解释
复制
struct Magazine<Content: View>: View {
    @ViewBuilder var content: Content
    
    var body: some View {
        ScrollView {
            Group(subviews: content) { subviews in
                if !subviews.isEmpty {
                    subviews[0]
                        .padding(.horizontal)
                        .containerRelativeFrame(.vertical) { length, _ in
                            return length / 3
                        }
                }
                
                if subviews.count > 1 {
                    ScrollView(.horizontal) {
                        LazyHStack {
                            ForEach(subviews[1...], id: \.id) { subview in
                                subview
                                    .containerRelativeFrame([.horizontal, .vertical])
                            }
                        }
                        .scrollTargetLayout()
                    }
                    .scrollTargetBehavior(.viewAligned)
                    .contentMargins(16)
                }
            }
        }
    }
}

在上面的示例中,我们使用了带有 subviews 参数的 Group 视图,它允许我们将子视图提取到一个名为 SubviewsCollection 的集合类型中。SubviewsCollection 类型符合 RandomAccessCollection 协议,并为我们提供了通过索引访问的功能。

组合子视图

如你所见,我们使用 Group 视图来分解内容视图,然后以另一种方式组合子视图。我们还利用了 id 参数的功能,允许我们使用 ForEach 视图与普通数据一起工作。

代码语言:swift
AI代码解释
复制
struct ContentView: View {
    var body: some View {
        Magazine {
            Color.yellow
            Color.orange
            Color.red
            Color.blue
            Color.green
        }
    }
}

可运行的 Demo

根据文章内容,我将提供一个可以展示如何使用 SwiftUI 新的容器视图 API 构建自定义视图的简单示例,包含 CardCarouselMagazine 容器视图。

代码语言:swift
AI代码解释
复制
import SwiftUI

// 定义 Card 视图,作为一个基本的容器视图
struct Card<Content: View>: View {
    @ViewBuilder var content: Content
    
    var body: some View {
        VStack {
            content
        }
        .padding()
        .background(Material.regular, in: RoundedRectangle(cornerRadius: 8))
        .shadow(radius: 4)
    }
}

// 定义 Carousel 视图,横向滚动的自定义容器视图
struct Carousel<Content: View>: View {
    @ViewBuilder var content: Content
    
    var body: some View {
        ScrollView(.horizontal) {
            LazyHStack {
                ForEach(subviews: content) { subview in
                    subview
                        .containerRelativeFrame(.horizontal)
                }
            }
            .scrollTargetLayout()
        }
        .scrollTargetBehavior(.viewAligned)
        .contentMargins(16)
    }
}

// 定义 Magazine 视图,具有垂直和水平组合布局的自定义容器视图
struct Magazine<Content: View>: View {
    @ViewBuilder var content: Content
    
    var body: some View {
        ScrollView {
            Group(subviews: content) { subviews in
                // 第一个子视图为大图
                if !subviews.isEmpty {
                    subviews[0]
                        .padding(.horizontal)
                        .containerRelativeFrame(.vertical) { length, _ in
                            return length / 3
                        }
                }
                
                // 其余子视图为横向滚动小图
                if subviews.count > 1 {
                    ScrollView(.horizontal) {
                        LazyHStack {
                            ForEach(subviews[1...], id: \.id) { subview in
                                subview
                                    .containerRelativeFrame([.horizontal, .vertical])
                            }
                        }
                        .scrollTargetLayout()
                    }
                    .scrollTargetBehavior(.viewAligned)
                    .contentMargins(16)
                }
            }
        }
    }
}

// 主视图,使用自定义容器视图
struct ContentView: View {
    var body: some View {
        VStack {
            // 使用 Card 视图
            Card {
                Text("SwiftUI 容器视图示例")
                    .font(.headline)
                Text("使用 Card 容器轻松复用视图")
            }
            .padding()
            
            // 使用 Carousel 视图
            Carousel {
                Color.yellow
                Color.orange
                Color.red
                Color.blue
                Color.green
            }
            .frame(height: 100)
            .padding()
            
            // 使用 Magazine 视图
            Magazine {
                Color.pink
                Color.purple
                Color.teal
                Color.mint
            }
            .frame(height: 300)
        }
        .padding()
    }
}

// 主应用入口
@main
struct ContainerViewDemoApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

功能概述

  1. Card:一个简单的容器视图,可以包裹任何内容并添加背景和阴影。你可以在应用中的多个地方使用该容器来保持一致的样式。
  2. Carousel:一个横向滚动的容器视图,可以自动排列并展示内容,适合展示横向滑动的图像或视图。
  3. Magazine:一个自定义的容器视图,允许你将第一个子视图设置为大图,其他子视图横向排列展示。类似于杂志布局。

运行这个Demo

此代码展示了如何在 SwiftUI 中构建自定义的容器视图,灵活地将不同的布局封装在容器中,以便在应用中多次复用这些布局模式。

总结

通过使用 SwiftUI 新引入的 API 以及容器视图,你可以轻松构建具有良好复用性的自定义布局,提升应用的开发效率和代码可维护性。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 容器视图
  • 使用 ViewBuilder
  • 访问子视图
  • 组合子视图
  • 可运行的 Demo
    • 功能概述
    • 运行这个Demo
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档