马上着手开发 iOS 应用程序 (三) - 构建基本界面

重要:这是针对于正在开发中的API或技术的预备文档(预发布版本)。苹果提供这份文档的目的是帮助你按照文中描述的方式对技术的选择及界面的设计开发进行规划。这些信息有可能发生变化,因此根据本文档的软件开发应当基于最终版本的操作系统和文档进行测试。该文档的新版本或许会随着API或相关技术未来的发展而进行更新。

翻译自苹果官网:

https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/DevelopiOSAppsSwift/Lesson2.html#//apple_ref/doc/uid/TP40015214-CH5-SW1

本教程让你熟悉 Xcode 开发工具。对项目结构有个初步了解,并学习如何使用 Xcode 的一些基本组件来为我们的 app 构建一个简单的界面然后在模拟器中查看它。最后,app 应该像这样:

[图片上传失败...(image-bcde35-1608214722107)]

学习目标:

学完本教程,你将学会:

  • 在 Xcode 中创建新项目
  • 认识 Xcode 项目模板创建的一些重要文件的功能
  • 在项目中打开和切换文件
  • 在模拟器中运行 app
  • 在 storyboard 中添加、移动界面控件并调整它们的尺寸
  • 使用自动布局构建一个自适应用户屏幕尺寸的界面

创建新项目

Xcode 随附了几个内建应用程序模板,可用于开发常见的 iOS 应用程序,如游戏、基于标签浏览的应用程序和基于表格视图的应用程序。这些模板大都预先配置了界面和源代码文件,可作为您进行开发工作的起点。本教程会从最基础的模板开始:Single View Application。

步骤
  1. 从 /Applications 目录打开 Xcode。

    Xcode 欢迎窗口会出现。如果出现的是项目窗口,而不是欢迎窗口,请不要着急;这说明您可能曾在 Xcode 中创建或打开过项目。您只需在接下来的步骤中,使用菜单项来创建项目。

    [图片上传失败...(image-863682-1608214722107)]

  2. 在欢迎窗口中,点按 Create a new Xcode project(或选取 File > New > Project)。
    Xcode 将打开一个新窗口并显示对话框,让您从中选取一个模板。

  3. 在对话框左边的 iOS 部分,选择 Application 。

  4. 在对话框的主区域中,点按 Single View Application,然后点按 Next。

    [图片上传失败...(image-6ae01d-1608214722107)]

  5. 在出现的对话框中,给应用程序命名并选取应用程序的其他选项。

    • Product Name:FoodTracker
      Xcode 会使用您输入的产品名称给您的项目和应用程序命名。
    • Organization Name: 公司或你自己的名字。可以置空。
    • Organization Identifier: 公司标识符(如果有)。如果没有,请使用 com.example。
    • Bundle Identifier: 根据 product name 和 organization identifier 自动生成。
    • Language: Swift
    • Devices: Universal
      Universal app 可以同时运行在 iPhone 和 iPad 两种设备上。
    • Use Core Date: 不选中。
    • Include Unit Tests: 选中。
    • Include UI Tests: 不选中。

    [图片上传失败...(image-b9f1da-1608214722107)]

  6. 点击 Next。

  7. 在出现的对话框中,选取项目的存放位置,然后点按 Create。
    Xcode 会在工作区窗口中打开新项目,窗口的外观如下:

[图片上传失败...(image-dbf5e6-1608214722107)]

在工作区窗口,你可能看到一个三角形警告消息,说“没有代码签名标志”。提醒你没有为 iOS 开发设置 Xcode 证书,但不要担心,本教程并不需要证书。

熟悉 Xcode

Xcode 包括了您创建应用程序时所需的一切。它不仅整理了创建应用程序时所需的文件,还提供了代码和界面编辑器,可让您构建和运行应用程序,并拥有强大的集成调试程序。

请花几分钟时间来熟悉 Xcode 工作区窗口。在接下来的整个教程中,您将会用到下面窗口中标出的区域。不要被所有这些吓倒,当某个区域需要用到时再去详述更多相关信息。

[图片上传失败...(image-322788-1608214722107)]

运行模拟器

