SwiftUI极简教程06:代码优雅复用

4,360 阅读9分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

前言:

今年很想做的一件事情,也是很重要的一个目标,是打造自己的知识体系。很多时候,我们会发现自己会的东西很多,但很杂,明明知道,但很难让别人知道。不难发现,很多优秀的开发者,他们都能把自己会的技能形成一套系统知识库,这点着实让人敬佩,也值得学习。

今日职言:求知若饥,谦卑若愚。

在本章中,你将学会如何处理可以复用的代码块。

在定价方案视图中,可以看到3个定价方案的样式还有代码是极其相似的。

74.png

和设计的逻辑一样,代码的逻辑也是是“复制”第一个定价方案的视图的代码,然后再修改颜色、字体、或者加多一些文字。

但他们的主体结构是一样的。

那么我们是不是可以只需要写一次代码,然后把文字什么的再复用重定义。

这样可以大大减少代码量,且代码阅读起来更加清晰,做到“优雅写代码”的目的。

//定价方案子视图

struct pricingView: View {
    var body: some View {
        HStack {
        
            // 连续包月
            ZStack {
                VStack {
                    Text("连续包月")
                        .fontWeight(.bold)
                        .font(.system(size: 17))
                        .foregroundColor(Color(red: 190 / 255, green: 188 / 255, blue: 184 / 255))
                    Text("¥18")
                        .fontWeight(.bold)
                        .font(.system(size: 30))
                        .foregroundColor(Color(red: 239 / 255, green: 129 / 255, blue: 112 / 255))
                }
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 90)
                .padding(20)
                .background(Color("faf7f3"))
                .overlay(RoundedRectangle(cornerRadius: 6)
                            .stroke(Color(red: 202 / 255, green: 169 / 255, blue: 106 / 255), lineWidth: 2))

                // 首月特惠
                Text("首月特惠")
                    .font(.system(size: 14))
                    .fontWeight(.bold)
                    .foregroundColor(.white)
                    .padding(5)
                    .background(Color(red: 202 / 255, green: 169 / 255, blue: 106 / 255))
                    .cornerRadius(4)
                    .offset(x: 0, y: -65)
            }

            // 1个月
            VStack {
                Text("1个月")
                    .fontWeight(.bold)
                    .font(.system(size: 17))
                    .foregroundColor(Color(red: 190 / 255, green: 188 / 255, blue: 184 / 255))
                Text("¥30")
                    .fontWeight(.bold)
                    .font(.system(size: 30))
                    .foregroundColor(Color(red: 239 / 255, green: 129 / 255, blue: 112 / 255))
            }
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 90)
            .padding(20)
            .background(Color(red: 244 / 255, green: 244 / 255, blue: 245 / 255))
            .cornerRadius(10)
 
            // 12个月
            VStack {
                Text("12个月")
                    .fontWeight(.bold)
                    .font(.system(size: 17))
                    .foregroundColor(Color(red: 190 / 255, green: 188 / 255, blue: 184 / 255)
                Text("¥228")
                    .fontWeight(.bold)
                    .font(.system(size: 30))
                    .foregroundColor(Color(red: 239 / 255, green: 129 / 255, blue: 112 / 255))
                Text("¥19.00/月")
                    .fontWeight(.bold)
                    .font(.system(size: 17))
                    .foregroundColor(Color(red: 190 / 255, green: 188 / 255, blue: 184 / 255))
            }
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 90)
            .padding(20)
            .background(Color(red: 244 / 255, green: 244 / 255, blue: 245 / 255))
            .cornerRadius(10)
        } // HStack结束位置
        .padding(.horizontal)
    }
}

说干就干!

首先分析下代码的相同点。

Text()下,设置字重、字体、字体颜色,在整个VStack包裹下,设置尺寸、边距、背景颜色、边框圆角。

我们发现,除了定价方案的标题不同(连续包月、1个月、12个月)、不同定价方案的价格不同(¥18、¥30、¥228)、背景颜色不同,其他都一样。

