QuickLook框架详细解析(四) —— QuickLook预览和缩略图扩展的实现(一)

版本记录

版本号 时间
V1.0 2021.03.22 星期一

前言

QuickLook框架提供了文档的预览功能。接下来几篇我们就一起看一下这个框架。感兴趣的可以看下面几篇。
1. QuickLook框架详细解析(一) —— 基本概览(一)
2. QuickLook框架详细解析(二) —— QuickLook 预览简单示例(一)
3. QuickLook框架详细解析(三) —— QuickLook 预览简单示例(二)

开始

首先看下写作内容:

了解如何构建自己的Quick Look预览和缩略图扩展名以在iOS中显示自定义文件类型。内容来自翻译

下面就是写作环境:

Swift 5, iOS 14, Xcode 12

接着就是正文啦。

您可能会发现自己想要在应用程序中显示文件的缩略图,或者甚至想要显示文件本身的更丰富的预览。 幸运的是,QuickLook框架使您可以轻松生成缩略图并显示许多标准文件类型的预览。

虽然这对标准文件类型非常有用,但是如果您的应用程序专注于QuickLook不能涵盖的特定文件类型,该怎么办?好吧,不要害怕。借助iOS 13中引入的Quick Look previewsthumbnail extension点,您可以在自己的应用程序以及也使用QuickLook的同一设备上的任何其他应用程序中为自定义文件类型提供自定义预览和缩略图。

注意:您会看到两种形式的间距形式,都用于后端QuickLook framework(不带空格)和面向用户的Quick Look(带空格)。名称中的空格规则似乎很复杂,因此我们努力做到平衡。

在本教程中,您将学习如何:

  • 使用QLThumbnailGenerator生成缩略图
  • 为您的自定义文件格式定义并导出您自己的document type
  • 构建自己的Quick Look preview extension
  • 构建自己的Thumbnail extension
  • 调试您在扩展extension程序中运行的代码

为此,您将使用RazeThumb,该应用程序显示文件列表并在点按时显示每个文件的Quick Look预览。您将增强该应用程序,以使用QuickLook显示代表列表中每个文件的缩略图。最后,您将为您的自定义.thumb文件类型添加Quick Look preview extensionThumbnail extension,并在appFiles应用程序中看到它。

打开下载项目材料。然后在启动程序文件夹中打开RazeThumb.xcodeproj

RazeThumb是一个简单的文档浏览应用程序。它预装了各种包含缩略图的文件类型。六种格式中的五种是标准文件类型。第六种是自定义文件类型,称为.thumb文件。

构建并运行:

该应用程序显示六种不同文件类型的列表,每种文件类型均带有一个占位符文档图标。 轻按每个文件将显示QLPreviewController,它显示文件的预览。 这就是目前所做的全部,但是您将在不断改进该应用程序。


Adding Quick Look Thumbnails

您要做的第一件事就是增强RazeThumb,以使用每种文件类型的缩略图。 当前,RazeThumb为每个文件显示一个通用文档图标。 QuickLook框架为各种文件类型提供缩略图,包括图像,PDF,音频和视频。 请查看Apple的文档Apple’s documentation,以获取有关Apple支持的其他文件类型的更多详细信息。 如果QuickLook无法识别文件类型,它仍会创建某种占位符缩略图。 将缩略图添加到应用后,您将学到更多有关此的知识。

现在,是时候开始修饰RazeThumb了。

打开Document.swift并将import Foundation更改为:

import QuickLook

在文件底部,粘贴以下扩展名:

// MARK: - QLThumbnailGenerator
extension Document {
  func generateThumbnail(
    size: CGSize,
    scale: CGFloat,
    completion: @escaping (UIImage) -> Void
  ) {
    if let thumbnail = UIImage(systemName: "doc") {
      completion(thumbnail)
    }
  }
}

上面的代码是异步缩略图生成方法的占位符。 现在,它返回文档系统图标的UIImage。 稍后,您将替换为对QuickLook框架的请求以生成缩略图。

接下来,打开DocumentThumbnailView.swift并将以下代码直接粘贴到.groupBoxStyle(PlainGroupBoxStyle())下面:

.onAppear {
  document.generateThumbnail(
    size: thumbnailSize,
    scale: displayScale
  ) { uiImage in
    DispatchQueue.main.async {
      self.thumbnail = Image(uiImage: uiImage)
    }
  }
}

上面的代码等待视图出现。 然后,它要求为每个文件提供缩略图UIImageUIImage到达后,视图将使用主线程更新SwiftUI图像Image。 这是所有用户界面更新所必需的。

构建并运行:

