如何在 Swift 语言下使用 iOS Charts API 制作漂亮图表?

【编者按】本文作者 Joyce Echessa 是渥合数位服务创办人,毕业于台湾大学,近年来专注于协助客户进行 App 软体以及网站开发。文中作者通过示例介绍用 ios-charts 库创建简易美观的图表,有助于开发者在应用中生动形象地向用户展示数据。本文系 OneAPM 工程师编译整理:

呈现大量数据时,比起表格中一行行枯燥的数据,使用图表来形象地表示数据可以帮助用户更好地理解。在图表中,不需要通读所有数据资料,便能很容易地了解数据模式,从而获取关键信息。图表的使用在商业 App 和健身 App 中非常常见。

本篇文章中,我们主要介绍由 Daniel Cohen Gindi 开发的 ios-charts library。ios-charts 是由 Philipp Jahoda 建立的,是非常流行的 Android 库 MPAndroidChartiOS 端口。有了这个库,你可以方便快捷地在应用中添加不同类型的图表。仅需寥寥几行代码,就可以制作出功能齐备、交互性强的图表,并且高度可定制。

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

函数库的主要特征:

  • 8种不同的图表类型;
  • 两个轴的缩放(用触控手势、分轴缩放或捏拉缩放);
  • 拖拽/平移(用触控手势);
  • 图表结合(线形图、柱形图、离散图、K 线图、气泡图);
  • 双(分开的)Y 轴;
  • 手指画图(用触控手势将数值画入图表);
  • 数值突出显示(自定义弹出视图);
  • 多个/分离的轴;
  • 储存图表到相册/以 PNG/JPEG 格式输出;
  • 预定义颜色模板;
  • 图例(可自动生成,可自定义);
  • 自定义轴(包括 X 和Y 轴);
  • 动画(在 X 轴和 Y 轴上建立动画);
  • 界限(提供额外信息,比如最大值等);
  • 全方位自定义(上色、字体、图例、颜色、背景、手势、虚线等)。

开始吧!

首先、下载本篇文章将会用到的初始示例——名为 iOSChartsDemo 的简易应用。应用运行时,你会看到有两个项目的表格:条形图和其他图表。当点击项目时,会得到空白的视图。在本例中,笔者已创建了要用的两个视图控制器:BarChartViewController 和 ChartsViewController。

接着,我们添加函数库到项目中。你可以用 CocoaPods 安装该库,这里我们直接手动安装。

下载 ios-charts 项目,这个 zip 文件包含了函数库(名为 Charts 的文件夹)和一个示例项目(名为 ChartsDemo)。如果你想了解更多关于函数库的知识,示例项目是很棒的资源。

解压缩已下载的文件,并将 Charts 文件复制粘贴到你项目(iOSChartsDemo)的根目录下。在 Finder 中打开Charts文件,并将 Charts.xcodeproj 拖拽到 Xcode 项目中。结构如下图所示。

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

接着从项目导航中选择你的项目,并确保该 iOSChartsDemo 目标被选中。在右边的常规选项卡中找到 Embedded Binaries 部分,点击该部分的+号添加图表框架。从列表中选择 Charts.framework 并点击 Add。

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

如果你想在 Objective-C 中使用函数库,请参考使用说明。

用 Command-B 或选择 Product > Build 来生成项目。如果不这样做,当你导入 Charts 框架到你的项目时,Xcode 会报错——无法加载 Charts 底层模块。

现在开始创建第一个图表。

创建一个柱形图

打开 BarChartViewController.swift 文件,添加以下声明。

import Charts

打开故事板文件。我们需要添加用来显示图表的视图。从文档纲要中选择 Bar Chart View Controller,并在属性检查器中取消 Extend Edges 的 Under Top Barsin 选项。我们不希望图表自动延伸至导航栏下方。

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

接着拖拽一个视图到 Bar Chart View Controller 中,并按下图定位边界。该视图为控制器中主视图的子视图。

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

视图被选中后,在识别检查器中将它的类设为 BarChartView。再使用助理编辑器,加入视图的 outlet 到 BarChartViewController 类,并命名为 outletbarChartView。在 BarChartViewController 类中添加下列代码。

@IBOutlet weak var barChartView: BarChartView!

运行项目,并从表格中选择柱形图,你可能会得到视图提示信息:无可用的图表数据。

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

如果在没有数据来产生图表时,你想在空白状态显示其他的信息,这时候可以自定义这个提示信息。在 viewDidLoad()函数底部,加入下列代码。

运行该项目,可以看到自定义的提示信息。

