# 細(xì)品Swift - 一目了然的圖表Swift Charts(2)

細(xì)品Swift - 一目了然的圖表Swift Charts(2)

Bar Chart 條形圖

Bar Chart的維基百科解釋如下:
條形圖(英語(yǔ):bar chart),或 條圖(英語(yǔ):bar graph)芋忿,臺(tái)灣常稱(chēng)為 長(zhǎng)條圖,又稱(chēng)為 柱狀圖、棒形圖,是一種以長(zhǎng)方形的長(zhǎng)度為變量的統(tǒng)計(jì)圖表活孩。長(zhǎng)條圖用來(lái)比較兩個(gè)或以上的價(jià)值(不同時(shí)間或者不同條件),只有一個(gè)變量乖仇,通常利用于較小的數(shù)據(jù)集分析。長(zhǎng)條圖亦可橫向排列询兴,或用多維方式表達(dá)乃沙。繪制長(zhǎng)條圖時(shí),長(zhǎng)條柱或柱組中線(xiàn)須對(duì)齊項(xiàng)目刻度诗舰。相較之下警儒,折線(xiàn)圖則是將數(shù)據(jù)代表之點(diǎn)對(duì)齊項(xiàng)目刻度。在數(shù)字大且接近時(shí)眶根,兩者皆可使用波浪形省略符號(hào)蜀铲,以擴(kuò)大表現(xiàn)數(shù)據(jù)間的差距,增強(qiáng)理解和清晰度属百。

長(zhǎng)條圖通常適用于較小的數(shù)據(jù)集分析记劝,其每個(gè)數(shù)據(jù)標(biāo)記通常表示一個(gè)分類(lèi)的數(shù)量或者幾個(gè)分類(lèi)的數(shù)量總和。

基本BarChart

在一個(gè)圖表中可以對(duì)同一數(shù)據(jù)使用不同的標(biāo)記來(lái)描繪族扰,如BarMark是條狀圖樣式的標(biāo)記厌丑,PointMark是點(diǎn)狀樣式的標(biāo)記定欧,LineMark是折線(xiàn)樣式的標(biāo)記。

BarChart可以從不同的維度來(lái)對(duì)數(shù)據(jù)進(jìn)行可視化怒竿,比如本圖表按照食物的名字或者食物的種類(lèi)砍鸠,分類(lèi)來(lái)統(tǒng)計(jì)對(duì)應(yīng)的食物總重量。