由于项目是基于 Xcode 模板创建的,因此基本的应用程序环境已经自动为您设置好了。即使没有编写任何代码,也可以构建和运行 Single View Application 模板,而无需进行任何额外的配置。

构建和运行您的 app,可以使用 Xcode 自带的 iOS 模拟器。顾名思义,iOS 模拟器可模拟在 iOS 设备上运行 app,让您初步了解它的外观和行为。

它可模拟多种不同类型的硬件,包括屏幕大小不同的 iPad、iPhone 等等。因此,您可以模拟在任何一款开发目标设备上运行 app。在本教程中,我们选择使用“iPhone 6”。

在 iOS 模拟器中运行 app
  1. 从 Xcode 工具栏的 Scheme 弹出菜单中选取 iPhone 6。

    在 Scheme 弹出菜单中可以选择你喜欢的 Simulator 或 Device 来运行你的 app,但请确保选择了 iPhone 6 Simulator,而不是 iOS Device。

    [图片上传失败...(image-51f003-1608214722107)]

  2. 点按 Xcode 工具栏左上角的 Run 按钮。

    [图片上传失败...(image-6894bb-1608214722107)]

    或者可以选取 Product > Run(或按下 Command-R)。

    如果是首次运行 app,Xcode 会询问您是否要在 Mac 上启用开发者模式。开发者模式可让 Xcode 访问特定的调试功能,无需每次都输入密码。请决定是否要启用开发者模式,然后按照提示操作。

    [图片上传失败...(image-fbbdc4-1608214722107)]

    如果选择不启用,可能稍后会要求您输入密码。本课程假定已启用了开发者模式。

  3. 构建过程完成后,请看 Xcode 工具栏。

    Xcode 会在工具栏中间的活动显示窗口中显示有关构建过程的消息。

Xcode 完成项目生成后,模拟器会自动启动。首次启动时可能需要几分钟时间。

模拟器以你指定的 iPhone 模式开启。在模拟的 iPhone 屏幕上,模拟器启动了你的 app。在 app 完成启动前,你会短暂看到一个带有 app 名字 FoodTracker 的启动界面。

[图片上传失败...(image-89ca76-1608214722108)]

之后会看到这样的界面

[图片上传失败...(image-6c1ae0-1608214722108)]

一如其名,Single View Application 模板并未包括过多的代码,仅会显示一个白色的屏幕。其他模板会有更多复杂的行为,因此在扩展模板制作自己的 app 之前,先要弄清楚模板的用处,这一点很重要。而要做到这一点,一个很好的方式,就是先不做任何修改,直接运行模板。

请选取 Simulator > Quit Simulator(或按下 Command-Q)来退出模拟器。

检查源代码

Single View Application 模板附带了少量现成的源代码来设置 app 环境,首先,让我们看下 AppDelegate.swift 文件。

查看 AppDelegate.swift 源文件
  1. 确保在导航区域打开了项目导航。
    项目导航会显示项目中的所有文件。如果项目导航未打开,请点按导航选择栏最左边的按钮。(或者选择 View -> Navigators -> Show Project Navigator。)

    [图片上传失败...(image-ca7c47-1608214722108)]

  2. 如果需要,点击项目导航旁边的提示三角打开 FoodTracker 文件夹。

  3. 选择 AppDelegate.swift。
    Xcode 会在主编辑器窗口区域打开源文件。

    [图片上传失败...(image-e1c5cd-1608214722108)]

    或者,双击 AppDelegate.swift 文件在单独的窗口中打开它。

App Delegate 源文件

AppDelegate.swift 源文件有两个主要功能:

  • 为你的 app 创建入口以及一个 run loop 来传递事件。这项工作由文件的顶部 UIApplicationMain 属性完成(@UIApplicationMain)。UIApplicationMain 创建一个 application 对象负责管理 app 和 app delegate 对象的生命周期。
  • 定义 AppDelegate 类,它创建 window 供你的 app 的内容进行渲染,并响应 app 状态转换。 AppDelegate 类主要用于写自定义系统级代码的。

AppDelegate 类包含一个属性:window。它使用这个属性来保持窗口跟踪并在窗口中绘制 app 的内容。window 属性是可选的,意味着有时候它是空的。

