自适应 Table View Cells

自适应Table View Cells

注意:这篇教程支持最新的Xcode 7.3,iOS 9和Swift 2.2。

学习如何使用Auto Layout创建自适应table view cells

如果你曾经创建过自定义table view cells,那么很可能你写了一堆适应大小的代码。你可能会对于计算每个label、image view、text field和其它一切cell里的东西高度很熟悉,而且还是手动的。

讲道理,这种方式实在令人难以接受、极易出错、性价比很低。

这篇自适应Table View Cells教程里,你会学习到如何创建自定义table view cells,并且动态改变他们到适合他们内容的大小。你可能在想,“那一定需要许多工作……!”

并不。:] 很幸运,苹果在iOS 8里让这一切都变得非常简单。

注意::这篇教程需要Xcode 7.3或更新的版本才能兼容最新的Swift语法。

这篇教程还假定你已经基本熟悉Auto Layout、UITableView和Swift开发。如果你是iOS或Swift开发的完全新手,你应该先看看我其他的教程。

上车

穿越到iOS 6时代,苹果发布了一个非常好的新技术:Auto Layout。为庆祝这一壮举,全国的开发者举国欢庆,夜夜笙歌,红旗招展,人山人海……

好吧,可能有一点点夸张,但真的是件事儿。

虽然它鼓舞了无数的开发者,但Auto Layout还是很笨重。手动写Auto Layout代码曾经是、现在也是iOS开发的啰嗦的绝佳例证。Interface Builder一开始的时候在设置constraints的时候也完全在帮倒忙。

回到现在。伴随着对Interface Builder的所有提升以及iOS 8的发布,使用Auto Layout来创建自适应table view cells终于简单了!

除了一点点额外的工作,你要做的所有事情就是:

  1. 创建table view cells的时候使用Auto Layout。
  2. 设置table view的rowHeightUITableViewAutomaticDimension
  3. 设置estimatedRowHeight或实现height estimation delegate方法。

但你不想现在马上就挖掘理论,不是吗?你已经准备好写代码了,所以让我们直接用项目开始吧。

教程App预览

想象一下你最大的客户跑过来和你说,“我想要一个app可以显示曾经的最著名艺术家以及他们最著名的作品!”

“我们开始做App了,但我们被如何在table view里显示内容给难倒了,”你的客户承认。“你能帮个忙吗?”

你突然有一种强烈欲望想跑进最近的电话亭然后换上穿上披风。

但你并不需要这些小花招来让你成为客户的大英雄——你的编程技能点已经够了!

首先,从这里下载“客户的代码”—艺术-起始(提取码:cc3a)-这个教程的起始项目,基于自适应table view cells。解压zip文件然后在Xcode里打开项目。

打开Main.storyboard(在Artistry项目下的Views分组。)你会看见三个scenes:

从左到右,他们是:

  • 一个顶级导航控制器
  • ArtistListViewController显示艺术家列表
  • ArtistDetailViewController显示艺术家的作品和每个作品的信息

Build and run。你会看到ArtistListViewController显示了艺术家的列表。选择第一个艺术家(Pablo Picasso),app会segue到ArtistDetailViewController,显示了选中的艺术家的作品列表:

不仅app没有每个艺术家和每件作品的图片,你想显示的信息都被裁掉了!每一条信息和图片都会是不同的尺寸,所以不能只是增加table view cell高度,然后就收工了!你的cell高度需要时动态的,基于每个cell的内容。

你会从ArtistListViewController开始实现动态cell高度。

自适应Table View Cells

要让动态cell高度能正常工作,你需要创建一个自定义table view cell然后设置它为正确的Auto Layout constraints。

在project navigator里选择Views分组,然后按Command-N来在这个分组里创建一个新文件。创建一个新的Cocoa Touch Class叫做ArtistTableViewCell,让它是UITableViewCell的子类

打开ArtistTableViewCell.swift,删除两个自动生成的方法,然后添加下面的property:

@IBOutlet var bioLabel: UILabel!

下一步,打开 Main.storyboard,选择ArtistListViewController里的table view里的cell。在Identity InspectorClass改为ArtistTableViewCell

拖一个新的UILabel到cell上,设置text为“简历”。在Identity Inspector里设置新的label的Lines property(label可以拥有的最大行数)为 0。看起来应该像这样:

设置行数对于动态大小cells非常重要。一个设置行数为0的label会根据它显示的文本数量来增长。设置为任何其他数字的行数label会缩短他们的文字,一旦超出了可用的行数的话。

