使用 Swift 創(chuàng)建簡(jiǎn)單的二維碼掃描應(yīng)用

作者:AppCoda棍掐,原文鏈接阵赠,原文日期:2016-05-19
譯者:Prayer;校對(duì):numbbbbb鹤啡;定稿:CMB

排著長(zhǎng)隊(duì)等待結(jié)賬的商店惯驼,幫助旅客記錄包裹和航班信息的機(jī)場(chǎng),幫助大型零售商處理大量無(wú)聊的存貨清單递瑰,這些場(chǎng)景非常適合使用條碼掃描器祟牲。此外,條碼掃描器也能幫消費(fèi)者進(jìn)行智能購(gòu)物和產(chǎn)品分類抖部。既然它這么棒说贝,不如我們?cè)?iPhone 上做一個(gè)吧!

幸運(yùn)的是慎颗,對(duì) Apple 開(kāi)發(fā)者來(lái)說(shuō)乡恕,實(shí)現(xiàn)條碼掃描非常容易,蘋(píng)果大法好俯萎!我們會(huì)使用 AV Foundation 來(lái)實(shí)現(xiàn)一個(gè)小巧的 iPhone app傲宜,能夠掃描 CD 上的條碼,獲取專輯的一些重要信息夫啊,并將內(nèi)容輸出到 App 視圖中函卒。能夠?qū)崿F(xiàn)讀取條碼的功能,這非常的酷撇眯,但是我們的野心不止于此报嵌,我們會(huì)對(duì)識(shí)別的條碼內(nèi)容作進(jìn)一步的操作。

我本不該再多啰嗦熊榛,不過(guò)還是友情提醒一下锚国,這個(gè)條碼掃描 app 只有在設(shè)備具有攝像頭時(shí)才能正確工作。記住這一點(diǎn)来候,準(zhǔn)備一臺(tái)有攝像頭的 iOS 設(shè)備跷叉,我們開(kāi)始吧逸雹!

關(guān)于 CDBarcodes

今天我們創(chuàng)建的應(yīng)用叫做 CDBarcodes —— 它還是很智能的营搅。當(dāng)設(shè)備掃描到一個(gè)條碼時(shí)云挟,我們會(huì)將處理后的條碼內(nèi)容發(fā)送給 Discogs 數(shù)據(jù)庫(kù),然后獲得專輯的名稱转质、藝術(shù)家以及發(fā)布年份园欣。Discogs 的數(shù)據(jù)庫(kù)中有大量的音樂(lè)數(shù)據(jù),所以我們基本上能查到所有數(shù)據(jù)休蟹。

從這里下載 CDBarcodes 的 starter project

Discogs

先從 Discogs 開(kāi)始沸枯。首先,我們需要登錄或者注冊(cè)一個(gè) Discogs 賬戶赂弓。登錄之后绑榴,拉到網(wǎng)站的最底端,在 footer 的最左邊邊欄盈魁,點(diǎn)擊 API翔怎。

在 Discogs API 頁(yè)面,點(diǎn)擊左邊欄 Database 中的 Search杨耙。

這個(gè)就是我們將會(huì)用到的 API赤套。我們使用 “title” 和 “year” 參數(shù)來(lái)獲取專輯信息。

現(xiàn)在我們需要將查詢的 URL 保存到我們的 CDBarcodes 中珊膜。在 Constants.swift 文件中容握,將 https://api.discogs.com/database/search?q= 添加到常量 DISCOGS_AUTH_URL 中。

let DISCOGS_AUTH_URL = "https://api.discogs.com/database/search?q="

現(xiàn)在我們可以很方便地在應(yīng)用中使用 DISCOGS_AUTH_URL 獲取查詢 URL车柠。

回到剛才的 Discogs API 網(wǎng)站剔氏。我們需要?jiǎng)?chuàng)建一個(gè)新應(yīng)用,取得 API 的使用資格竹祷。在導(dǎo)航欄中介蛉,網(wǎng)頁(yè)的最頂部,點(diǎn)擊 Create an App溶褪。之后點(diǎn)擊 Create an Application 按鈕币旧。

應(yīng)用名稱的話,輸入 “CDBarcodes + 你的名字”猿妈,或者其他你喜歡的名字吹菱。description 字段可以寫(xiě):