你可以为下面信息进一步添加描述。这可以用来向用户解释,为什么图表是空的,他们需要获取数据来生成图表。例如:健身 App 应该让用户知道在整理图表分析之前,他们需要先记录跑步数据。

barChartView.noDataTextDescription = "GIVE REASON"

添加下面的属性到该类。我们用它来存储一些图表的模拟数据。

var months: [String]!

将下面的函数添加到该类,用以建立图表。

func setChart(dataPoints: [String], values: [Double]) {
    barChartView.noDataText = "You need to provide data for the chart."
        
}

请注意,我曾经在 viewDidLoad()函数中添加了声明。现在从 viewDidLoad()中移除该声明。我们将用 setChart()来自定义图表。

在 viewDidLoad()中,添加以下内容到函数底部。

months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
let unitsSold = [20.0, 4.0, 6.0, 3.0, 12.0, 16.0, 4.0, 18.0, 2.0, 4.0, 5.0, 4.0]
        
setChart(months, values: unitsSold)

我们设置一些模拟数据,给出一些产品一年中每个月售出的单位数。然后,我们将数据传给 setChart()。

让一个图表显示数据,我们还需创建一个 BarChartData 对象,并将其设置为 barChartView 的数据属性。添加下面的代码到 setChart()函数底部。

var dataEntries: [BarChartDataEntry] = []
        
for i in 0..<dataPoints.count {
    let dataEntry = BarChartDataEntry(value: values[i], xIndex: i)
    dataEntries.append(dataEntry)
}
        
let chartDataSet = BarChartDataSet(yVals: dataEntries, label: "Units Sold")
let chartData = BarChartData(xVals: months, dataSet: chartDataSet)
barChartView.data = chartData

以上代码中,我们创建了一个 BarChartDataEntry 对象的数组。BarChartDataEntry 初始化需要每个数据项的值、其对应的项目索引以及一个任意的标签。

随后,我们使用这个对象创建 BarChartDataSet,主要用来传递 BarChartDataEntry 对象的数组,以及描述数据的标签。

最后,我们用它来创建一个 BarChartData 对象,用来设定我们图表视图的数据。

运行该应用,可以看到如下图所示有数据的柱形图。

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

你可以为出现在视图右下方的图表设置说明。默认情况下,文本可用来设置「说明」,该说明会出现在图片上。参考 ofMPAndroidChart 文档,你可以更改描述的位置,但看一下 iOS 的 API,这并没有包括在内。函数库仍在维护,所以可能会在之后添加。如果你想改变描述的位置,你可以修改 ChartViewBase 类(是 BarChartView 类的子类)中的 drawDescription(上下文)函数。

针对本应用,我们移除描述文本。在 setChart()函数的底部添加下列内容,将描述文本设为空字符串。

barChartView.descriptionText = ""

自定义图表

你可以修改一些属性来自定义图表视图的外观。下面我们来具体介绍,你也可以浏览文档看看哪些可以自定义。

首先,我们修改柱形图的默认颜色。添加下列设置到 setChart()函数的底部。

chartDataSet.colors = [UIColor(red: 230/255, green: 126/255, blue: 34/255, alpha: 1)]

以上代码设置了与数据相关的颜色。将该设定给 UIColor 对象数组。因此只要数组中有一种颜色,所有的实体均可用。

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

如果你想给每一个数据对象设置不同的颜色,那么你需要提供更多的颜色,本例中需要12种。如果你的颜色数量少于实体总数,那么从坐到右将不同的颜色分配给柱形图,直到颜色用完后重新开始分配。

API 中也自带一些预定义颜色模板,你可以用它来为数据项设定不同颜色,它们包括:

  • ChartColorTemplates.liberty()
  • ChartColorTemplates.joyful()
  • ChartColorTemplates.pastel()
  • ChartColorTemplates.colorful()
  • ChartColorTemplates.vordiplom()

如下所示,使用 ChartColorTemplates.colorful()模板。

chartDataSet.colors = ChartColorTemplates.colorful()
如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

如下所示修改 X 轴标签的位置。

barChartView.xAxis.labelPosition = .Bottom

现在标签在图表底部。

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

按照以下办法可以修改图表的背景色。

barChartView.backgroundColor = UIColor(red: 189/255, green: 195/255, blue: 199/255, alpha: 1)

根据上述设定,你会看到如下所示的界面。

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

动画

你可以为图表设定一些动画,使之更加生动活泼。你可以使用三种主要的动画类型方法,可以同时让 XY 轴或者分别某个轴产生动画。

  • animate(xAxisDuration: NSTimeInterval, yAxisDuration: NSTimeInterval)
  • animate(xAxisDuration: NSTimeInterval)
  • animate(yAxisDuration: NSTimeInterval)