BarChart會(huì)將x值相同的數(shù)據(jù)堆疊在一個(gè)條形圖上耕驰,通過(guò)修飾語(yǔ)句.foregroundStyle可以把同一條形上的不同數(shù)據(jù)按照指定的value分開(kāi)爷辱。 這里指定的value會(huì)被當(dāng)作圖例使用。同樣朦肘,可以指定圖例的顏色饭弓,這里是通過(guò)對(duì)Chart的修飾語(yǔ)句.chartForegroundStyleScale來(lái)指定圖例對(duì)應(yīng)的顏色。

    var basicBarChart: some View {
        VStack {
            VStack {
                Divider()
                Text("用$0直接表示單個(gè)數(shù)據(jù)").font(.footnote)
                // Chart使用類(lèi)似于List的方式厚骗,從foods中直接拆解處單個(gè)數(shù)據(jù)并繪制
                Chart(foods) {
                    BarMark(x: .value("name", $0.name), y: .value("weight", $0.weight))
                }
            }

            VStack {
                Divider()
                Text("數(shù)據(jù)標(biāo)記的選擇決定了圖表的類(lèi)別").font(.footnote)
                // 數(shù)據(jù)標(biāo)記(mark)可以選擇多種表示方式(點(diǎn)PointMark示启、條形BarMark、折線(xiàn)LineMark领舰、矩形RectangleMark等)
                // 本圖表對(duì)每個(gè)數(shù)據(jù)選擇了兩種標(biāo)記方式: BarMark和LineMark
                Chart(foods) { food in
                    BarMark(x: .value("name", food.name), y: .value("weight", food.weight))
                    LineMark(x: .value("name", food.name), y: .value("weight", food.weight))
                        .foregroundStyle(.red)

                }
            }

            VStack {
                Divider()
                Text("從不同維度對(duì)數(shù)據(jù)進(jìn)行觀察的BarChart").font(.footnote)
                // 可以選擇不同的數(shù)據(jù)屬性作為X軸夫嗓,以從不同的維度對(duì)數(shù)據(jù)進(jìn)行觀察
                // 如多個(gè)數(shù)據(jù)具有相同的x軸維度值,Chart會(huì)把他們直接合并冲秽,顯示所有對(duì)應(yīng)的y軸值之和舍咖。
                // 本例中把所有的數(shù)據(jù)按照type維度,分成4個(gè)條狀圖锉桑。
                Chart(foods) { food in
                    BarMark(x: .value("type", food.type) ,
                            y: .value("weight", food.weight))
                }
            }


            VStack {
                Divider()
                Text("合并后在每個(gè)Bar中顯示原始數(shù)據(jù)所占部分").font(.footnote)
                // 使用修飾指令.foregroundStyle(by: PlottableValue)
                // 可以看出排霉,圖表中多了legend(圖例)部分,指定每種顏色對(duì)應(yīng)的數(shù)據(jù)民轴,但可以看出
                Chart(foods) { food in
                    BarMark(x: .value("type", food.type) ,
                            y: .value("weight", food.weight))
                    // 在每個(gè)Bar上為每個(gè)color屬性的值賦不同的顏色
                    // 同一bar上同顏色的數(shù)據(jù)再次合并, 如Avocado和GreenGrape的食物類(lèi)型相同攻柠,所有都在Fruit Bar上,進(jìn)一步后裸,他們的顏色都是Green瑰钮,所以以同樣的顏色顯示在Bar上。
                    .foregroundStyle(by: .value("color", food.color))
                }
            }

            VStack {
                Divider()
                Text("指定顯示每種圖例對(duì)應(yīng)的顏色").font(.footnote)
                // 用chartForgroundStyleScale來(lái)指定每種圖例對(duì)應(yīng)的顏色

                Chart(foods) { food in
                    BarMark(x: .value("type", food.type) ,
                            y: .value("weight", food.weight))
                    // 在每個(gè)Bar上為每個(gè)color屬性的值賦不同的顏色
                    // 同一bar上同顏色的數(shù)據(jù)再次合并, 如Avocado和GreenGrape的食物類(lèi)型相同微驶,所有都在Fruit Bar上浪谴,進(jìn)一步,他們的顏色都是Green因苹,所以以同樣的顏色顯示在Bar上苟耻。
                    .foregroundStyle(by: .value("color", food.color.description))
                }
                .chartForegroundStyleScale([
                    "yellow-color": .yellow,
                    "green-color": .green,
                    "red-color": .red,
                    "white-color": .gray.opacity(0.5), // 用半透明的gray來(lái)標(biāo)識(shí)白色
                    "pink-color": .pink,
                    "gray-color": .gray
                ])
            }


            VStack {
                Divider()
                Text("從顏色維度來(lái)分類(lèi)并顯示每條Bar中食物種類(lèi)分布").font(.footnote)

                Chart(foods) { food in
                    BarMark(x: .value("color", food.color) ,
                            y: .value("weight", food.weight))
                    // 在每個(gè)Bar上為每個(gè)color屬性的值賦不同的顏色
                    // 同一bar上同顏色的數(shù)據(jù)再次合并, 如Avocado和GreenGrape的食物類(lèi)型相同,所有都在Fruit Bar上扶檐,進(jìn)一步凶杖,他們的顏色都是Green,所以以同樣的顏色顯示在Bar上款筑。
                    .foregroundStyle(by: .value("type", food.type))
                }
                // 按照食物種類(lèi)指定顏色
                .chartForegroundStyleScale([
                    "Meat": .pink,
                    "Vegetable": .green,
                    "Grain": .brown,
                    "Fruit": .orange
                ])
            }
            
            
        }
    }
image.png

一維BarChart和區(qū)間BarChart

BarChart可以用于展示一維數(shù)據(jù)圖以及區(qū)間圖官卡。

如果需要在一維上展示數(shù)據(jù)蝗茁,只需要輸入x或y中的一個(gè)數(shù)據(jù),以及圖例基于的數(shù)據(jù)寻咒。