链接ArtistTableViewCellbioLabel outlet到cell上的label。一个快速方法是右击Document Outline里的Cell,然后按住从弹出的菜单的Outlets列表的bioLabel右侧的空白圆圈到拖到你排放的label:

让Auto Layout在 UITableViewCell 上工作的秘诀就是确保每个subview所有的边上都有constraints来把它们固定住——这就是,每个subview都要有leading、top、trailing和bottom constraints。然后,subviews的实际高度会被用来决定每个cell的高度。你马上就会这么做。

注意:如果你并不熟悉Auto Layout,或者希望复习一下如何设置Auto Layout constraints,可以看看其他教程。

选择bioLabel然后按storyboard底部的Pin按钮。在这个菜单里,简单的选择菜单最上方的4条虚线,改变leading和trailing值为8,然后点击Add Constraints。看起来会像这样

这确保不论cell是大还是小,bio label总是:

  • 上下外边距都是0点
  • 左右外边距都是8点

回顾:这满足之前的Auto Layout标准吗?

  1. 是否每个子视图每个边都有constraints固定?是的。
  2. constraints是从contentView的顶部到底部吗?是的
    bioLabel 用0点连接了上下外边距
    所以,Auto Layout现在可以确定cell的高度了!

酷,你的ArtistTableViewCell设置好啦!如果你现在build and run一下app,你会看到...

什么都没变。什么鬼?!不要担心,在cells成为动态前只需要再写一点点代码。

配置 Table View

首先,你需要配置 table view 来正确使用你的自定义cell。

打开 ArtistListViewController.swift 然后用下面的替换 tableView(_:cellForRowAtIndexPath:)

func tableView(tableView: UITableView,
                 cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell",
                                                           forIndexPath: indexPath) as! ArtistTableViewCell

    let artist = artists[indexPath.row]
    cell.bioLabel.text = artist.bio
    cell.bioLabel.textColor = UIColor(red: 114 / 255,
                                      green: 114 / 255,
                                      blue: 114 / 255,
                                      alpha: 1.0)
    return cell
  }

上面的代码很直接:让一个 cell 出列(dequeue),设置它的信息和文字颜色,然后返回 cell。

再次运行app,它会看起来还是没什么改变。你现在用的是 bioLabel,但每个cell只显示一行文本。即使行数设置为 0并且你的 constraints 被正确配置了,所以你的 bioLabel 占据了整个 cell,这说明 table views 需要被告知让 Auto Layout 来驱动每个 cell 的高度。

回到 ArtistListViewController.swift 把这两行代码加到 viewDidLoad() 方法的底部:

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 140

当你设置 rowHeightUITableViewAutomaticDimension 的时候,table view 被告知使用 Auto Layout constraints 和 cells 的内容来决定每个 cell 的高度。

为了让 table view 这么做,你必须要也提供一个 ** estimatedRowHeight**。这个例子里,140.0 只是一个随意值,在这个特定实例里很合适。对于你自己的项目,你应该选择一个更符合你会显示的数据类型的值。

Build and run,你现在应该能看到每个艺术家的简介啦 :]

添加图片

现在你能看到每个艺术家的整个简介了,这很好,但还有更多需要需要显示。每个艺术家有一个图片和名字。使用这些额外数据块来让 app 看起来更棒。

你需要添加一个 image view 给 ArtistTableViewCell,还有另一个 label 给艺术家的名字。
打开 ArtistTableViewCell.swift 然后添加下面的 properties:

@IBOutlet var nameLabel: UILabel!
@IBOutlet var artistImageView: UIImageView!

image view 的变量名叫做 artistImageView 而不是 imageView 因为 UITableViewCell 已经有一个 imageView property 了。

打开 Main.storyboard,选择 cell 然后在 Size Inspector 改变 Row Height 为 140;给你更多空间来使用:

现在选择 Bio Label 的 leading constraint。你可以在 Document Outline 里找到它,在 Content View 里的 Constraints 下面:

按 Delete 删除这个 constraint。现在忽略任何 Auto Layout 警告。用你的光标抓住 bio label 左侧边缘,然后把它拉到右边让 bio label 现在只占据 cell 一半的宽度。左半边会用来给 image view 和 艺术家名字的 label:

拖一个新 label,放到 cell 的底部,在你新开辟的区域水平居中。设置 label 的 text 为 “名字”:

现在拖出一个 image view,放到 name label 上面:

最后,为新的 image view 和新的 label 都连接 outlets,使用你对 bio label 所用的同样的技术:

现在是时候设置更多 constraints 了。从 name label 开始向上去,使用 Pin 菜单来:

  • 固定 name label 的底部边距为 0 点,从 content view 的底部外边距。
  • 固定 name label 的上边距为 8 点,从 image view 的底部。
  • 固定 image view 的上边距为 0 点,从 content view 的上边距。
  • 固定 image view 的 leading 边距为 0 点,从 content view 的 leading 边。
  • 固定 image view 的 trailing 边距为 16 点,从 bio label 的 leading 边距。

选中 image view,按住 Control 拖到 cell 的 content view。Let go,然后选择菜单里的 Equal Widths

Document Outline 里选择这个新的宽度 constraint,然后设置 multiplier 为 0.5:

这让 image view 的宽度会等于准确的 cell 的一半宽度。

只有一对 constraints 需要添加:

  • 按住 Shift 点击 image view 和 name label 然后从 Pin 菜单里选择 Equal Width
  • 按住 Shift 点击 image view 和 name label 然后从 Align 菜单里选择 Horizontal Centers

带有这些全新的 constraints,Auto Layout 大概已经抛出一些警告让你知道一些框架过期了。要修复好它,选择 Document Outline 里 cell 的 Content View 然后点击 Resolve Auto Layout Issues 菜单然后选择 All Views 下面的 Update Frames

storyboard 现在就这样了。打开 ArtistListViewController.swift 然后添加下面两行代码到 tableView(_:cellForRowAtIndexPath:),在你设置好 bioLabel 的文字之后:

cell.artistImageView.image = artist.image
cell.nameLabel.text = artist.name

然后在设置好 textColor 之后添加这些行:

cell.nameLabel.backgroundColor = UIColor(red: 255 / 255, green: 152 / 255, blue: 1 / 255, alpha: 1.0)
cell.nameLabel.textColor = UIColor.whiteColor()
cell.nameLabel.textAlignment = .Center
cell.selectionStyle = .None

Build and run 这个 app。这一屏看起来更好了,但向下滑到 Georgia O’Keeffe 然后你会注意到一些奇怪的事情:

根据 constraints name label 被撑大了(上面到 image view 底部 8 点,底部外边距到 cell 的 content view 为 0 点)。

可以调整两个 constraints 来修复它。在 Main.storyboard 选择 Name label 然后从它的底端创建另一个 constraint 到 cell 的 底部外边距(margin)。现在从 Document Outline 选择那个 constraint 然后改变它的 RelationGreater Than or Equal

现在选择 name label 的旧的底部 constraint 然后设置它的 priority 为 250:

这样的话,Auto Layout 会在需要的时候打破老的 constraint,因为它的 priority(优先级)比带有 >=0 relation 的底部 constraint 要低。再次运行 app 然后所有东西现在看起来应该都很棒。

展示艺术!

如果你回顾一下开头,选择一个艺术家显示一个 view controller,它显示选中艺术家的作品。table view 里的 cells 会需要有动态高度,因为每个作品有不同尺寸的图片和伴随数据。

第一步,就像之前一样,创建另一个 UITableViewCell 子类。

在 project navigator 里选择 Views 组然后按 Command-N 在这个组里创建一个新文件。创建一个新的 Cocoa Touch Class 叫做 WorkTableViewCell 然后让它成为 UITableViewCell 的子类。

打开 WorkTableViewCell.swift 然后,像从前一样,删除两个 WorkTableViewCell 自动生成的方法然后添加这些 properties:

@IBOutlet weak var workImageView: UIImageView!
@IBOutlet weak var workTitleLabel: UILabel!
@IBOutlet weak var moreInfoTextView: UITextView!

打开 Main.storyboard 然后选择 Artist Detail View Controller 场景里的 table view 里的 cell。设置 cell 的 Custom ClassWorkTableViewCell,然后改变 row height 为 200 来给你自己大量空间去操作。

现在拖出一个 image view,一个 label,和一个 text view,像下面的图片那样放置他们(text view 在最下面):

改变 text view 的 text 为“点击查看更多信息 >”,并且 label 改为 “名字”。把 image view 的 mode 改为 Aspect Fit。选择 text view,在 Attribute Inspector 里,改变 alignment 为居中并且禁用 scrolling:

禁用 scrolling 和设置 label 为 0 lines 相似的重要。scrolling 禁用的话,text view 知道增长它的尺寸来满足它的全部内容,因为用户不能滑动来浏览文本呀。

再远一点,回到你禁用 scrolling 的地方,移除 User Interaction Enabled 的钩钩,这会允许触摸传递给 text view 并且除法 cell 本身的选中状态。

连接三个元素到对应的 outlets 上,就像你对第一个 cell 做的那样。

