编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

SwiftUI入门六:组合复杂的界面

wxchong 2024-11-21 22:07:33 开源技术 53 ℃ 0 评论

Landmarks App的主界面显示了一个可滚动的分类列表,在每个分类中有可以水平滑动的地标数据。在我们构建这个基本的导航功能时,我们可以探索如何组合视图可以适配不同设备的尺寸和旋转方向。

<下载>启动项目并跟随本教程,或打开完成的项目自己研究代码。

01 添加主页视图

现在我们已经有了所有Landmarks App需要的视图,是时候添加一个主页--一个将所有视图统一的视图。主页视图不仅仅是包含所有其它视图,还提供了导航到显示地标的方式。


第一步

新建一个Home.swift文件,并在文件中创建一个自定义的视图

import SwiftUI

struct CategoryHome: View {
    var body: some View {
        Text("Landmarks Content")
    }
}

struct CategoryHome_Previews: PreviewProvider {
    static var previews: some View {
        CategoryHome()
    }
}

第二步

将SceneDelegate.swift文件中的默认显示的地标列表改为显示新创建的CategoryHome视图。

            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(
                rootView: CategoryHome()
                    .environmentObject(UserData())
            )

主页视图是作为Landmarks App的基本,所以它需要一个方式来展现所有的其它视图。

第三步

在Home.swift文件中,为CategoryHome添加一个NavigationView视图来托管Landmarks中的不同视图。

使用导航视图配合NavigationLink实例及相关的修饰器构建App中的导航层级结构。

struct CategoryHome: View {
    var body: some View {
        NavigationView {
            Text("Landmarks Content")
        }
    }
}

第四步

将导航栏的标题设置为Featured。

        NavigationView {
            Text("Landmarks Content")
                .navigationBarTitle(Text("Featured"))
        }
    }

02 创建分类列表

Landmarks App为了便于浏览将所有的分类分成不同的行并竖直排列。我们通过组合竖直和水平stack,以及在列表中添加滚动功能实现。


第一步

使用Dicionary结构体的init(grouping:by:)初始化器及地标的分类属性将地标分成不同的组。

初始项目文件中包含了为每个地标预定义的分类。

struct CategoryHome: View {
    var categories: [String: [Landmark]] {
        Dictionary(
            grouping: landmarkData,
            by: { $0.category.rawValue }
        )
    }
    