var window: UIWindow?

AppDelegate 类也包含一些重要模板方法的实现。这些预定义的方法允许 application 对象与 app delegate 交流。

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
func applicationWillResignActive(application: UIApplication)
func applicationDidEnterBackground(application: UIApplication)
func applicationWillEnterForeground(application: UIApplication)
func applicationDidBecomeActive(application: UIApplication)
func applicationWillTerminate(application: UIApplication)

在 app 状态变化过程中 - 例如,app 启动状态,过渡到后台状态,和 app 终止状态 - application 对象会调用 app delegate 中相应的方法,给它一个机会来做合适的响应。你不需要做任何特别的事情来确保这些方法在正确时间被调用 - application 对象为你做了这部分工作了。

这些自动实现的方法中的每一个都有默认的功能。如果让模板实现为空或者从 AppDelegate 类中删除它,当方法被调用时你会得到默认的功能。添加自定义代码到这些模板方法,当方法被调用了代码相应会执行。在本课中,你不会使用任何自定义 app delegate 代码,所以你不需要对 AppDelegate.swift 文件做任何修改。

View Controller 源文件

Single View Application 模板生成了另外一个源文件: ViewController.swift 文件。在项目导航中选择 ViewController.swift 查看。

[图片上传失败...(image-87e108-1608214722108)]

文件定义了 UIViewController 的子类 ViewController。此刻,这个类简单继承 UIViewController 定义的所有功能。为了覆写或扩展这些功能,覆写 UIViewController 定义的方法(就像你看到的 ViewController.swift 文件中的 viewDidLoad() 和 didReceiveMemoryWarning() 方法),或者实现自定义的方法。

尽管模板为你生成了 didReceiveMemoryWarning() 方法,在本教程并不会用到,所以删掉它吧。

现在,ViewController.swift 代码应该像这样:

import UIKit
 
class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
    
}

稍后会在这个文件中写代码。

打开 Storyboard

准备好使用 storyboard 工作吧。它是 app 用户界面的可视化描述,显示界面的内容和界面间的跳转。当设计界面的时候可以实时看到效果 - 所见即所得。

打开 storyboard
  • 在项目导航中,选择 Main.storyboard。
    Xcode 会在编辑区的可视化界面编辑器 Interface Builder 中打开 storyboard。storyboard 的背景就是画板(canvas)。使用画板来添加和排列 UI 控件。

storyboard 应该类似这样:

[图片上传失败...(image-d8a038-1608214722108)]

至此,app 的 storyboard 中包含了一个场景(scene),代表 app 满屏的内容。在场景的左边有个箭头表示这个场景是 storyboard 的入口,意味着当 app 启动的时候这个场景最先加载。现在,你看到场景仅仅包含单一控制器管理的视图。不久你会学到更多关于视图和视图控制器的用处。

在 iPhone 6 模拟器中运行你的 app,会在设备屏幕上看到场景中的视图。但是在画板中查看场景时,你会注意到它的尺寸并不是 iPhone 6 屏幕那么大。因为画板中的场景是界面的广义描绘,它可以被用于表示任何方向的任何设备。使用它创建一个自适应界面,该界面会自动调节大小,以便能适配当前设备和方向。

构建基本 UI

是时候构建基本界面了。开始为场景做一个 UI 让你添加一份新的食物到你的食物跟踪 app,FoodTracker。

Xcode 提供了可以添加到 storyboard 中的对象库。有些如 button 和 text fields 等能显示在界面中。其他的,如视图控制器和手势,主要用于定义 app 的功能而不是显示在屏幕上。

在 UI 中显示的控件被称为视图,视图向用户展示内容。它们是构建你的 UI 的基本模块,它们用一种清晰优雅的方式展示你的内容,视图有一些很有用内置行为,包括在屏幕上显示和响应用户的输入。

iOS 中所有视图对象都是 UIView 或它的一个子类。许多 UIView 的子类有高度定制的外观和功能。通过往你的场景中添加 UITextField,一个UIView 的子类。它让用户输入单行文本来作为食物的名字。