區(qū)間圖需要提供在一個(gè)維度上的數(shù)據(jù)的上下區(qū)間值哮翘,已經(jīng)另外一個(gè)維度的數(shù)據(jù)屬性。

    var advancedBarChart: some View {
        VStack {
            VStack {
                Divider()
                Text("一維條形圖").font(.footnote)
                // 按照種類(lèi)排序后毛秘,同種類(lèi)的食物連續(xù)顯示
                Chart(foods.sorted(by: {$0.type < $1.type})) {
                    BarMark(x: .value("weight", $0.weight))
                        .foregroundStyle(by: .value("type", $0.type))
                }
                
            }
            
            VStack {
                Divider()
                Text("區(qū)間條形圖").font(.footnote)
                // 用條狀圖來(lái)標(biāo)識(shí)一個(gè)區(qū)間饭寺,特別是時(shí)間區(qū)間,如甘特圖叫挟。
                Chart(foods) { food in
                    
                    BarMark(x: .value("name", food.name),
                            yStart: .value("minGi", food.minGiValue),
                            yEnd: .value("maxGi", food.maxGiValue)
                    )
                }
                
                
            }
            
            
            VStack {
                Divider()
                Text("橫向區(qū)間條形圖").font(.footnote)
                Chart(foods) { food in
                    
                    BarMark(xStart: .value("minGi", food.minGiValue), xEnd: .value("maxGi", food.maxGiValue),
                            y: .value("color", food.color))
                    .foregroundStyle(by: .value("type", food.type))
                }
            }
        }
    }

image.png

完整演示代碼


完整的演示代碼如下:


struct Food: Identifiable {
    let id = UUID()
    let name: String
    let maxGiValue: Int
    let minGiValue: Int
    let maxLength: Double
    let minLength: Double
    let color: String
    let type: String
    let weight: Double
}

let foods: [Food] = [
    Food(name: "Cabbage", maxGiValue: 13, minGiValue: 8, maxLength: 23, minLength: 10, color: "green-color", type: "Vegetable", weight: 2),
    Food(name: "Beef", maxGiValue: 54, minGiValue: 18, maxLength: 53, minLength: 20, color: "pink-color", type: "Meat", weight: 1.8),
    Food(name: "Rice", maxGiValue: 93, minGiValue: 78, maxLength: 5, minLength: 2, color: "gray-color", type: "Grain", weight: 8),
    Food(name: "Tomato", maxGiValue: 33, minGiValue: 21, maxLength: 13, minLength: 5, color: "red-color", type: "Vegetable", weight: 5),
    Food(name: "Pork", maxGiValue: 63, minGiValue: 38, maxLength: 63, minLength: 10, color: "pink-color", type: "Meat", weight: 5.3),
    Food(name: "Fish", maxGiValue: 49, minGiValue: 28, maxLength: 53, minLength: 5, color: "white-color", type: "Meat", weight: 1.5),
    Food(name: "Flour", maxGiValue: 83, minGiValue: 3, maxLength: 0.1, minLength: 10, color: "white-color", type: "Grain", weight: 6.5),
    Food(name: "Banana", maxGiValue: 93, minGiValue: 78, maxLength: 33, minLength: 10, color: "yellow-color", type: "Fruit", weight: 3.8),
    Food(name: "Avocado", maxGiValue: 33, minGiValue: 18, maxLength: 25, minLength: 8, color: "green-color", type: "Fruit", weight: 8),
    Food(name: "GreenGrape", maxGiValue: 99, minGiValue: 68, maxLength: 23, minLength: 10, color: "green-color", type: "Fruit", weight: 8)
    
]