“This is an iOS app that reads barcodes from CDs and displays information about the albums.”

譯注:“這個(gè) iOS 應(yīng)用會(huì)讀取 CD 的條形碼并顯示唱片信息∨碓颍”

最后鳍刷,點(diǎn)擊 Create Application 按鈕。

在最后的結(jié)果頁(yè)面俯抖,我們能夠得到使用條碼來(lái)做一些操作的資格信息输瓜。

拷貝 Consumer Key,粘貼到 Constants.swift 文件的 DISCOGS_KEY 中。再拷貝 Consumer Secret尤揣,粘貼到 Constants.swift 文件的 DISCOGS_SECRET 中搔啊。

同 URL 一樣,現(xiàn)在我們可以在應(yīng)用中很方便地使用這些變量了北戏。

CocoaPods

為了能夠和 Discogs API 通信负芋,我們使用一個(gè)優(yōu)秀的第三方庫(kù)管理工具:CocoaPods。如果想要了解更多關(guān)于 CocoaPods 的信息嗜愈,或者想學(xué)習(xí)如何安裝它旧蛾,可以到它的官網(wǎng)查詢。

有了 CocoaPods 就可以安裝第三方庫(kù)蠕嫁,我們會(huì)使用 Alamofire 來(lái)請(qǐng)求網(wǎng)絡(luò)锨天,使用 SwiftyJSON 來(lái)處理從 Discogs 返回的 JSON 數(shù)據(jù)。

下面我們把這兩個(gè)庫(kù)引入到 CDBarcodes 工程中剃毒!

CocoaPods 安裝好之后绍绘,打開(kāi)終端,進(jìn)入 CDBarcodes 目錄迟赃,初始化 CocoaPods陪拘,命令如下:

bash
cd <your-xcode-project-directory>
pod init

使用 Xcode 打開(kāi) Podfile:

bash
open -a Xcode Podfile

將下面內(nèi)容拷貝到 Podfile 中:

ruby
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!

pod 'Alamofire', '~> 3.0'

target ‘CDBarcodes’ do
pod 'SwiftyJSON', :git => 'https://github.com/SwiftyJSON/SwiftyJSON.git'
end

最后,使用下面的命令來(lái)下載 Alamofire 和 SwiftyJSON:

bash
pod install

現(xiàn)在讓我們回到 Xcode 中纤壁!切記要打開(kāi)的是 CDBarcodes.xcworkspace

識(shí)別條碼

AV Foundation 框架提供了識(shí)別條碼的工具左刽。我們來(lái)大概描述一下工作原理。

  • AVCaptureSession 會(huì)管理從攝像頭獲取的數(shù)據(jù)——將輸入的數(shù)據(jù)轉(zhuǎn)為可以使用的輸出
  • AVCaptureDevice 表示物理設(shè)備和其他屬性酌媒。AVCaptureSession 會(huì)從 AVCaptureDevice 獲取輸入數(shù)據(jù)
  • AVCaptureDeviceInput 從設(shè)備中捕獲數(shù)據(jù)
  • AVCaptureMetadataOutput 會(huì)向處理數(shù)據(jù)的 delegate 轉(zhuǎn)發(fā)獲得的元數(shù)據(jù)

BarcodeReaderViewController.swift 文件中欠痴,首先導(dǎo)入 AVFoundation

import UIKit
import AVFoundation

同時(shí),我們需要遵循 AVCaptureMetadataOutputObjectsDelegate 協(xié)議秒咨。

viewDidLoad() 中喇辽,我們要發(fā)動(dòng)條碼掃描引擎。

首先雨席,創(chuàng)建一個(gè) AVCaptureSession 對(duì)象菩咨,然后設(shè)置 AVCaptureDevice。之后我們將創(chuàng)建一個(gè)輸入對(duì)象(input object)陡厘,然后將其加入到 AVCaptureSession 中抽米。

class BarcodeReaderViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {

var session: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 創(chuàng)建一個(gè) session 對(duì)象
    session = AVCaptureSession()
    
    // 設(shè)置 captureDevice.
    let videoCaptureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
    
    // 創(chuàng)建 input object.
    let videoInput: AVCaptureDeviceInput?
    