往场景中添加文本框
  1. 打开对象库。
    对象库在 Xcode 右边的实用工具区的底部。如果没有看到它,点击库选择栏左边的第三个按钮(或者选择 View > Utilities > Show Object Library)

    [图片上传失败...(image-651abe-1608214722108)]

    出现一个列表显示每个对象的名字、描述和外观。

  2. 在对象库的搜索框中输入 text field 快速找到 Text Field对象。

  3. 从对象库中拖动 Text Field 对象到场景中。

    [图片上传失败...(image-28bf08-1608214722108)]

    如果需要,选择 Editor > Canvas > Zoom 来缩小画板尺寸。

  4. 拖动文本框使它位于场景的前半部分,并与场景的左边缘对齐。
    当达到下图效果停止拖动。

    [图片上传失败...(image-ce4e5-1608214722108)]

    蓝色的布局引导线帮助你放置文本框。当你拖动文本框接近另一个对象或调整它的尺寸时布局引导线可见;而放开文本框线就消失了;

  5. 如果需要,点击文本框激活 resize handles。
    resize handles 是在控件边缘出现的小白色方块,通过拖动它来调整 UI 控件的尺寸。选中对象激活控件的 resize handles。如果文本框像下面这样,说明已经为调整大小做好准备了;如果没有,请在画板中选中它。

    [图片上传失败...(image-1f45a7-1608214722108)]

  6. 调整文本框左右边距直到看到三个垂直布局引导线;左边缘对齐、水平居中对齐和右边缘对齐。

    [图片上传失败...(image-ddbc29-1608214722108)]

尽管在场景中有文本框,但并没有指引用户在文本框中输入什么。使用文本框的 placeholder 属性来提示用户输入美食的名字。

配置文本框的 placeholder 文本
  1. 选中文本框,在实用工具区打开属性检查器。
    当你点击检查器选择栏第四个按钮打开属性检查器。它能让你修改 storyboard 中对象的属性。

    [图片上传失败...(image-6b2049-1608214722108)]

  2. 在属性检查器中,找到名为 Placeholder 的区域输入 Enter meal name。

  3. 回车确认,会在文本框中显示新的 placeholder 文本。

你的场景应该如下:

[图片上传失败...(image-a9e55b-1608214722108)]

当修改文本框属性的时候,还可以修改选中文本框时弹出的系统键盘的属性。

配置文本框的键盘
  1. 确保文本框仍然是选中状态。
  2. 在属性检查器中,找到名为 Reture Key 的区域并选择 Done。
    把键盘默认的 Return 键改成 Done 键,意思更加明白。
  3. 在属性检查器中,选中 Auto-enable Return Key 复选框。
    这会让用户在文本框输入文本之前不可点击完成按钮,确保用户永远不会输入一个空的字符串作为食物的名字。

下一步,在场景的顶部添加一个标签(UILabel)。标签不可交互;主要用于在界面中显示静态的文本。为了帮助你更好的理解 UI 控件间怎么交互的,请配置标签来显示用户在文本框中输入的文字。这是很好的方式来测试用户输入的文字及对文字进行适当的处理。

往场景中添加标签
  1. 在对象库的搜索框中输入 label 快速找到标签。
  2. 从对象库中拖动标签对象放到你的场景中。
  3. 拖动标签使其在文本框上方并与场景左边缘对齐。
    当场景像下面这样停止拖动:

[图片上传失败...(image-90e22d-1608214722108)]

  1. 双击标签并输入 Meal Name。
  2. 回车确认标签显示新的文字。

你的场景应该像这样:

[图片上传失败...(image-ab12-1608214722108)]

往界面中添加按钮(UIButton)。按钮可交互,所以用户可以点击它来触发你定义的一个 action。之后,你会创建一个重置标签文本为默认值的动作。

添加按钮到你的场景中
  1. 在对象库的搜索框中输入 buton 快速找到 Button 对象。

  2. 从对象库往你的场景中拖动按钮。

  3. 拖动以使其在文本框下方并与场景的左边缘对齐。
    当达到如下效果停止拖动:

    [图片上传失败...(image-92f072-1608214722108)]

  4. 双击按钮输入 Set Default Label Text。

  5. 回车确认在按钮中显示了新的文字。

现在,你的场景应该像这样:

