SwiftUI极简教程35:构建一个AppStore应用市场推荐页面(上)

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

在本章中,你将学会使用Transition过渡Animation动画构建一个AppStore应用市场推荐页面。

项目背景

AppStore应用市场中,我们可以看到一个Apple常用的交互形式,它采用线性动画,当用户点击推荐的卡片视图时,卡片会缓缓弹出,然后进入到对应的详情页中。

这种动画交互很有意思。

那么本章,我们就尝试构建一个AppStore应用市场推荐页面。

首先,创建一个新项目,命名为SwiftUITransition

1.png

素材准备

我们先导入一批图片作为卡片视图展示的封面。

2.png

Model准备

然后,我们构建下Model。新建一个swift类型的文件,命名为Model.swift

我们分析下卡片视图所涵盖的内容:封面图片、类目、标题、副标题、内容,我们在Model中创建对应的参数,并且创建一些示例数据。

import SwiftUI

struct Model: Identifiable {
    var id = UUID()
    var category: String
    var headline: String
    var subHeadline: String
    var content: String
    var image: UIImage
}

#if DEBUG
    var sampleModels = [
        Model(category: "推荐", headline: "编辑精选", subHeadline: "本周推荐语录", content: "要想在一个生活圈中生活下去,或者融入职场的氛围,首先你要学习这个圈子的文化和发展史,并尝试用这个圈子里面的“话术”和他们交流,这样才能顺利地融入这个圈子。", image: UIImage(named: "image01")!),
        Model(category: "热榜", headline: "每日热榜", subHeadline: "今日热门事件推送", content: "对于产品经理来说,用户真正使用产品或产品上线交付后,产品的价值才真正发挥出来。这时候,价值的多少就取决于产品本身所提供的服务,以及产品的设计者后续的运营了。", image: UIImage(named: "image02")!),
        Model(category: "头条", headline: "精选头条", subHeadline: "你关注的才是头条", content: "在任何环境中都需要去发现他人身上的优点,但在学习他人优点的同时,也不要忘了自己本身的个人特性,因为这才是你的优点。遇到困难时,我们要迎难而上,而不是畏畏缩缩。找到一个明确的目标,不在乎别人的眼光,努力去做就好了。", image: UIImage(named: "image03")!),
    ]
#endif
复制代码

3.png

我们定义了一个Model结构体,它遵循Identifiable协议,这样我们就可以使用id定位到结构体中的实例。

我们使用var声明了卡片视图需要用到的变量,并声明了其类型属性:StringUIImage等。并创建了一个sampleModels数组,创建了一个示例数据作为View视图中展示的内容。

以上就是我们Model承载的内容,在MVVMMVC开发模式中,基本使用的都是这样的方式。

摘要视图

AppStore应用市场推荐页面,我们可以看到可以向下滚动一个个卡片视图。一张张卡片上展示着部分推荐信息,包括文章的图片、类别、标题和副标题。

我们可以创建一个新的结构体用来展示卡片视图。

//摘要视图

struct SummaryView: View {

    let category: String
    let headline: String
    let subHeadline: String
    @Binding var isShowContent: Bool

    var body: some View {
        VStack(alignment: .leading) {
            Spacer()
            
            Rectangle()
                .frame(minHeight: 100, maxHeight: 150)
                .overlay(
                    HStack {
                        VStack(alignment: .leading) {

                            Text(self.category.uppercased())
                                .font(.subheadline)
                                .fontWeight(.bold)
                                .foregroundColor(.secondary)

                            Text(self.headline)
                                .font(.title)
                                .fontWeight(.bold)
                                .foregroundColor(.primary)
                                .minimumScaleFactor(0.1)
                                .lineLimit(2)
                                .padding(.bottom, 5)
                                
                            if !self.isShowContent {
                                Text(self.subHeadline)
                                    .font(.subheadline)
                                    .foregroundColor(.secondary)
                                    .minimumScaleFactor(0.1)
                                    .lineLimit(3)
                            }
                        }.padding()
                        Spacer()
                    }
                )
        }
        .foregroundColor(.white)
    }
}
复制代码

上述代码中,我们构建了一个SummaryView结构体,用来构建卡片视图内容的摘要内容。

我们使用let声明了类别category、标题headline和副标题subHeadline三个常量及其类型,在结构体主体内容上,我们创建了一个Rectangle视图,并将和标题和副标题进行overlay叠加。

叠加部分,我们使用横向视图,包裹了三个Text文本,Text文本来源于我们声明的常量,为之后在主视图赋值使用。

这里科普一个知识点。

minimumScaleFactor修饰符,使用后,Text文本内容可以根据展示的空间大小自动缩小文本的字体大小。因为考虑到headline标题和副标题subHeadline可能会很长的关系,我们这里设定了可以缩小到原始大小的10%

完成后,我们在ContentView视图中展示下内容。

SummaryView(category: sampleModels[0].category, headline: sampleModels[0].headline, subHeadline: sampleModels[0].subHeadline, isShowContent: .constant(false))
复制代码

我们在ContentView视图实例化了一个SummaryView视图,数据来源我们使用下标的方式访问sampleModels数组中的数据,这里取的的是[0]第一条,对于是否展示内容isShowContent我们暂时使用false

我们预览下效果:

4.png

完整视图

完成了摘要视图后,我们来尝试完成完整的视图展示。

struct CardView: View {

    let category: String
    let headline: String
    let subHeadline: String
    let image: UIImage
    var content: String = ""
    @Binding var isShowContent: Bool

    var body: some View {
        ScrollView {
            VStack(alignment: .leading) {
                Image(uiImage: self.image)
                    .resizable()
                    .scaledToFill()
                    .frame(height: min(self.image.size.height / 3, 500)).border(Color(.sRGB, red: 150 / 255, green: 150 / 255, blue: 150 / 255, opacity: 0.1), width: self.isShowContent ? 0 : 1)
                    .cornerRadius(15)
                    .overlay(
                        SummaryView(category: self.category, headline: self
                            .headline, subHeadline: self.subHeadline, isShowContent: self.$isShowContent).cornerRadius(self.isShowContent ? 0 : 15)
                    )
                if self.isShowContent {
                    Text(self.content)
                }
            }
        }
        .foregroundColor(Color(.darkGray))
        .font(.system(.body, design: .rounded))
        .padding(.horizontal)
        .padding(.bottom, 50)
        .transition(.move(edge: .top))
        .animation(.linear, value: 0)
        .shadow(color: Color(.sRGB, red: 64 / 255, green: 64 / 255, blue: 64 / 255, opacity: 0.3), radius: self.isShowContent ? 0 : 15)
    }
}
复制代码

上述代码中,我们定义了一个CardView结构体,声明了需要用到的参数:category类目、headline主标题、subHeadline副标题、image主要展示图片、content详情内容、以及是否展示内容isShowContent

我们在ContentView视图实例化CardView视图,和之前摘要视图的方法一致,访问sampleModels数据数据。

CardView(category: sampleModels[0].category, headline: sampleModels[0].headline, subHeadline: sampleModels[0].subHeadline, image: sampleModels[0].image,content: sampleModels[0].content, isShowContent: .constant(false))
复制代码

我们预览下效果:

5.png

我们再把isShowContent是否展示详情改为true,再预览下效果。

6.png

恭喜你,完成了基础页面的绘制!

由于章节内容过多,我们将分章节进行学习。

快来动手试试吧!

如果本专栏对你有帮助,不妨点赞、评论、关注~

猜你喜欢

转载自juejin.im/post/7105414039240441886