    do {
        videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
    } catch {
        return
    }
    
    // 將 input 加入到 session 中
    if (session.canAddInput(videoInput)) {
        session.addInput(videoInput)
    } else {
        scanningNotPossible()
    }

如果你的設(shè)備沒(méi)有攝像頭,那就無(wú)法掃描條碼糙置。我們添加了一個(gè)處理失敗場(chǎng)景的方法云茸。如果沒(méi)有攝像頭,會(huì)彈出一個(gè)提示框來(lái)提示用戶谤饭,換一個(gè)有攝像頭的設(shè)備來(lái)掃描 CD 的條碼标捺。

func scanningNotPossible() {
    // 告知用戶該設(shè)備無(wú)法進(jìn)行條碼掃描
    let alert = UIAlertController(title: "Can't Scan.", message: "Let's try a device equipped with a camera.", preferredStyle: .Alert)
    alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
    presentViewController(alert, animated: true, completion: nil)
    session = nil
}

回到 viewDidLoad() 方法中懊纳,將 input 添加到 session 之后,我們需要?jiǎng)?chuàng)建 AVCaptureMetadataOutput 并把它也添加到 session 中亡容。我們會(huì)將捕獲到的數(shù)據(jù)通過(guò)串行隊(duì)列發(fā)送給 delegate 對(duì)象嗤疯。

下一步需要聲明我們將要掃描的條碼類型。對(duì)我們而言萍倡,我們需要使用 EAN-13 條碼身弊。有意思的是辟汰,我們掃描的條碼并非都是 EAN-13 類型的列敲;一些有可能是 UPC-A 類型,這可能會(huì)造成識(shí)別的問(wèn)題帖汞。

Apple 通過(guò)在前面加上 0 來(lái)將 UPC-A 條碼轉(zhuǎn)換為 EAN-13 條碼戴而。UPC-A 條碼只有 12 位,EAN-13 條碼翩蘸,和你猜測(cè)的一樣所意,是 13 位。這個(gè)自動(dòng)轉(zhuǎn)化特性的好處是催首,我們?cè)谠O(shè)置 metadataObjectTypes 時(shí)扶踊,只要設(shè)置為 AVMetadataObjectTypeEAN13Code,EAN-13 和 UPC-A 條碼都將會(huì)被識(shí)別郎任。不過(guò)這會(huì)修改條碼秧耗,因此有可能會(huì)在查詢 Discogs 時(shí)出問(wèn)題,后面我們會(huì)處理這個(gè)問(wèn)題舶治。

如果攝像頭有問(wèn)題分井,我們需要使用 scanningNotPossible() 來(lái)告知用戶。

// 創(chuàng)建 output 對(duì)象
let metadataOutput = AVCaptureMetadataOutput()

// 將 output 對(duì)象添加到 session 上
if (session.canAddOutput(metadataOutput)) {
   session.addOutput(metadataOutput)
   
   // 通過(guò)串行隊(duì)列霉猛,將捕獲到的數(shù)據(jù)發(fā)送給相應(yīng)的代理
   metadataOutput.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
   
   // 設(shè)置可掃描的條碼類型
   metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeEAN13Code]
   
} else {
   scanningNotPossible()
}

我們已經(jīng)擁有了掃描條碼的強(qiáng)大能力尺锚,現(xiàn)在需要做的是預(yù)覽掃描畫(huà)面。使用 AVCaptureVideoPreviewLayer 在整個(gè)屏幕上顯示拍攝到的畫(huà)面惜浅。

然后瘫辩,我們就可以開(kāi)始掃描了。

// 添加 previewLayer 讓其顯示攝像頭拍到的畫(huà)面
?        
previewLayer = AVCaptureVideoPreviewLayer(session: session);
previewLayer.frame = view.layer.bounds;
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
view.layer.addSublayer(previewLayer);
    
// 開(kāi)始運(yùn)行 session
    
session.startRunning()

captureOutput:didOutputMetadataObjects:fromConnection 方法中坛悉,我們可以慶祝一下杭朱,因?yàn)閳?zhí)行到該方法就說(shuō)明已經(jīng)識(shí)別了一些信息。