struct AllAboutBarChartTest: View {
    var body: some View {
       basicBarChart
       advancedBarChart
    }
    
    
    var basicBarChart: some View {
        VStack {
            VStack {
                Divider()
                Text("用$0直接表示單個(gè)數(shù)據(jù)").font(.footnote)
                // Chart使用類(lèi)似于List的方式艰匙,從foods中直接拆解處單個(gè)數(shù)據(jù)并繪制
                Chart(foods) {
                    BarMark(x: .value("name", $0.name), y: .value("weight", $0.weight))
                }
            }

            VStack {
                Divider()
                Text("數(shù)據(jù)標(biāo)記的選擇決定了圖表的類(lèi)別").font(.footnote)
                // 數(shù)據(jù)標(biāo)記(mark)可以選擇多種表示方式(點(diǎn)PointMark、條形BarMark抹恳、折線(xiàn)LineMark员凝、矩形RectangleMark等)
                // 本圖表對(duì)每個(gè)數(shù)據(jù)選擇了兩種標(biāo)記方式: BarMark和LineMark
                Chart(foods) { food in
                    BarMark(x: .value("name", food.name), y: .value("weight", food.weight))
                    LineMark(x: .value("name", food.name), y: .value("weight", food.weight))
                        .foregroundStyle(.red)

                }
            }

            VStack {
                Divider()
                Text("從不同維度對(duì)數(shù)據(jù)進(jìn)行觀察的BarChart").font(.footnote)
                // 可以選擇不同的數(shù)據(jù)屬性作為X軸,以從不同的維度對(duì)數(shù)據(jù)進(jìn)行觀察
                // 如多個(gè)數(shù)據(jù)具有相同的x軸維度值奋献,Chart會(huì)把他們直接合并健霹,顯示所有對(duì)應(yīng)的y軸值之和。
                // 本例中把所有的數(shù)據(jù)按照type維度瓶蚂,分成4個(gè)條狀圖糖埋。
                Chart(foods) { food in
                    BarMark(x: .value("type", food.type) ,
                            y: .value("weight", food.weight))
                }
            }


            VStack {
                Divider()
                Text("合并后在每個(gè)Bar中顯示原始數(shù)據(jù)所占部分").font(.footnote)
                // 使用修飾指令.foregroundStyle(by: PlottableValue)
                // 可以看出,圖表中多了legend(圖例)部分窃这,指定每種顏色對(duì)應(yīng)的數(shù)據(jù)瞳别,但可以看出
                Chart(foods) { food in
                    BarMark(x: .value("type", food.type) ,
                            y: .value("weight", food.weight))
                    // 在每個(gè)Bar上為每個(gè)color屬性的值賦不同的顏色
                    // 同一bar上同顏色的數(shù)據(jù)再次合并, 如Avocado和GreenGrape的食物類(lèi)型相同,所有都在Fruit Bar上杭攻,進(jìn)一步祟敛,他們的顏色都是Green,所以以同樣的顏色顯示在Bar上兆解。
                    .foregroundStyle(by: .value("color", food.color))
                }
            }

            VStack {
                Divider()
                Text("指定顯示每種圖例對(duì)應(yīng)的顏色").font(.footnote)
                // 用chartForgroundStyleScale來(lái)指定每種圖例對(duì)應(yīng)的顏色

                Chart(foods) { food in
                    BarMark(x: .value("type", food.type) ,
                            y: .value("weight", food.weight))
                    // 在每個(gè)Bar上為每個(gè)color屬性的值賦不同的顏色
                    // 同一bar上同顏色的數(shù)據(jù)再次合并, 如Avocado和GreenGrape的食物類(lèi)型相同垒棋,所有都在Fruit Bar上,進(jìn)一步痪宰,他們的顏色都是Green,所以以同樣的顏色顯示在Bar上畔裕。
                    .foregroundStyle(by: .value("color", food.color.description))
                }
                .chartForegroundStyleScale([
                    "yellow-color": .yellow,
                    "green-color": .green,
                    "red-color": .red,
                    "white-color": .gray.opacity(0.5), // 用半透明的gray來(lái)標(biāo)識(shí)白色
                    "pink-color": .pink,
                    "gray-color": .gray
                ])
            }


            VStack {
                Divider()
                Text("從顏色維度來(lái)分類(lèi)并顯示每條Bar中食物種類(lèi)分布").font(.footnote)

                Chart(foods) { food in
                    BarMark(x: .value("color", food.color) ,
                            y: .value("weight", food.weight))
                    // 在每個(gè)Bar上為每個(gè)color屬性的值賦不同的顏色
                    // 同一bar上同顏色的數(shù)據(jù)再次合并, 如Avocado和GreenGrape的食物類(lèi)型相同衣撬,所有都在Fruit Bar上,進(jìn)一步扮饶,他們的顏色都是Green具练,所以以同樣的顏色顯示在Bar上。
                    .foregroundStyle(by: .value("type", food.type))
                }
                // 按照食物種類(lèi)指定顏色
                .chartForegroundStyleScale([
                    "Meat": .pink,
                    "Vegetable": .green,
                    "Grain": .brown,
                    "Fruit": .orange
                ])
            }
            
            
        }
    }
   