你可以加入任意的 ChartEasingOption 到以上函数。选项如下:

  • Linear
  • EaseInQuad
  • EaseOutQuad
  • EaseInOutQuad
  • EaseInCubic
  • EaseOutCubic
  • EaseInOutCubic
  • EaseInQuart
  • EaseOutQuart
  • EaseInOutQuart
  • EaseInQuint
  • EaseOutQuint
  • EaseInOutQuint
  • EaseInSine
  • EaseOutSine
  • EaseInOutSine
  • EaseInExpo
  • EaseOutExpo
  • EaseInOutExpo
  • EaseInCirc
  • EaseOutCirc
  • EaseInOutCirc
  • EaseInElastic
  • EaseOutElastic
  • EaseInOutElastic
  • EaseInBack
  • EaseOutBack
  • EaseInOutBack
  • EaseInBounce
  • EaseOutBounce
  • EaseInOutBounce

添加下行内容到 setChart()函数。

运行应用,柱形图以动画的形式加入视图。我们同时为两个轴都设置2秒的动画。

如何在Swift语言下iOS Charts API制作漂亮图表
如何在Swift语言下iOS Charts API制作漂亮图表

修改上述状态为:

barChartView.animate(xAxisDuration: 2.0, yAxisDuration: 2.0, easingOption: .EaseInBounce)

你会看到这行的特效如下。

如何在Swift语言下iOS Charts API制作漂亮图表
如何在Swift语言下iOS Charts API制作漂亮图表

界限

界限是针对所有线形图、柱形图和离散图的附加功能。它允许在图表中显示额外的线条为特定轴(X 或 Y 轴)加限制。这额外的线用来设定数据的目标值,帮助用户更容易了解是否达到界限。

要在图表中加入界限,可以添加以下代码到 setData()函数中。

let ll = ChartLimitLine(limit: 10.0, label: "Target")
barChartView.rightAxis.addLimitLine(ll)

运行该应用,你可以看到一条红线,标记在单位10左右。上面的代码中,我们在界限上添加了一个标签。但是 ChartLimitLine 有另一个没有添加标签的初始化函数,如果不想加的话可以省略。

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

触控事件

如果运行应用,你会发现通过默认的捏拉缩放和双击来进行缩放。此外,如果某个柱形条被单击,该柱形条会突出显示。很棒的是无需自己再编代码就能使用该功能,除非你想添加更多的功能,比如当用户单击柱形条时有其他响应。

为了检测图表内的选择,我们将使用 ChartViewDelegate 协议。

修改该类的声明如下。

class BarChartViewController: UIViewController, ChartViewDelegate {

在调用 super.viewDidLoad()后面添加下列代码到 viewDidLoad()。

barChartView.delegate = self

添加下面的函数到该类。

func chartValueSelected(chartView: ChartViewBase, entry: ChartDataEntry, dataSetIndex: Int, highlight: ChartHighlight) {
    println("\(entry.value) in \(months[entry.xIndex])")
}

当图表视图中的值被选中时,会调用上述函数。这里我们打印出被选中的月份和值。

保存图表

你可以将图表的当前状态保存为图像。可以选择将它保存到相机胶卷或者重新设置一个保存路径。

首先,我们将添加一个保存按钮到图表视图。打开故事板文件,并找到柱形图表视图控制器。拖动导航项目到视图控制器的导航栏,然后拖动柱形按钮项,并将其放置于导航项目的右上角。删除属性检查器中的导航项目的标题属性中的「标题」文本。选择柱形按钮项,并设置其标识便于保存到属性检查器。可以看到如下界面。

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

接着为按钮创建一个消息响应,命名为 saveChart,添加以下代码到 BarChartViewController 类中。

@IBAction func saveChart(sender: UIBarButtonItem) {
        
}

按照下列代码修改上面的方法。

@IBAction func saveChart(sender: UIBarButtonItem) {
    barChartView.saveToCameraRoll()
}

执行该程序后,当点击保存按钮时,图表图像讲保存到相册,可用 Photos 应用浏览。

下列代码可以重新设置保存路径。

barChartView.saveToPath(path: String, format: ChartViewBase.ImageFormat, compressionQuality: Double)

图片格式可能是 .JPEG或 .PNG,该 compressionQuality 是一种无损格式(JPEG)的压缩质量。

更多图表

现在我们看一下其他几个图表。大部分我们都看过了,所以不再过多赘述。

首先,在故事板文件中找到图表视图控制器,在识别检查器中,设定视图的标签为 Pie Chart View 的类为 PieChartView。

如下所示修改 ChartsViewController 类。

import UIKit
import Charts
 
class ChartsViewController: UIViewController {
    