首先吹散,我們需要從 metadataObjects 數(shù)組中取出第一個(gè)對(duì)象弧械,然后將其轉(zhuǎn)化為機(jī)器可以識(shí)別的格式。然后將轉(zhuǎn)換后的 readableCode 作為一個(gè) string 值傳入 barcodeDetected() 方法中空民。

在看 barcodeDetected() 方法之前刃唐,我們需要以震動(dòng)的形式給用戶一些掃描成功的反饋并且關(guān)閉 session(stop the session)羞迷。萬(wàn)一你忘記關(guān)閉了 session,沒(méi)關(guān)系画饥,你的設(shè)備會(huì)一直震動(dòng)衔瓮,直到你關(guān)閉為止。

func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
 
    // 從 metadataObjects 數(shù)組中取得第一個(gè)對(duì)象
    if let barcodeData = metadataObjects.first {
        // 將其轉(zhuǎn)化為機(jī)器可以識(shí)別的格式
        let barcodeReadable = barcodeData as? AVMetadataMachineReadableCodeObject;
        if let readableCode = barcodeReadable {
            // 將 readableCode 作為一個(gè) string 值抖甘,傳入 barcodeDetected() 方法中
            barcodeDetected(readableCode.stringValue);
        }
        
        // 以震動(dòng)的形式告知用戶热鞍,識(shí)別成功        AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
        
        // 關(guān)閉 session (避免你的設(shè)備一直嗡嗡震動(dòng))
        session.stopRunning()
    }
}

我們需要在 barcodeDetected() 中做一些操作。第一個(gè)任務(wù)是彈出一個(gè)提示框告知用戶衔彻,我們掃描到了一個(gè)條碼薇宠。然后將掃描到的信息轉(zhuǎn)化為我們需要的內(nèi)容。

必須去掉掃描內(nèi)容中的空格艰额。去掉空格之后澄港,我們需要判斷條碼是 EAN-13 還是 UPC-A 類型。如果是 EAN-13 類型柄沮,不需要額外的操作回梧。如果是 UPC-A 條碼,它被轉(zhuǎn)化為了 EAN-13 類型祖搓,我們需要把它還原成原有的格式狱意。

就像我們之前討論的那樣,蘋(píng)果在 UPC-A 條碼的前頭加上一個(gè) 0 來(lái)將其轉(zhuǎn)換為 EAN-13拯欧,所以我們需要判斷其是否以 0 開(kāi)頭详囤,如果是的話,刪掉它哈扮。如果沒(méi)有這一步纬纪,Discogs 無(wú)法識(shí)別這個(gè)數(shù)字,我們也沒(méi)有辦法得到正確的數(shù)據(jù)滑肉。

拿到處理后的條碼數(shù)據(jù)之后包各,我們將它傳給 DataService.searchAPI() 然后顯示 BarcodeReaderViewController

func barcodeDetected(code: String) {
 
    // 讓用戶知道,我們掃描到了
    let alert = UIAlertController(title: "Found a Barcode!", message: code, preferredStyle: UIAlertControllerStyle.Alert)
    alert.addAction(UIAlertAction(title: "Search", style: UIAlertActionStyle.Destructive, handler: { action in
        
        // 去除空格
        let trimmedCode = code.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
        
        // 判斷是 EAN 還是 UPC?
        
        let trimmedCodeString = "\(trimmedCode)"
        var trimmedCodeNoZero: String
        
        if trimmedCodeString.hasPrefix("0") && trimmedCodeString.characters.count > 1 {
            trimmedCodeNoZero = String(trimmedCodeString.characters.dropFirst())
            
            // Send the doctored UPC to DataService.searchAPI()
            DataService.searchAPI(trimmedCodeNoZero)
        } else {
            
            // Send the doctored EAN to DataService.searchAPI()
            DataService.searchAPI(trimmedCodeString)
        }
        
        self.navigationController?.popViewControllerAnimated(true)
    }))
    
    self.presentViewController(alert, animated: true, completion: nil)
}

查看 BarcodeReaderViewController.swift 之前靶庙,我們?cè)?viewDidLoad() 后面添加 viewWillAppear()viewWillDisappear()问畅。在 viewWillAppear() 方法中,我們讓 session 開(kāi)始運(yùn)行六荒。相應(yīng)的护姆,在 viewWillDisappear() 方法中,讓 session 停止運(yùn)行掏击。