现在你要添加 constraints。从 text view 开始然后向上走:

  • 固定 text view 的底边到 content view 的底部 margin 为 0 点。
  • 固定 text view 的 leading 和 trailing 边到 content view 的 leading 和 trailing margins 为 8 点。
  • 固定 text view 的上边到 label 的底部为 8 点。
  • 固定 label 的上边到 image view 的下边为 8 点。
  • 居中 label,通过在 Align 菜单选择 Horizontally in Container
  • 同时选择名字 label 和 image view(按住 Shift 点击)然后从 Pin 菜单选择 Equal Widths
  • 固定 image view 的上边到 content view 的顶部 margin 为 0 点。
  • 固定 image view 的 leading 和 trailing 边到 content view 的 leading 和 trailing margins 为 8 点。

更新 frames 就像之前 Auto Layout 显示任何警告的时候那样做。现在 storyboard 都已经搞完了。就像你要对之前的 view controller 要做的,动态 cell 高度也要用一点代码来做。

打开 ArtistDetailViewController.swift 然后替换 tableView(_:cellForRowAtIndexPath:) 为如下代码:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) ->               UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as!     WorkTableViewCell

    let work = selectedArtist.works[indexPath.row]

    cell.workTitleLabel.text = work.title
    cell.workImageView.image = work.image

    cell.workTitleLabel.backgroundColor = UIColor(red: 204 / 255, green: 204 / 255, blue: 204 / 255, alpha: 1.0)
    cell.workTitleLabel.textAlignment = .Center
    cell.moreInfoTextView.textColor = UIColor(red: 114 / 255, green: 114 / 255, blue: 114 / 255, alpha: 1.0)
    cell.selectionStyle = .None

    return cell
  }

这个现在看起来应该非常熟悉了。你在让你的 cell 出列,然后打造他们,获得连接到你要显示的模型结构的关系,然后在返回 cell 之前配置好它。

现在这个类的 viewDidLoad() 里,添加如下代码到方法的结尾:

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 300

这是同样的代码,你在前一个 view controller 里也用了。运行 app,选择 Picasso,然后你会看到现在可以遍历艺术家的作品了:

哎呦不错哦,但让我们到更高级别吧,添加扩展 cells 来展现有关每个作品更多的信息。你的客户会爱上这个的!

扩展 Cells

因为你的 cell 高度由 Auto Layout constraints 和每个界面元素的内容来驱动,用户点击 cell 的时候扩展 cells 应该和添加更多 text 到 text view 一样简单。

打开 ArtistDetailViewController.swift 然后添加如下extension:

extension ArtistDetailViewController: UITableViewDelegate {

  func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    // 1
    guard let cell = tableView.cellForRowAtIndexPath(indexPath) as? WorkTableViewCell else { return }

    var work = selectedArtist.works[indexPath.row]

    // 2
    work.isExpanded = !work.isExpanded
    selectedArtist.works[indexPath.row] = work

    // 3
    cell.moreInfoTextView.text = work.isExpanded ? work.info : moreInfoText
    cell.moreInfoTextView.textAlignment = work.isExpanded ? .Left : .Center

    // 4
    UIView.animateWithDuration(0.3) {
      cell.contentView.layoutIfNeeded()
    }

    // 5
    tableView.beginUpdates()
    tableView.endUpdates()

    // 6
    tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Top, animated: true)
  }
}

这是发生的事情:

  1. 你问 tableview 要了到 cell 的关系,到那个与 selected index path 相符的 cell,然后获得了相应的 Work
  2. 改变 WorkisExpanded 状态,然后再把它放回数组(很必要,因为结构体是通过拷贝传递的)。
  3. 下一步,改变 cell 的 text view,基于 work 是不是被扩展了:如果是,设置 text view 显示作品的 info property,然后改变 text alignment 为 Left。如果没被扩展,把 text 设置回 “点击查看更多信息 >”以及 alignment 设置回 Center
  4. 现在 text view 的内容已经改变了,cell 的 constraints 需要被刷新。在动画 block 中调用 layoutIfNeeded(),会显示这些 constraint 改变的动画。
  5. 除了 constraint 改变,table view 现在需要刷新 cell 高度。调用 beginUpdates()endUpdates() 会强制 table view 用动画的方式刷新高度。
  6. 最后,告诉 table view 把选中的行滑动到 table view 的顶端,用动画的方式。

现在在 tableView(_:cellForRowAtIndexPath:) 里,添加下面两行到末尾,在你返回 cell 之前:

cell.moreInfoTextView.text = work.isExpanded ? work.info : moreInfoText
cell.moreInfoTextView.textAlignment = work.isExpanded ? NSTextAlignment.Left : NSTextAlignment.Center

这个代码会让正在被复用的 cell 正确的记住之前是否在被扩展状态。

Build and run app。当你点击一个作品 cell,你会看到它扩展到容纳了全部文本。但图片动画有一点诡异。

这不难修复!打开 Main.storyboard 然后在你的 WorkTableViewCell 里选择 image view,然后打开 size inspector。改变 Content Hugging PriorityContent Compression Resistance Priority 为下面图片里的值:

设置 Vertical Content Hugging Priority 为 252 会帮助 image view 紧挨着它的内容,并且在动画过程中不会被撑大。设置 Vertical Compression Resistance Priority 为 749 让图片可以被压缩,如果其它界面元素在它旁边增长的话。但这只会有助于让 cell 扩大的动画更平滑。图片根本不会被压缩,因为如果 cell 里面的东西增长了 cell 的高度也会增长。

Build and run app。选择一个艺术家,然后点击作品。你会看到一些非常平滑的 cell 扩展,展示有关每个艺术品的信息:

万岁!

动态类型

你已经向你的客户展示了你的进步,他们都爱上它了!但他们还有最后一个请求。他们想要 app 支持 更大字体(Larger Text)辅助功能。app 需要调整到顾客偏好的阅读尺寸。

在 iOS 7 中发布的,动态类型(Dynamic Type)让这变得简单。动态类型给开发者能力来为不同的文本块(例如大标题或正文)指定不同的文本样式,当用户在设备的设置里改变偏好尺寸的时候文本就能自动调整。

ArtistListViewController.swift,添加这两行代码到 tableView(_:cellForRowAtIndexPath:) 的最后,就在返回 cell 之前:

cell.nameLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cell.bioLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)

这是使用动态类型设置一个基于文本的界面元素的方式。preferredFontForTextStyle(style:) 只有一个参数,就是你希望这个文本元素使用的样式。有 10 个不同的常量供你使用,参考苹果的文档上 preferredFontForTextStyle(style:) 来了解更多相关内容。

现在你只需要确保在用户改变他们的偏好尺寸的时候 table view 会刷新自己。要实现它,添加下面的方法到 ArtistListViewController 里,就在 viewDidLoad() 正下方:

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
                    NSNotificationCenter.defaultCenter().addObserverForName(UIContentSizeCategoryDidChangeNotification, object: nil, queue: NSOperationQueue.mainQueue()) { [weak self] _ in self?.tableView.reloadData()
      }
    }
  }

这里你为 onContentSizeCategoryChange: 通知添加了 observer,只要用户改变了偏爱的文本大小就会触发。

observer 使用闭包告诉 table view 来重载自己。这会导致屏幕上的所有 cells 都调用 tableView(_:cellForRowAtIndexPath:),会执行我们刚刚添加的 preferredFontForTextStyle(style:)。现在通知一旦接收字体就总是会更新。

注意:从 iOS 9 开始,移除通知中心 observers 不再是必要的了。如果你的 app 的开发目标是 iOS 8,那么你还是需要这么做!

ArtistDetailViewController 添加动态类型支持也几乎相同。打开 ArtistDetailViewController.swift 然后添加这两行代码到 tableView(_:cellForRowAtIndexPath:) 的末尾:

cell.workTitleLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cell.moreInfoTextView.font = UIFont.preferredFontForTextStyle(UIFontTextStyleFootnote)

然后添加和之前的 view controller 完全相同的 viewDidAppear(_:) 实现。

目前用 iOS 9.3 模拟器测试不会工作,所以你要在你的设备上构建来测试它。在你的设备上启动 app,然后回到主屏。打开设置 app,然后点击通用 > 辅助功能 > 更大字体,然后拖动滑块到右边,来增大文本大小到更大的设置:

然后回到艺术 app,你的文本现在应该显示的更大了。并且由于你对动态尺寸 cells 所做的工作,table view 现在看起来棒极了:

接下来去哪儿

恭喜完成了自适应 table view cells的教程!:]

你可以从这里下载完整项目,https://yunpan.cn/cBI579k4YhgkJ (提取码:cde6)。

Table views 可能是 iOS 里最基本的结构化数据视图了。在你的 apps 变复杂的时候,您可能会使用各种自定义 table view cell 布局。但幸运的是,Auto Layout 和 iOS 8 让这个任务变得无比简单。

如果你有任何评论或问题,就写在下面吧!

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

推荐阅读更多精彩内容