    var body: some View {
        NavigationView {

第二步

在Landmarks中使用列表显示分类。

Landmark.Category字段名用来区分列表中的每个条目,这个字段是枚举类型,所以一定是唯一的。

    var body: some View {
        NavigationView {
            List {
                ForEach(categories.keys.sorted(), id: \.self) { key in
                    Text(key)
                }
            }
            .navigationBarTitle(Text("Featured"))
        }
    }

03 添加每行的地标

Landmarks显示的每一行分类都可以水平滑动。添加一个新的视图类型来显示行,然后将所有的这个分类下的地标添加到这行中。


第一步

创建一个新的文件CategoryRow.swift并定义一个新的自定义视图CategoryRow用来存放分类行的内容。

这个视图需要存储指定分类中的地标信息用于显示。

struct CategoryRow: View {
    var categoryName: String
    var items: [Landmark]
    
    var body: some View {
        Text(self.categoryName)
            .font(.headline)
    }
}

struct CategoryRow_Previews: PreviewProvider {
    static var previews: some View {
        CategoryRow(categoryName: landmarkData[0].category.rawValue, items: Array(landmarkData.prefix(4)))
    }
}

第二步

更新Home.swift文件中CategoryHome类型的body属性,将分类信息传入新创建的CategoryRow类型

            List {
                ForEach(categories.keys.sorted(), id: \.self) { key in
                    CategoryRow(categoryName: key, items: self.categories[key]!)
                }
            }

第三步

切换要CategoryRow.swift将分类中的地标显示在HStack中。

    var body: some View {
        HStack(alignment: .top, spacing: 0) {
            ForEach(self.items) { landmark in
                Text(landmark.name)
            }
        }
    }
}

第四步

通过使用frame(width:height:)指定高度并将stack嵌套进一个滚动视图让行视图的内容有更合理的空间。

使用更大样本的数据来更新视图预览可以更容易确定滚动表现正常。

    var body: some View {
        VStack(alignment: .leading) {
            Text(self.categoryName)
                .font(.headline)
                .padding(.leading, 15)
                .padding(.top, 5)
            
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(alignment: .top, spacing: 0) {
                    ForEach(self.items) { landmark in
                        Text(landmark.name)
                    }
                }
            }
            .frame(height: 185)

04 组合主页视图

Landmarks App的主页在用户点击查看详情前需要简单地显示地标。

这里我们复用部分在<创建和组合视图>教程中创建的Landmark视图创建一个熟悉但简单的地标预览图。


第一步

在CategoryRow.swift文件中,创建一个新的自定义视图CategoryItem,并将CategoryRow类型中的用来显示地标名称的Text视图替换为新创建的视图。

struct CategoryItem: View {
    var landmark: Landmark
    var body: some View {
        VStack(alignment: .leading) {
            landmark.image
                .resizable()
                .frame(width: 155, height: 155)
                .cornerRadius(5)
            Text(landmark.name)
        }
        .padding(.leading, 15)
    }
}


struct CategoryRow: View {
    var categoryName: String
    var items: [Landmark]
// 省略部分代码
                HStack(alignment: .top, spacing: 0) {
                    ForEach(self.items) { landmark in
                        CategoryItem(landmark: landmark)
                    }
                }

第二步

在Home.swift文件中,添加一个名为FeaturedLandmarks的简单视图只显示那些被标记为isFeatured重要的地标。

在后面的教程中,我们会将这个视图变成一个交互式的跑马灯。现在,它还只是显示重要地标其中一个的被缩放和剪切的预览图片。

struct FeaturedLandmarks: View {
    var landmarks: [Landmark]
    var body: some View {
        landmarks[0].image.resizable()
    }
}

struct CategoryHome: View {
// 省略部分代码
    
    var featured: [Landmark] {
        landmarkData.filter { $0.isFeatured }
    }
    
    var body: some View {
        NavigationView {
            List {
                FeaturedLandmarks(landmarks: featured)
                    .scaledToFill()
                    .frame(height: 200)
                    .clipped()
                
                ForEach(categories.keys.sorted(), id: \.self) {

第三步

将所有类型的地标预览图片的边距设置为0,让内容可以扩展到显示的边缘。

                    .frame(height: 200)
                    .clipped()
                    .listRowInsets(EdgeInsets())
                
                ForEach(categories.keys.sorted(), id: \.self) { key in
                    CategoryRow(categoryName: key, items: self.categories[key]!)
                .listRowInsets(EdgeInsets())

05 在各模块之间添加导航

现在所有不同分类的地标在主页视图都可见了,用户需要一种方式可以去到App的其它模块。使用导航和展现API让详情页、收藏列表以及用户档案都可以从主页导航过去。


第一步

在CategoryRow.swift文件中,用NavigationLink将CategoryItem包裹起来。

分类条目本身作为按钮的标签,它的目标是卡片显示的地标的详情视图。

                HStack(alignment: .top, spacing: 0) {
                    ForEach(self.items) { landmark in
                        NavigationLink(
                            destination: LandmarkDetail(
                                landmark: landmark
                            )
                        ) {
                            CategoryItem(landmark: landmark)
                        }
                    }
                }

第二步

使用renderingMode(_:)和foregroundColor(_:)修饰器改变分类条目的导航外观。

默认情况下,传入导航链接作为标签的文本会使用环境中的强调色,图片会渲染为模板图片,这些表现我们都可以修改以适配我们的设计。

        VStack(alignment: .leading) {
            landmark.image
                .renderingMode(.original)
                .resizable()
                .frame(width: 155, height: 155)
                .cornerRadius(5)
            Text(landmark.name)
                .foregroundColor(.primary)
                .font(.caption)

第三步

在Home.swift文件中,添加一个弹出页,在用户点击了标签栏的档案图标后用一个模态视图显示用户档案。

SwiftUI在showProfile状态变量设置为true的时候显示用户档案,并在showProfile设置为false的时候隐藏。

    }
    
    @State var showingProfile = false

    var body: some View {
        NavigationView {

第四步

在导航栏添加一个按钮,当按钮被点击的时候将showProfile的值从false转为true。

    @State var showProfile = false
    
    var profileButton: some View {
        Button(action: { self.showProfile.toggle()}) {
            Image(systemName: "person.crop.circle")
                .imageScale(.large)
                .accessibility(label: Text("User Profile"))
                .padding()
        }
    }
    
    var body: some View {
// 省略部分代码
            }
            .navigationBarTitle(Text("Featured"))
            .navigationBarItems(trailing: profileButton)
            .sheet(isPresented: $showProfile) {
                Text("User Profile")
            }
        }

第五步

最后添加一个导航链接到可筛选的全部地标列表页面完成主页面。

                }
                .listRowInsets(EdgeInsets())
                
                NavigationLink(destination: LandmarkList()) {
                    Text("See All")
                }
            }
            .navigationBarTitle(Text("Featured"))

第六步

在LandmarkList.swift文件中,移除包裹地标列表的NavigationView,并在预览中添加它。

在App上下文中,LandmarkList总是会在Home.swift中声明的导航视图中展示。

    var body: some View {

        List {
            Toggle(isOn: $userData.showFavoritesOnly) {

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表