override func viewWillAppear(animated: Bool) {
 
    super.viewWillAppear(animated)
    if (session?.running == false) {
        session.startRunning()
    }
}
 
override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    
    if (session?.running == true) {
        session.stopRunning()
    }
}

數(shù)據(jù)服務(wù)

DataService.swift 中卵皂,我們將引入 Alamofire 和 SwiftyJSON。

接下來(lái)砚亭,聲明一些變量來(lái)存儲(chǔ)我們從 Discogs 獲得的原始數(shù)據(jù)灯变。根據(jù) Bionik6 的建議殴玛,我們將使用 private(set) 來(lái)實(shí)現(xiàn)只讀屬性。

然后創(chuàng)建 Alamofire GET 請(qǐng)求添祸。這里通過(guò)解析 JSON 得到專輯的名稱和年份滚粟。我們分別把得到的名稱和年份原始數(shù)據(jù)賦值給 ALBUM_FROM_DISCOGSYEAR_FROM_DISCOGS,之后會(huì)使用這些變量來(lái)創(chuàng)建專輯對(duì)象刃泌。

現(xiàn)在凡壤,我們從 Discogs 上獲得了數(shù)據(jù),下面要做的就是展示給全世界耙替!好吧亚侠,展示給 AlbumDetailsViewController.swift 就夠了。使用通知的方式來(lái)實(shí)現(xiàn)林艘。

import Foundation
import Alamofire
import SwiftyJSON
 
class DataService {
 
    static let dataService = DataService()
     
    private(set) var ALBUM_FROM_DISCOGS = ""
    private(set) var YEAR_FROM_DISCOGS = ""
     
    static func searchAPI(codeNumber: String) {
        
        // 從 Discogs 上獲取專輯數(shù)據(jù)的 URL
        let discogsURL = "\(DISCOGS_AUTH_URL)\(codeNumber)&?barcode&key=\(DISCOGS_KEY)&secret=\(DISCOGS_SECRET)"
        
        Alamofire.request(.GET, discogsURL)
            .responseJSON { response in
                
                var json = JSON(response.result.value!)
                
                let albumArtistTitle = "\(json["results"][0]["title"])"
                let albumYear = "\(json["results"][0]["year"])"
                
                self.dataService.ALBUM_FROM_DISCOGS = albumArtistTitle
                self.dataService.YEAR_FROM_DISCOGS = albumYear
                
                // 發(fā)送通知盖奈,讓 AlbumDetailsViewController 知道我們得到了數(shù)據(jù)
                NSNotificationCenter.defaultCenter().postNotificationName("AlbumNotification", object: nil)
        }
    }

}

Album 模型

在專輯的數(shù)據(jù)模型 Album.swift 中混坞,需要將專輯模型轉(zhuǎn)化為我們想要的數(shù)據(jù)狐援。這個(gè)模型接受原始的 artistAlbumalbumYear 數(shù)據(jù),把它們轉(zhuǎn)換為更加易讀的數(shù)據(jù)究孕。

import Foundation
 
class Album {        
 
private(set) var album: String!
private(set) var year: String!
 
init(artistAlbum: String, albumYear: String) {
    
    // 為專輯信息添加一些額外的數(shù)據(jù)
    self.album = "Album: \n\(artistAlbum)"
    self.year = "Released in: \(albumYear)"
}
 
}

是時(shí)候秀一波專輯數(shù)據(jù)了啥酱!

viewDidLoad() 方法中,設(shè)置 labels 的內(nèi)容厨诸,提示用戶開(kāi)始掃描镶殷。我們需要添加 observer 來(lái)監(jiān)聽(tīng) NSNotification 從而接收通知。同時(shí)需要在 deinit 中移除監(jiān)聽(tīng)者微酬。

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

override func viewDidLoad() {
    super.viewDidLoad()
    
    artistAlbumLabel.text = "Let's scan an album!"
    yearLabel.text = ""
    
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(setLabels(_:)), name: "AlbumNotification", object: nil)
}

當(dāng)監(jiān)聽(tīng)到通知的時(shí)候绘趋,setLabels() 方法將會(huì)被調(diào)用。這里我們將使用 DataService.swift 中的原始字符串來(lái)初始化 Album 對(duì)象颗管。然后將 label 中的內(nèi)容設(shè)置為我們想要的 Album 內(nèi)容陷遮。