    var advancedBarChart: some View {
        VStack {
            VStack {
                Divider()
                Text("一維條形圖").font(.footnote)
                // 按照種類(lèi)排序后甜无,同種類(lèi)的食物連續(xù)顯示
                Chart(foods.sorted(by: {$0.type < $1.type})) {
                    BarMark(x: .value("weight", $0.weight))
                        .foregroundStyle(by: .value("type", $0.type))
                }
                
            }
            
            VStack {
                Divider()
                Text("區(qū)間條形圖").font(.footnote)
                // 用條狀圖來(lái)標(biāo)識(shí)一個(gè)區(qū)間扛点,特別是時(shí)間區(qū)間哥遮,如甘特圖。
                Chart(foods) { food in
                    
                    BarMark(x: .value("name", food.name),
                            yStart: .value("minGi", food.minGiValue),
                            yEnd: .value("maxGi", food.maxGiValue)
                    )
                }
                
                
            }
            
            
            VStack {
                Divider()
                Text("橫向區(qū)間條形圖").font(.footnote)
                Chart(foods) { food in
                    
                    BarMark(xStart: .value("minGi", food.minGiValue), xEnd: .value("maxGi", food.maxGiValue),
                            y: .value("color", food.color))
                    .foregroundStyle(by: .value("type", food.type))
                }
            }
        }
    }
}

總結(jié)如下:

  • BarChart中文名稱(chēng)為條狀圖陵究,柱狀圖等眠饮,是圖形化少量數(shù)據(jù)的有效方法。
  • BarChart可以在多種維度上展示數(shù)據(jù)铜邮。
  • BarChart可以用圖例更詳細(xì)的演示堆疊在一個(gè)Bar上的多種數(shù)據(jù)仪召。
  • BarChart可以以一維的方式展示數(shù)據(jù)。
  • BarChart可以展示數(shù)據(jù)區(qū)間松蒜。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子先改,更是在濱河造成了極大的恐慌恢暖,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惊楼,死亡現(xiàn)場(chǎng)離奇詭異玖瘸,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)胁后,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)店读,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人攀芯,你說(shuō)我怎么就攤上這事屯断。” “怎么了侣诺?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵殖演,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我年鸳,道長(zhǎng)趴久,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任搔确,我火速辦了婚禮彼棍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘膳算。我一直安慰自己座硕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布涕蜂。 她就那樣靜靜地躺著华匾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪机隙。 梳的紋絲不亂的頭發(fā)上蜘拉,一...
    開(kāi)封第一講書(shū)人閱讀 51,155評(píng)論 1 299
  • 那天萨西,我揣著相機(jī)與錄音,去河邊找鬼旭旭。 笑死谎脯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的您机。 我是一名探鬼主播穿肄,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼际看!你這毒婦竟也來(lái)了咸产?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤仲闽,失蹤者是張志新(化名)和其女友劉穎脑溢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體赖欣,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡屑彻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了顶吮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片社牲。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖悴了,靈堂內(nèi)的尸體忽然破棺而出搏恤,到底是詐尸還是另有隱情,我是刑警寧澤湃交,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布熟空,位于F島的核電站,受9級(jí)特大地震影響搞莺,放射性物質(zhì)發(fā)生泄漏息罗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一才沧、第九天 我趴在偏房一處隱蔽的房頂上張望迈喉。 院中可真熱鬧,春花似錦温圆、人聲如沸挨摸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至嫉戚,卻和暖如春刨裆,著一層夾襖步出監(jiān)牢的瞬間澈圈,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工帆啃, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瞬女,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓努潘,卻偏偏與公主長(zhǎng)得像诽偷,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子疯坤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容