    @IBOutlet weak var lineChartView: LineChartView!
    @IBOutlet weak var pieChartView: PieChartView!
 
    override func viewDidLoad() {
        super.viewDidLoad()
 
        // Do any additional setup after loading the view.
        
        let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
        let unitsSold = [20.0, 4.0, 6.0, 3.0, 12.0, 16.0]
        
        setChart(months, values: unitsSold)
        
    }
    
    func setChart(dataPoints: [String], values: [Double]) {
        
        var dataEntries: [ChartDataEntry] = []
        
        for i in 0..<dataPoints.count {
            let dataEntry = ChartDataEntry(value: values[i], xIndex: i)
            dataEntries.append(dataEntry)
        }
        
        let pieChartDataSet = PieChartDataSet(yVals: dataEntries, label: "Units Sold")
        let pieChartData = PieChartData(xVals: dataPoints, dataSet: pieChartDataSet)
        pieChartView.data = pieChartData
        
        var colors: [UIColor] = []
        
        for i in 0..<dataPoints.count {
            let red = Double(arc4random_uniform(256))
            let green = Double(arc4random_uniform(256))
            let blue = Double(arc4random_uniform(256))
            
            let color = UIColor(red: CGFloat(red/255), green: CGFloat(green/255), blue: CGFloat(blue/255), alpha: 1)
            colors.append(color)
        }
        
        pieChartDataSet.colors = colors
        
        
        let lineChartDataSet = LineChartDataSet(yVals: dataEntries, label: "Units Sold")
        let lineChartData = LineChartData(xVals: dataPoints, dataSet: lineChartDataSet)
        lineChartView.data = lineChartData
        
    }
 
}

以上代码中,我们将 Charts 框架导入类中,像之前创建柱形图那样,创建一个饼状图和一个线形图。但需要注意的是,在柱形图表例中我们使用的是 BarChartDataEntry,而现在使用父类 ChartDataEntry 创建数据项。

运行应用,选择表格视图中的其他图表,可以看见一个线形图和有颜色的饼状图。你的应用可能跟下图不完全相同,因为饼状图的颜色是随机的。

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

结束语

本篇文章大致介绍了使用 ios-charts 库创建的一些图表类型,大概涉及了自定义图表的一些内容。如果你想知道这个库的其他功能,你可以参考这个下载库自带的 ChartsDemo 项目的代码,也可以看看该项目的维基百科。这个链接到 Wiki 页面会引导至 MPAndroidChart 项目文档。作者在写这篇文章时,还没有库的 iOS 版本的文档说明,但其实 API 跟 Android 版本约95%都相同,当你百思不得其解时,Android 文档仍然可以派上用场。

你可以在这里下载完整项目

下面列出可创建的图表类型。

线形图(图例、简单的设计)

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

线形图(图例、简单的设计)

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

线图(立方线)

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

线图(单数据组)

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

复合图(柱形图和线图)

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

柱形图(图例、简单设计)

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

柱形图(分组数据集)

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

水平柱形图

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

饼状图(有选取)

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

散点图(以方形、三角形、圆形等图例)

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

K线图(针对财经数据)

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

气泡图(由气泡区域覆盖来表示值)

如何在Swift语言下用iOS Charts API制作漂亮图表
如何在Swift语言下用iOS Charts API制作漂亮图表

雷达图(蛛网图)

如何在Swift语言下用iOS Charts API制作漂亮图
如何在Swift语言下用iOS Charts API制作漂亮图

本文系 OneAPM 工程师编译整理。OneAPM 是应用性能管理领域的新兴领军企业,能帮助企业用户和开发者轻松实现:缓慢的程序代码和 SQL 语句的实时抓取。想阅读更多技术文章,请访问 OneAPM 官方博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,066评论 4 62
  • 感觉我的大学生活是从今天才真正开始。 早上闹了5:00的闹钟,真的很难爬起来,但是想想还是强迫自己起床了,一个人静...
    一颗榴莲_阅读 158评论 0 0
  • 这里是一篇清明节随笔。 清明节去扫墓看到了很多墓碑,多数墓碑上都有一张或两张黑白的老照片。 不过在一片黑白照片中突...
    侯悍超阅读 208评论 4 1
  • 又是一个清晨 阳光多了一些妩媚 遥远的风来自海的另一半 带着湿意 带着潮新 日头渐渐拉短了塔影 走在细沙的脚上 多...
    垄上行云阅读 596评论 2 1