[图片上传失败...(image-2ecb03-1608214722108)]

查看大纲视图中所有已经添加到你的场景的界面对象,这样就能更好的理解这些对象如何排列了。

查看大纲视图

  1. 在 storyboard 中,找到 outline view toggle。

[图片上传失败...(image-e31927-1608214722108)]

  1. 如果大纲视图被折叠了,点击切换(toggle)按钮来展开它。
    你可以使用 outline view toggle 来折叠和展开大纲视图。

画板左边的大纲视图能让你看到 storyboard 中对象分层表示。你应该看到了刚才添加的文本框、标签和按钮了,但是为什么这些 UI 对象要嵌套在另一个视图中?

视图不止用于屏幕显示和响应用户输入,也可以当做其他视图的容器。视图放在一个叫做 view hierarchy 的视图层次结构中。这个层次结构定义了视图间的布局关系。在层次中有父视图和子视图。一个视图可以拥有多个子视图但是只能拥有一个父视图。

[图片上传失败...(image-5d73a-1608214722108)]

通常每个场景都有自己的视图层次结构。在视图层次的顶层是个内容视图。在当前场景中,这个内容视图是视图控制器的顶层视图。而文本框、标签和按钮是它的子视图。你放置在场景中所有其他视图都是内容视图的子视图(尽管它们自己可以有嵌套的子视图)

预览界面

定期预览 app 来检查它是否按照你期望的目标来开发。通过使用辅助编辑器来预览 app 的界面,它是显示在主编辑器旁边的第二个编辑器。

预览界面
  1. 点击 Xcode 右上角工具栏中的 Assistant 按钮来打开辅助编辑器。
    [图片上传失败...(image-2f18e4-1608214722108)]

  2. 点击 Xcode 工具栏中 Navigator 和 Utilities 按钮来收缩项目导航和实用工具区,这样就有更多的工作空间了。

    [图片上传失败...(image-5014bb-1608214722108)]

    同样可以收缩大纲视图。

  3. 在辅助编辑器顶部的编辑器选择栏中,将辅助编辑器从 Automatic 切换为 Preview > Main.storyboard。

[图片上传失败...(image-2dc8a2-1608214722108)]

正如在辅助编辑器中看到的那样,文本框看起来不太正确。它超过了屏幕的边界了。在故事板中它看起来是正确的,但为什么在 iPhone 预览中会出现这种问题呢?

[图片上传失败...(image-25aa88-1608214722108)]

这时候需要为不同尺寸的 iPhone 和 ipad 屏幕构建一个自适应界面。例如,当界面缩小到 iPhone 屏幕大小,文本框应该收缩。当界面变成 ipad 屏幕大小,文本框应该更长。您可以使用自动布局来指定这些规则。

使用自动布局(Auto Layout)

自动布局是一个功能强大的布局引擎,它可以帮助您设计自适应界面。向它表达你的意图来描述场景中对象的位置,然后让布局引擎决定如何最好地实现这一意图。使用约束规则来描述你的意图,说明一个对象相对于另一个的位置,它应该是什么大小,当布局空间减少了这两个对象应该收缩。

与自动布局相结合,有一个很有用的工具就是堆栈视图(UIStackView)。用于在一个列或行中放置布局对象的集合。它能充分利用自动布局的力量,创建动态适应设备方向、屏幕大小和可用空间变化的用户界面。

您可以轻松地将现有的界面加入堆栈视图中,添加必要的约束,以使堆栈视图在不同的情况下显示正确。