您会看到每个文件的文档图标现在要小得多。 这是因为您没有调整图像的大小。 但是很快,QuickLook框架将为您处理大小调整。

1. Generating a Quick Look thumbnail

要从QuickLook框架中获取缩略图,您需要使用QLThumbnailGenerator创建一个QLThumbnailGenerator.Request并执行请求。 再次打开Document.swift,并用以下代码替换generateThumbnail(size:scale:completion :)的内容:

// 1
let request = QLThumbnailGenerator.Request(
  fileAt: url,
  size: size,
  scale: scale,
  representationTypes: .all)

// 2
let generator = QLThumbnailGenerator.shared
generator.generateRepresentations(for: request) { thumbnail, _, error in
  // 3
  if let thumbnail = thumbnail {
    print("\(name) thumbnail generated")
    completion(thumbnail.uiImage)
  } else if let error = error {
    print("\(name) - \(error)")
  }
}

在上面的代码中,您将执行以下操作:

  • 1) 使用视图提供的大小和比例以及定义的表示形式类型,创建一个新的QLThumbnailGenerator.Request。 可用的表示形式为.icon,.lowQualityThumbnail.thumbnail。 在这种情况下,您需要全部请求并使用最佳选择。 如果使用所有格式的速度太慢,则可以指定一种格式。
  • 2) 使用.shared生成器,您可以启动请求并等待提供缩略图。
  • 3) 如果generator在完成闭包中传递了图像,则将其传递回视图。 如果收到错误,请打印错误消息。 请记住,generator最多可以返回三个不同的图像,因为该请求适用于所有表示形式。

构建并运行:

现在,您将看到每种文件类型的缩略图。 不幸的是,QuickLook.thumb文件一无所知,因此其缩略图为空白。

2. Searching messages printed in the Console

现在,查看“调试”区域中的消息。 每个缩略图有三种可能的表示形式,因此控制台中针对每个显示的文档有三则消息。

在“调试”区域右下角的Filter字段中,键入.html。 您会看到三则消息 —— 一则消息显示缩略图已生成,两则消息显示失败。 这是正常现象,在要求所有表示时您可以忽略这一点。 要求所有表示的好处是您将获得至少一个可以使用的代表。

现在删除.html并输入.pdf。 对于PDF文件,QuickLook生成了两个缩略图。 DocumentThumbnailView显示占位符文档图标,然后使用生成的缩略图对其进行两次更新。 使用所有表示形式的权衡是多次更新视图的额外工作。

替换.pdf并键入.thumb。 尽管QuickLook对缩略文件一无所知,但它至少为您提供了一个缩略图,尽管它是空白的。

现在,花点时间检查框架生成的不同缩略图。 文件zombiethumb.jpghumanthumb.pdfthumbsup.txt生成了文件内容的精美缩略图。 Markdown文件thumbsdown.md的缩略图以文本形式呈现,因为该框架不支持Markdown展示。

thumbsdown.html文件将一个通用的HTML图标作为其缩略图,因为框架设计者认为渲染HTML以生成实际的缩略图效果不佳。

最后,您已经发现,greenthumb.thumb缩略图是空白图像。但是不用担心;您将很快解决该问题。

生成缩略图,给自己一个很大的“竖起大拇指”。接下来做下一个事情。


Defining and Exporting a Document Type

那么,为什么.thumb文件没有缩略图?好吧,第一个问题是系统不知道如何将此文件扩展名映射到文档类型。系统使用Uniform Type Identifiers定义已知的数据格式,同时还允许您定义自己的专有格式,例如.thumb。您可以按照相同的步骤在将来的项目中实现新的文件类型。

1. Defining a Document Type

请按照以下步骤定义新的文档类型:

  • 在项目导航器中,选择顶部的RazeThumb项目图标。
  • Targets下选择RazeThumb target
  • 选择target上方的Info标签。
  • 单击Document Types旁边的显示三角形以展开该部分。
  • 显示时单击+

使用以下详细信息填写新的文档类型:

  • Name设置为Thumb File
  • Types设置为com.raywenderlich.rwthumbfile
  • Handler Rank设置为Owner,因为此应用是该文件类型的所有者。
  • 单击显示Click here to add additional document type properties
  • Key设置为CFBundleTypeRole
  • Type保留为String
  • 输入编辑器的值Value

查看您的设置。 它们应符合以下显示的内容:

2. Exporting a Document Type