既然这些都可以复用,那么我们可以将可以复用的参数脱离出来。

先定义一个变量,再在代码中设定不同的变量值,就可以达到全部复用的效果。

先定一个标准的定价方案,定义了一个title变量,它是字符串类型,定义了一个价格变量,也是字符串类型,定一个一个背景颜色,它是颜色类型。

然后将定义的变量换到下面的代码中。

//定价方案

struct pricingView: View {

//定义变量
var title: String
var price: String
var bgColor: Color

var body: some View {

    VStack {
        Text(title)    //定义的titie
            .fontWeight(.bold)
            .font(.system(size: 17))
            .foregroundColor(Color(red: 190 / 255, green: 188 / 255, blue: 184 / 255))
        Text(price)    //定义的price
            .fontWeight(.bold)
            .font(.system(size: 30))
            .foregroundColor(Color(red: 239 / 255, green: 129 / 255, blue: 112 / 255))
        }
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 90)
        .padding(20)
        .background(bgColor)    //定义的背景颜色

    }

定义完变量后,我们可以看到系统报错了,不用担心。

这因为我们pricingView子视图中定义了3个变量,但在主视图引用的时候,主视图不知道这3个变量是什么值,只知道它的类型。

所以系统提醒我们需要加上具体的值。

我们可以点击“Fix”,Xcode会自动帮我们加上缺少的值。

75.png

“Fix”后,Xcode将缺少的变量补充了进来。

这时候,我们需要给变量赋予真实的值。