func setLabels(notification: NSNotification){

    // 使用 DataService.swift 中的數(shù)據(jù)初始化 Album 對(duì)象
    let albumInfo = Album(artistAlbum: DataService.dataService.ALBUM_FROM_DISCOGS, albumYear: DataService.dataService.YEAR_FROM_DISCOGS)
    artistAlbumLabel.text = "\(albumInfo.album)"
    yearLabel.text = "\(albumInfo.year)"
}

測(cè)試 CDBarcodes

我們的 app 完成啦!當(dāng)然垦江,我們可以直接從 CD 封面看到專輯名稱帽馋、藝術(shù)家和發(fā)行年份,但是用我們的 app 要有趣得多比吭!為了更好地測(cè)試 CDBarcodes 應(yīng)用绽族,我們需要找一些 CD 和唱片。這樣就有可能同時(shí)遇到 EAN-13 和 UPC-A 條碼衩藤,真正發(fā)揮 app 的威力吧慢。

BarcodeReaderViewController 中,注意將相機(jī)對(duì)焦到條碼上赏表。

這里是完成之后的 CDBarcodes 代碼检诗。

總結(jié)

無(wú)論是商務(wù)人士怖喻、購(gòu)物者還是普通人,條碼掃描器都一個(gè)特別有用的工具岁诉。因此锚沸,能夠開(kāi)發(fā)條碼掃描也非常有用。

掃描那部分比較有趣涕癣。在獲得掃描的數(shù)據(jù)之后哗蜈,我們需要對(duì)數(shù)據(jù)做進(jìn)一步操作,例如判斷是 EAN-13 還是 UPC-A 類型坠韩。我們需要找到轉(zhuǎn)化數(shù)據(jù)的正確方式距潘,然后老司機(jī)就上路了。

如果想了解更多內(nèi)容只搁,可以讀取其他的 metadataObjectTypes 和一些新 API音比。唯一的限制就是你的想象力。

本文由 SwiftGG 翻譯組翻譯氢惋,已經(jīng)獲得作者翻譯授權(quán)洞翩,最新文章請(qǐng)?jiān)L問(wèn) http://swift.gg

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末焰望,一起剝皮案震驚了整個(gè)濱河市骚亿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌熊赖,老刑警劉巖来屠,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異震鹉,居然都是意外死亡俱笛,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)传趾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)迎膜,“玉大人,你說(shuō)我怎么就攤上這事墨缘⌒呛纾” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵镊讼,是天一觀的道長(zhǎng)宽涌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蝶棋,這世上最難降的妖魔是什么卸亮? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮玩裙,結(jié)果婚禮上兼贸,老公的妹妹穿的比我還像新娘段直。我一直安慰自己,他們只是感情好溶诞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布鸯檬。 她就那樣靜靜地躺著,像睡著了一般螺垢。 火紅的嫁衣襯著肌膚如雪喧务。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,482評(píng)論 1 302
  • 那天枉圃,我揣著相機(jī)與錄音功茴,去河邊找鬼。 笑死孽亲,一個(gè)胖子當(dāng)著我的面吹牛坎穿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播返劲,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼玲昧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了旭等?” 一聲冷哼從身側(cè)響起酌呆,我...
    開(kāi)封第一講書(shū)人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤衡载,失蹤者是張志新(化名)和其女友劉穎搔耕,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體痰娱,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡弃榨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了梨睁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鲸睛。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖坡贺,靈堂內(nèi)的尸體忽然破棺而出官辈,到底是詐尸還是另有隱情,我是刑警寧澤遍坟,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布拳亿,位于F島的核電站,受9級(jí)特大地震影響愿伴,放射性物質(zhì)發(fā)生泄漏肺魁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一隔节、第九天 我趴在偏房一處隱蔽的房頂上張望鹅经。 院中可真熱鬧寂呛,春花似錦、人聲如沸瘾晃。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蹦误。三九已至呢诬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胖缤,已是汗流浹背尚镰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哪廓,地道東北人狗唉。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像涡真,于是被迫代替她去往敵國(guó)和親分俯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

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