既然您已经告诉系统您的应用拥有.thumb文档类型,则需要将其导出,以便其他应用可以识别.thumb文件。 这是执行此操作的步骤:

  • 单击Exported Type Identifiers旁边的显示三角形。
  • 点击显示的+
  • 对于Description,键入Thumb File
  • 对于Identifier,键入com.raywenderlich.rwthumbfile
  • 对于Conforms To,键入public.data,public.content
  • 对于Extensions,键入thumb,这是此新文件类型的文件扩展名。

仔细检查您的设置是否符合以下显示的内容:

构建并运行

在缩略图上查看greenthumb.thumb。 通过添加和导出文档类型信息,您使iOS知道了.thumb文件类型。 默认情况下,QuickLook框架会将应用程序图标(如您在主屏幕上看到的那样)用作缩略图,因为此应用程序拥有该文件类型。 因此,RazeThumb应用程序图标现在显示为系统上所有.thumb文件的缩略图。 再给自己竖起大拇指!


Quick Look Extensions

因此,现在RazeThumb使用QuickLook框架为每个文件提供缩略图,如果您一个一个地点击每个文件,您还将看到每个文件的丰富预览,直到进入greenthumb.thumb为止:

现在该看看如何利用iOS 13中引入的新扩展点来改善.thumb文件的使用体验。

1. Adding a Quick Look preview extension

Quick Look预览扩展程序使您的应用程序可以使用完全自定义的视图控制器替换上述无聊的预览。 RazeThumb不仅可以利用您的扩展名,而且使用已安装的扩展名预览.thumb文件,所有安装的其他应用程序都将受益。

要将扩展添加到项目中,请按照下列步骤操作:

  • 在项目导航器中选择RazeThumb项目。
  • 在列出Targets的窗口底部,单击+图标。
  • Preview中键入Filter
  • 双击Quick Look Preview Extension,并将其命名为ThumbFilePreview
  • 单击Finish
  • 如果出现提示,请不要立即激活新的ThumbFilePreview scheme

RazeThumb应用程序的Sources文件夹中有两个文件,您的新扩展程序将在生成预览时使用这些文件。 您将使用ThumbFile.swift来加载Thumb文件,并使用ThumbFileViewController.swiftThumbFile的实例作为预览呈现。

为确保这些文件也包含在新创建的ThumbFilePreview target中,请按照下列步骤操作:

  • Project navigator中选择ThumbFile.swift
  • 打开Xcode右侧的Inspectors,然后单击文档图标以显示File inspector
  • Target Membership部分下,选中该框以将文件包括在ThumbFilePreviewRazeThumb目标中。
  • 在项目导航器中选择ThumbFileViewController.swift,然后重复上述步骤以再次更Target Membership资格。

在项目导航器中,展开新的ThumbFilePreview组。 Xcode添加了名为PreviewViewController.swift,MainInterface.storyboardInfo.plist的文件。 这些是构成扩展名的文件。

2. Invoking a Quick Look Preview Extension

现在,要正确调用扩展程序,您需要将其与.thumb文件类型相关联。 为此,请按照以下步骤操作:

  • 打开ThumbFilePreview / Info.plist
  • 通过按住Option键单击旁边的显示三角形,展开NSExtension条目及其所有子条目。
  • 将鼠标悬停在QLSupportedContentTypes上,然后单击出现的+,在其下方添加一个项。
  • Item 0设置为String,并为其赋予com.raywenderlich.rwthumbfile值,该值与您之前添加的文档类型标识符匹配。

现在,系统知道何时调用您的扩展程序,您需要自定义扩展程序提供的预览。 打开PreviewViewController.swift并将其内容替换为以下内容:

import UIKit
import QuickLook

// 1
class PreviewViewController: ThumbFileViewController, QLPreviewingController {
  // 2
  enum ThumbFilePreviewError: Error {
    case unableToOpenFile(atURL: URL)
  }

  func preparePreviewOfFile(
    at url: URL,
    completionHandler handler: @escaping (Error?) -> Void
  ) {
    // 3
    guard let thumbFile = ThumbFile(from: url) else {
      handler(ThumbFilePreviewError.unableToOpenFile(atURL: url))
      return
    }

    // 4
    self.thumbFile = thumbFile

    // 5
    handler(nil)
  }
}

这是您在上面的代码中所做的事情:

  • 1) 不是从UIViewController继承,而是从ThumbFileViewController继承,ThumbFileViewController是已经知道如何通过分配thumbFile属性来显示ThumbFile的视图控制器。
  • 2) 声明一个简单的错误类型,该错误类型可以通知QuickLook加载失败。
  • 3) 在preparePreviewOfFile(at:completionHandler :)内部,尝试从指定位置加载ThumbFile,并在失败时返回错误。
  • 4) 更新视图控制器以显示刚刚加载的缩略图。
  • 5) 通过传递nil错误,通知提供的处理程序预览已成功完成加载。