为食物场景添加自动布局约束
  1. 点击 Standard 按钮返回标准编辑器。

    [图片上传失败...(image-152ed9-1608214722108)]

    点击 Navigator 和 Utilities 按钮展开项目导航和实用工具区。

  2. 按住键盘的 Shift 键,选中文本框、标签和按钮。

    [图片上传失败...(image-4c1fc5-1608214722108)]

  3. 在画板的底部,单击 Stack 按钮。(或者选择 Editor > Embed In > Stack View)。

    [图片上传失败...(image-9b9672-1608214722108)]

    Xcode 将 UI 控件堆叠在堆栈视图中并分析布局算出所有项应该垂直而不是水平堆叠。

    [图片上传失败...(image-448db6-1608214722108)]

  4. 打开大纲视图。选择 Stack View 对象。

    [图片上传失败...(image-e5096d-1608214722108)]

  5. 在属性检查器的 Spacing field 中输入 12。回车确认。
    你会注意到垂直方向拉开了一些距离,以及与它们一起变长的堆栈视图

    [图片上传失败...(image-561741-1608214722108)]

  6. 打开画板右下角的 Pin 菜单。

    [图片上传失败...(image-d23382-1608214722108)]

  7. 点击菜单中Spacing to nearest neighbor上面的两个横向约束和垂直 top 约束来选中它们。当约束被选中就会变红。

    [图片上传失败...(image-7883c2-1608214722108)]

    这些约束表明间距最近的 leading、trailing、以及 top 边界的间距。术语 "nearest neighbor" 意思是最近的 UI 对象的边界,UI 对象可以是父视图或同级视图。因为 “Constrain to margins” 复选框被选中,堆栈视图将与它父视图的左、右和顶部边界有额外8个点的间距。

  8. 在左、右框中输入0,在顶部框中输入60的间隙。

  9. 在 Update Frames 旁边的弹出菜单中,选择 Items of New Constraints 。
    Pin menu 应该像这样:

    [图片上传失败...(image-1e8876-1608214722108)]

  10. 在 Pin 菜单中,点击 Add 3 Constraints 按钮。

    [图片上传失败...(image-34c9cd-1608214722108)]

最后的场景 UI 应该像这样:

[图片上传失败...(image-a56c1c-1608214722108)]

你会注意到,文本框并没有像之前那样超过场景的边界了。

调整场景文本框的宽度
  1. 选择 storyboard 中食物场景的文本框。

  2. 打开画板右下角的 Pin 菜单。

    [图片上传失败...(image-6d5a0a-1608214722108)]

  3. 点击Spacing to nearest neighbor上面的两个水平约束来选中它们。当选中后它们变成红色。

  4. 在左右框中输入0。

  5. 在 Update Frames 旁边的弹出框中,选择 Items of New Constraints。
    Pin 菜单应该像这样:

    [图片上传失败...(image-1b8efd-1608214722108)]

  6. 在 Pin 菜单中,点击 Add 2 Constraints 按钮。

    [图片上传失败...(image-a7c33e-1608214722108)]

  7. 选中文本框,在实用工具区打开尺寸检查器。
    点击检查器栏第五个按钮打开尺寸检查器。它能让你编辑 storyboard 中控件的尺寸和位置。

    [图片上传失败...(image-8c1581-1608214722108)]

  8. 在 Intrinsic Size 区域,选择 Placeholder(这个区域在尺寸检查器的底部,所以你需要滚动才能看到它。)。
    文本框的尺寸基于它的内容,而内容定义了它的固有内容尺寸(intrinsic content size)。固有内容尺寸是指显示视图所有内容所需的最小尺寸。现在,文本框的内容仅是其占位字符串,但是用户实际输入的内容有可能比它长。

    食物场景界面应该像这样:

    [图片上传失败...(image-2bc54e-1608214722108)]

检验:在模拟器中运行你的 app。文本框不再超过屏幕的边界了。点击文本框并使用键盘输入一些文本(通过按 Command-K 切换软键盘)。如果旋转设备( Command-左箭头 或 Command-右箭头)或在不同设备上运行 app,文本框会按照设备方向和屏幕尺寸缩放到合适的大小。注意,在横屏方向上状态栏消失了。

[图片上传失败...(image-9cd6dc-1608214722108)]

如果没有得到你期望的效果,请使用自动布局调试功能。单击 Resolve Auto Layout Issues 图标,并选择 Reset to Suggested Constraints 让 Xcode 使用默认的约束来更新界面。或单击 Resolve Auto Layout Issues 图标,并选择 Clear Constraints 来删除界面对象的所有约束,然后再一次按照之前的步骤来设置约束。

[图片上传失败...(image-80700c-1608214722108)]

注意:

为了看到本课的完整示例项目,下载文件并在 Xcode 中查看它。

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

推荐阅读更多精彩内容