示例:title赋予"连续包月”,price赋予"¥18”,bgColor赋予Color("faf7f3”)。

76.png

// 定价方案

pricingView(title: "连续包月", price: "¥18", bgColor: Color("faf7f3"))

77.png

很好,我们已经创建好了1个定价方案了!

像UI稿一样,我们需要3个定价方案,由于我们已经拆离开可以复用的代码了,那么我们只需要在主视图复制就行了,而无需再额外复制一大段代码。

 // 定价方案

    //连续包月
    pricingView(title: "连续包月", price: "¥18", bgColor: Color("faf7f3"))

    //1个月
    pricingView(title: "1个月", price: "¥30", bgColor: Color(red: 244 / 255, green: 244 / 255, blue: 245 / 255))
         
    //12个月
    pricingView(title: "12个月", price: "¥228", bgColor: Color(red: 244 / 255, green: 244 / 255, blue: 245 / 255))

78.png

由于在主视图中,我们的标题和定价方案是放在一个VStack里面的,而3个定价方案需要横向分布,也就是使用HStack。

那我们只需要在主视图中将3个定价方案放在一个HStack中,这样它们就能横向排列了。

// 定价方案

HStack {
    //连续包月
    pricingView(title: "连续包月", price: "¥18", bgColor: Color("faf7f3"))

    //1个月
    pricingView(title: "1个月", price: "¥30", bgColor: Color(red: 244 / 255, green: 244 / 255, blue: 245 / 255))

    //12个月
    pricingView(title: "12个月", price: "¥228", bgColor: Color(red: 244 / 255, green: 244 / 255, blue: 245 / 255))
}

79.png

下一步,我们修订下样式,我们可以看到第一个定价方案是有圆角边框的,其他两个定价方案是有圆角的。

那么我们需要单独为第一个定价方案加圆角边框,其他两个定价方案加圆角。

38.png

// 定价方案

HStack {

    //连续包月
    pricingView(title: "连续包月", price: "¥18", bgColor: Color("faf7f3”))
        .overlay(RoundedRectangle(cornerRadius: 6).stroke(Color(red: 202 / 255, green: 169 / 255, blue: 106 / 255), lineWidth: 2))

    //1个月
    pricingView(title: "1个月", price: "¥30", bgColor: Color(red: 244 / 255, green: 244 / 255, blue: 245 / 255))
        .cornerRadius(10)

    //12个月
    pricingView(title: "12个月", price: "¥228", bgColor: Color(red: 244 / 255, green: 244 / 255, blue: 245 / 255))
        .cornerRadius(10)
}

80.png

下一步,我们看到UI稿中,第一个定价有【首月特惠】的标识,其他两个定价方案是没有的。

那么我们需要再给第一个定价方案用ZStack包裹,然后再里面添加一个【首月特惠】的标识。

38.png

//连续包月

ZStack {

    // 连续包月
    pricingView(title: "连续包月", price: "¥18", bgColor: Color("faf7f3"))
        .overlay(RoundedRectangle(cornerRadius: 6).stroke(Color(red: 202 / 255, green: 169 / 255, blue: 106 / 255), lineWidth: 2))

    // 首月特惠
    Text("首月特惠")
        .font(.system(size: 14))
        .fontWeight(.bold)
        .foregroundColor(.white)
        .padding(5)
        .background(Color(red: 202 / 255, green: 169 / 255, blue: 106 / 255))
        .cornerRadius(4)
        .offset(x: 0, y: -65)
    }

81.png

这时候,我们会发现样式基本okay了。

但有个问题,主视图的代码好像太多了,不够优雅。

结合上一章所学的代码分组管理,我们可以把【首月特惠】单独抽离出来成为一个子视图。

鼠标移动到ZStack定价方案里的Text()视图,按住command键,点击选择Text()视图,选择Extract SubView。

82.png

83.png

在得到新的子视图后,我们依旧需要重命名。

我们把首月特惠的ExtractedView更名为specialOfferView。

鼠标定位到原来没有命名的ExtractedView位置,点击鼠标右键,选择Refactor,选择Rename。

84.png

85.png

很好!代码优雅多了!

86.png

然后,我们再给HStack加个.padding。

87.png

你以为完成了吗?并不!

我们看到第三个定价方案还有1个Text(“¥19.00/月”),而其他定价方案没有。

38.png

先思考下怎么做。

在pricingView里加一个Text()?先试试吧。

和之前一样,我们定一个perPrice变量,它是String类型。

然后在pricingView中加上Text(perPrice)的代码。

//定价方案子视图

struct pricingView: View {

    // 定义变量
    var title: String
    var price: String
    var perPrice: String
    var bgColor: Color
    var body: some View {

        VStack {
            Text(title)
                .fontWeight(.bold)
                .font(.system(size: 17))
                .foregroundColor(Color(red: 190 / 255, green: 188 / 255, blue: 184 / 255))
            Text(price)
                .fontWeight(.bold)
                .font(.system(size: 30))
                .foregroundColor(Color(red: 239 / 255, green: 129 / 255, blue: 112 / 255))
            Text(perPrice)
                .fontWeight(.bold)
                .font(.system(size: 17))
                .foregroundColor(Color(red: 190 / 255, green: 188 / 255, blue: 184 / 255))
        }
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 90)
        .padding(20)
        .background(bgColor)
    }
}

88.png

再回到主视图中,补充缺失的变量。

点击“Fix”,Xcode会自动补充缺失的变量。

89.png

补充缺失的变量后,根据定义好的数据类型,完成相对应的数据即可。

90.png

这样,我们就完成了整个定价方案的编程!

// 定价方案

HStack {

    // 连续包月
    ZStack {
        pricingView(title: "连续包月", price: "¥18", perPrice: "", bgColor: Color("faf7f3")
            .overlay(RoundedRectangle(cornerRadius: 6).stroke(Color(red: 202 / 255, green: 169 / 255, blue: 106 / 255), lineWidth: 2))

        // 首月特惠
        specialOfferView()
    }

    // 1个月
    pricingView(title: "1个月", price: "¥30", perPrice: "", bgColor: Color(red: 244 / 255, green: 244 / 255, blue: 245 / 255))
        .cornerRadius(10)

    // 12个月
    pricingView(title: "12个月", price: "¥228", perPrice: "¥19.00/月", bgColor: Color(red: 244 / 255, green: 244 / 255, blue: 245 / 255))
        .cornerRadius(10)
    }
    .padding()

91.png