最后,在再次运行该应用程序之前,打开ThumbFilePreview / MainInterface.storyboard并删除默认情况下添加的“Hello World”标签。

构建并运行,然后单击greenthumb.thumb以调用您的扩展程序:

恭喜你!由于上述工作,QuickLook框架可以加载您的扩展名并生成.thumb文件的更好预览。再给自己竖起大拇指!

3. Adding a Thumbnail Extension

现在RazeThumb可以渲染一个thumb文件了,是时候从该渲染中生成缩略图了。为此,您可以通过将缩略图扩展名添加到项目中的方式,类似于添加最后一个扩展名的方式。

QuickLook框架使用缩略图扩展名来创建自定义文件类型的缩略图,然后通过QLThumbnailGenerator将其返回到RazeThumb以及设备上的任何其他应用程序。首先,请按照以下步骤添加下一个扩展名:

  • Project navigator中选择RazeThumb项目。
  • 在列出Targets的窗口底部,单击+图标,然后在Filter中键入Thumbnail
  • 双击Thumbnail Extension,并将其命名为ThumbFileThumbnail
  • 单击Finish
  • 如果出现提示,请不要立即激活新的ThumbFileThumbnail scheme

Project navigator中,展开新的ThumbFileThumbnail组。Xcode已添加ThumbnailProvider.swiftInfo.plist。这些是构成扩展名的文件。

4. Invoking a Thumbnail extension

为了调用Thumbnail扩展名,您需要将此缩略图扩展名与.thumb文件类型相关联,就像使用预览扩展名一样。

  • 打开ThumbFileThumbnail扩展文件夹中的Info.plist文件。
  • 通过按住Option键单击旁边的显示三角形,展开NSExtension条目及其所有子条目。
  • 将鼠标悬停在QLSupportedContentTypes上,然后单击出现的+,在其下方添加一个item
  • Item 0设置为String,并为其赋予com.raywenderlich.rwthumbfile值,该值与您之前添加的文档类型标识符(document type identifier)匹配。
  • 可选,您还可以使用QLThumbnailMinimumDimension为缩略图指定最小尺寸(以点为单位)。如果缩略图在Internet浏览器选项卡中显示为像收藏夹中的小图标,则您可能想使用缓存的缩略图(例如默认的应用程序图标),而不是生成的缩略图(例如markdown文字),因为缩略图可能太复杂而无法识别小尺寸。在此应用中,您将不会使用最小尺寸功能。

ThumbFilePreview target目标一样,ThumbFileThumbnail target也将使用ThumbFile.swiftThumbFileViewController.swift。 按照之前的相同步骤,在Project navigator中选择两个快速文件,并通过在File inspector中选中相应的复选框,将其target memberships更新为包括ThumbFileThumbnail

接下来,打开ThumbnailProvider.swift并将文件内容替换为以下内容:

import UIKit
import QuickLookThumbnailing

class ThumbnailProvider: QLThumbnailProvider {
  // 1
  enum ThumbFileThumbnailError: Error {
    case unableToOpenFile(atURL: URL)
  }

  // 2
  override func provideThumbnail(
    for request: QLFileThumbnailRequest,
    _ handler: @escaping (QLThumbnailReply?, Error?) -> Void
  ) {
    // 3
    guard let thumbFile = ThumbFile(from: request.fileURL) else {
      handler(
        nil, 
        ThumbFileThumbnailError.unableToOpenFile(atURL: request.fileURL))
      return
    }

    // 4
    DispatchQueue.main.async {
      // 5
      let image = ThumbFileViewController.generateThumbnail(
        for: thumbFile,
        size: request.maximumSize)

      // 6
      let reply = QLThumbnailReply(contextSize: request.maximumSize) {
        image.draw(in: CGRect(origin: .zero, size: request.maximumSize))
        return true
      }

      // 7
      handler(reply, nil)
    }
  }
}

这里涉及一些步骤:

  • 1) 定义一个简单的错误类型,如果需要,可以将任何失败描述回QLThumbnailProvider
  • 2) 覆盖ProvideThumbnail(for:_),以便您可以加载ThumbFile并为QuickLookThumbnailing框架呈现其缩略图。
  • 3) 尝试失败时尝试加载ThumbFile并使用错误调用处理程序。
  • 4) QuickLookThumbnailing框架在后台线程上调用此方法。但是为了在屏幕上绘制用户界面,您必须位于主线程中。
  • 5) 使用ThumbFileViewController中的一些现有代码,使用正确的尺寸构造预览的UIImage表示形式。
  • 6) 使用生成的缩略图,创建一个QLThumbnailReply对象,该对象将图像缩略图绘制到提供的上下文中。
  • 7) 使用回复对象和nil错误来调用处理程序,以指示成功。

注意:在此示例中,您调度到主队列,因为ThumbFileViewController.generateThumbnail(for:size :)在内部使用UIGraphicsImageRenderer(需要使用主线程)。在其他情况下,您可能直接使用其他绘图API(例如CoreGraphics),则可能不需要这样做。

构建并运行。现在,您应该会看到greenthumb.thumb的缩略图,看起来像是预览的微型版本。

祝贺您两个伟大的大拇指! RazeThumb应用程序已完成。


Trying Out Your Extensions Using the Files App

现在,您已经为.thumb文件创建了预览扩展名和缩略图扩展名,其他应用程序将可以使用该文件类型。在本部分中,您可以通过向模拟器添加.thumb文件并使用Files应用程序浏览到该文件来表明扩展程序支持其他应用程序。

以下是将缩略图文件添加到模拟器的步骤:

  • 1) 从模拟器中删除RazeThumb。这是因为模拟器会在收到拇指文件后启动RazeThumb,但RazeThumb不会保存文件。将文件复制到模拟器后,您将重新安装该应用程序。
  • 2) 将greenthumb.thumbXcodeFinder拖到模拟器应用程序中。这将打开Files应用程序以导入文件。选择将文件保存在On My iPhone上。
  • 3) 点击Save返回浏览。
  • 4) 如果选择了Recents选项卡,请依次单击BrowseOn My iPhone以查看刚添加的文件。

由于您删除了RazeThumb,因此您将不会看到缩略图,并且如果打开文件,则不会看到预览。

Xcode中,构建并运行RazeThumb以将其重新安装到模拟器上。安装RazeThumb将重新建立缩略图文件和RazeThumb之间的关联。这是因为RazeThumb包括缩略图文件的预览和缩略图扩展名。

在模拟器中打开Files应用程序:

点按On My iPhone,您将看到缩略图文件的渲染缩略图。点击greenthumb.thumb,文件将呈现其预览,就像在RazeThumb应用程序中一样。多么酷啊?


Attaching the Debugger to Your Extension

Quick Look预览扩展和缩略图扩展过程与主应用程序分开运行。它们一起安装时,它们是完全隔离的,这使系统可以在Quick Look框架中有效地使用它们,以在设备上安装的其他应用程序中生成预览和缩略图。

在开发扩展程序时,您可能需要设置一个断点并查看其执行过程,但是由于Xcode调试器只能附加到一个进程一次,因此必须决定是否将调试器附加到您的应用程序或者扩展中。

注意:在撰写本文时,在扩展中设置断点时,Xcode调试器似乎无法正常工作。一些断点似乎会导致运行循环run loop崩溃。其他时候,调试器似乎失去了与扩展的连接。您可能必须尝试几次此练习。

首先在ThumbnailProvider.swift文件中,在ProvideThumbnail(for:_ :)的开头guard let thumbFile = ThumbFile(from: request.fileURL)语句处设置一个断点。

如果您构建并运行RazeThumb并滚动直到greenthumb.thumb可见,您会注意到调试器不会在断点处停止。 不要惊慌。 您可以解决该问题。

单击scheme selector中的RazeThumb以查看带有可用schemes列表的下拉列表。 选择ThumbFileThumbnail作为scheme,然后单击Run。 当提示您选择要启动的应用程序时,选择RazeThumb并滚动直到greenthumb.thumb可见。 这次,调试器应在断点处停止。

为了获得更多乐趣,请重新运行ThumbFileThumbnail scheme,而不是选择Files应用程序以查看您的代码在其他应用程序中运行!

您现在已经了解了如何为受支持的文件类型和自定义文件类型创建Quick Look缩略图和预览。

以下是您在此过程中涉及的一些关键点:

  • 缩略图是文件内容的缩影。
  • QuickLook框架可以为各种标准文件类型(例如图像,音频,视频,文本,PDF等)创建缩略图和预览。
  • 使用扩展名,您可以扩展QuickLook框架以支持您自己的自定义文件类型。
  • 扩展程序允许设备上的所有应用程序访问其功能。
  • 扩展程序与应用程序分开运行。
  • 您可以在开发过程中将Xcode调试器附加到扩展程序。

如果您想了解更多信息,可以探索许多相关主题:

后记

本篇主要讲述了QuickLook预览和缩略图扩展的实现,感兴趣的给个赞或者关注~~~

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

推荐阅读更多精彩内容