如何使用 Swift 開發(fā)簡單的條形碼檢測器滞欠?

【編者按】本文作者為 Matthew Maher,主要手把手地介紹如何用 Swift 構(gòu)建簡單的條形碼檢測器雹洗。文章系 OneAPM 工程師編譯整理。

超市收銀員對貨物進行掃碼卧波,機場內(nèi)錄入行李或檢查乘客时肿,或是在大型零售商的存貨管理等活動中,條形碼掃碼器都是一個簡單而實用的工具港粱。事實上螃成,條形碼掃碼器還幫助消費者實現(xiàn)了智能購物,貨物分類等用途查坪。這次寸宏,我們將為iPhone開發(fā)一個掃碼器。

我們很幸運偿曙,蘋果公司讓條形碼掃描過程的實現(xiàn)變得很簡單氮凝。我們將會深入AV Foundation框架開發(fā)一個簡單的能夠掃描CD條形碼的app,然后獲得專輯的關(guān)鍵信息望忆,最后在app的界面中打印出來罩阵。閱讀條形碼很酷炫也很重要,我們會根據(jù)讀到的條形碼采取進一步的操作启摄。

不用多說稿壁,能掃碼的設(shè)備必須要有一個攝像頭。從這里開始歉备,讓我們拿一個配備有攝像頭的iOS設(shè)備開始干活吧傅是!

簡介 CDbarcodes

我們今天開發(fā)的這個app名叫CDBarcodes——通俗易懂,即條形碼掃描對象是CD。當(dāng)我們的設(shè)備檢測到一個條形碼時落午,會拾取這個貨碼然后發(fā)送到Discogs的數(shù)據(jù)庫谎懦,獲得其專輯名稱肚豺、藝人姓名以及發(fā)布年份溃斋。Discogs的音樂數(shù)據(jù)庫十分強大,因此我們很有可能找到一些實用信息吸申。

下載CDBarcodes的初始項目梗劫。

如何使用 Swift 開發(fā)簡單的條形碼檢測器?

除了一個不錯的數(shù)據(jù)庫截碴,Discogs還有一個實用的API來幫助查詢梳侨。我們涉及的僅僅是Discogs提供給開發(fā)者的一小部分功能,不過這已經(jīng)足夠使我們的app跑起來了日丹。

Discogs

進入Discogs網(wǎng)站走哺。首先我們必須注冊一個Discogs賬號并登錄。在這之后哲虾,下拉到頁面最底端丙躏。在頁尾最左欄點擊API。

如何使用 Swift 開發(fā)簡單的條形碼檢測器束凑?

在Discogs的API界面左側(cè)的數(shù)據(jù)庫區(qū)域點擊搜索(Search)晒旅。

如何使用 Swift 開發(fā)簡單的條形碼檢測器?

這是我們查詢的端點汪诉。我們將會從“title”和“year”這兩個參數(shù)上獲得專輯信息废恋。

現(xiàn)在,我們將這個URL記錄在CDBarcodes中以便后面的查詢扒寄。在Constants.swift中添加DISCOGS_AUTH_URL并賦值https://api.discogs.com/database/search?q=作為常量鱼鼓。

let DISCOGS_KEY = "your-discogs-key"

現(xiàn)在我們能夠在整個app里面通過DISCOGS_AUTH_URL調(diào)用URL。

回到Discogs的API頁面该编,選擇創(chuàng)建一個新的app迄本,并獲得一些認(rèn)證信息。在頁面頂端的導(dǎo)航欄中上渴,找到“Create an App”岸梨,點擊該按鈕。

如何使用 Swift 開發(fā)簡單的條形碼檢測器稠氮?

在應(yīng)用名稱欄里輸入“CDBarcodes Your Name”曹阔,或是其他合適的名字。描述可以使用下面的文字:

“這是一個iOS應(yīng)用隔披,旨在在讀取CD的條形碼后顯示專輯信息赃份。”

然后,點擊“Create Application”(即創(chuàng)建應(yīng)用)按鈕抓韩。

在結(jié)束頁面纠永,會看到允許我們使用條形碼的認(rèn)證信息。

復(fù)制“Consumer Key”(用戶秘鑰)到Constants.swiftDISCOGS_KEY里面谒拴。

有了這個URL尝江,我們可以很方便的在整個CDBarcodes應(yīng)用里使用這些參數(shù)。

如何使用 Swift 開發(fā)簡單的條形碼檢測器英上?

CocoaPods

我們使用功能強大的依賴管理器(dependency manager)CocoaPods來與Discogs的API進行交互炭序。有關(guān)CocoaPods的安裝和其他信息,可以參照CocoaPods官網(wǎng)苍日。

經(jīng)由CocoaPods惭聂,在網(wǎng)絡(luò)端我們將會使用Alamofire,并借助SwiftyJSON來處理Discogs返回的JSON相恃。

現(xiàn)在開始在CDBarcodes實戰(zhàn)吧辜纲!

安裝好CocoaPods,打開終端界面拦耐,調(diào)至CDBarcodes耕腾,在Xcode項目中使用下面的代碼初始化CoccoaPods:

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

在Xcode里打開Podfile文件:

open -a Xcode Podfile

輸入或是復(fù)制粘貼下面的代碼至Podfile文件:

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

最后,運行下面的代碼下載Alamofire和SwiftyJSON:

pod install

現(xiàn)在回到Xcode揩魂!注意開發(fā)app時要保持打開CDBarcodes.xcworkspace(工作區(qū))幽邓。

條形碼閱讀器

蘋果的AV Foundation框架提供了我們開發(fā)這個條形碼閱讀器app需要的相關(guān)工具。下面是整個過程中會涉及到的幾個方面:

  • AVCaptureSession將會處理來自相機的輸入輸出數(shù)據(jù)火脉。

  • AVCaptureDevice指的是物理設(shè)備及其它的屬性牵舵。AVCaptureSession從AVCaptureDevice這里接受輸入信息。

  • AVCaptureDeviceInput從輸入設(shè)備獲取輸入數(shù)據(jù)倦挂。

  • AVCaptureMetadataOutput將元數(shù)據(jù)對象發(fā)送至代理對象(delegate object)處進行處理畸颅。

BarcodeReaderViewController.swift里面,我們的第一步操作是導(dǎo)入AVFoundation方援。

import UIKit
import AVFoundation

注意要遵循AVCaptureMetadataOutputObjectsDelegate没炒。

viewDidLoad(),將運行我們的條形碼閱讀引擎犯戏。

首先送火,新建一個AVCaptureSession對象并設(shè)置AVCaptureDevice。然后先匪,我們新建一個輸入對象并添加至AVCaptureSession种吸。

class BarcodeReaderViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {

var session: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!

override func viewDidLoad() {
    super.viewDidLoad()

    // Create a session object. 新建一個模塊對象
    session = AVCaptureSession()

    // Set the captureDevice. 設(shè)置captureDevice
    let videoCaptureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)

    // Create input object. 新建輸入設(shè)備
    let videoInput: AVCaptureDeviceInput?

    do {
        videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
    } catch {
        return
    }

    // Add input to the session. 將輸入添加至模塊中
    if (session.canAddInput(videoInput)) {
        session.addInput(videoInput)
    } else {
        scanningNotPossible()
    }

如果設(shè)備碰巧沒有攝像頭時,掃描過程將不可能實現(xiàn)呀非。因此坚俗,我們需要一個報錯函數(shù)镜盯。在這里,我們通知用戶尋找一個有相機的iOS設(shè)備以便進行下一步CD條形碼的讀取猖败。

func scanningNotPossible() {
    // Let the user know that scanning isn't possible with the current device. 告知用戶掃描現(xiàn)有設(shè)備無法掃描
    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()速缆,在將輸入添加至(session)模塊后,我們接著新建AVCaptureMetadataOutput并將它添加到模塊中恩闻。我們將捕捉到的數(shù)據(jù)通過一個串行序列的形式發(fā)送給代理對象艺糜。

下一步就是明確我們應(yīng)該掃描的條形碼類型。在這里我們面對的是EAN-13類型的條形碼判呕。有趣的是倦踢,并不是所有的條形碼都是這種類型送滞;有一些將會是UPC-A格式侠草。這可能會導(dǎo)致錯誤出現(xiàn)。

蘋果會自動將UPC-A格式的條形碼前面加一個0后轉(zhuǎn)為EAN-13格式犁嗅。UPC-A格式的條形碼僅僅有12位數(shù)字边涕;而在EAN-13格式的條形碼中則是13位。這個自動轉(zhuǎn)換過程的一個好處是我們可以查詢metadataObjectTypes AVMetadataObjectTypeEAN13Code褂微,因此兩種格式的條形碼我們就都能讀取了功蜓。需要注意的是這個轉(zhuǎn)換會直接改變條形碼從而誤導(dǎo)Discogs數(shù)據(jù)庫。不過不用擔(dān)心宠蚂,我們馬上就會解決這個問題式撼。

無論如何,在用戶設(shè)備相機有問題時我們就將用戶引導(dǎo)至scanningNotPossible()函數(shù)求厕。

// Create output object. 新建輸出對象
let metadataOutput = AVCaptureMetadataOutput()

// Add output to the session. 將輸出添加至模塊
if (session.canAddOutput(metadataOutput)) {
    session.addOutput(metadataOutput)

    // Send captured data to the delegate object via a serial queue. 通過串行序列將捕捉到的數(shù)據(jù)發(fā)送至代理對象著隆。
    metadataOutput.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())

    // Set barcode type for which to scan: EAN-13. 設(shè)置需要掃描的條形碼類型:EAN-13
    metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeEAN13Code]

} else {
    scanningNotPossible()
}

現(xiàn)在我們就搞定了這個酷炫的功能,拉出來溜溜吧呀癣!我們將使用AVCaptureVideoPreviewLayer以整個屏幕展示視頻美浦。

最后,我們開始捕捉模塊项栏。

// Add previewLayer and have it show the video data. 添加previewLayer并展示視頻數(shù)據(jù)

    previewLayer = AVCaptureVideoPreviewLayer(session: session);
    previewLayer.frame = view.layer.bounds;
    previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    view.layer.addSublayer(previewLayer);

    // Begin the capture session. 開啟捕捉模塊

    session.startRunning()

In captureOutput:didOutputMetadataObjects:fromConnection, we celebrate, as our barcode reader found something!

通過captureOutput:didOutputMetadataObjects:fromConnection浦辨,我們的條形碼閱讀器終于讀取到了一些數(shù)據(jù)。

首先沼沈,我們需要使用第一個對象獲得metadataObjects數(shù)組并將其轉(zhuǎn)換為可機讀代碼流酬。然后,我們將readableCode字符串發(fā)送至barcodeDetected()列另。

在進入barcodeDetected()函數(shù)前芽腾,我們會停止捕捉模塊并給用戶一個震動反饋。如果我們忘了叫停捕捉模塊访递,那么震動也就停不下來了晦嵌!這也是為什么這是一個好案例的原因。

func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {

    // Get the first object from the metadataObjects array. 獲得metadataObjects數(shù)組的第一個對象
    if let barcodeData = metadataObjects.first {
        // Turn it into machine readable code 轉(zhuǎn)換為可機讀代碼
        let barcodeReadable = barcodeData as? AVMetadataMachineReadableCodeObject;
        if let readableCode = barcodeReadable {
            // Send the barcode as a string to barcodeDetected() 發(fā)送條形碼數(shù)據(jù)
            barcodeDetected(readableCode.stringValue);
        }

        // Vibrate the device to give the user some feedback. 震動反饋
        AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))

        // Avoid a very buzzy device. 結(jié)束捕捉模塊
        session.stopRunning()
    }
}

barcodeDetected()函數(shù)里面我們有很多事情要做。第一個任務(wù)是在震動反饋之后惭载,提示用戶我們已經(jīng)發(fā)現(xiàn)了條形碼旱函。然后我們利用找到的數(shù)據(jù)開始干活!

條形代碼中的空格必須移除描滔。在這之后我們需要確認(rèn)條形碼格式是EAN-13還是UPC-A棒妨。如果是EAN-13我們可以直接使用。如果對象是一個UPC-A代碼含长,那么它已經(jīng)被轉(zhuǎn)化為EAN-13格式券腔,我們需要將其轉(zhuǎn)換為原始格式。

如我們前文已經(jīng)討論的那樣拘泞,蘋果設(shè)備在UPC-A格式的條形碼前添加一個0將其轉(zhuǎn)化為EAN-13格式纷纫,因此我們首先確定代碼是以0開頭的。如果是陪腌,我們需要將它移除辱魁。少了這一步,Discogs數(shù)據(jù)庫將不能識別這個數(shù)字诗鸭,我們也就得不到想要的數(shù)據(jù)了染簇。

在獲得清理后的條形碼字符串后,我們將它發(fā)送至DataService.searchAPI()并彈出BarcodeReaderViewController.swift强岸。

func barcodeDetected(code: String) {

    // Let the user know we've found something. 告知用戶掃描結(jié)果
    let alert = UIAlertController(title: "Found a Barcode!", message: code, preferredStyle: UIAlertControllerStyle.Alert)
    alert.addAction(UIAlertAction(title: "Search", style: UIAlertActionStyle.Destructive, handler: { action in

        // Remove the spaces. 移除空格
        let trimmedCode = code.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())

        // EAN or UPC?  確定格式
        // Check for added "0" at beginning of code.

        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() 將UPC發(fā)送至API
            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之前锻弓,在viewDidLoad()下面,我們添加 viewWillAppear()viewWillDisappear()函數(shù)蝌箍。viewWillAppear()將會開啟捕捉模塊青灼;而viewWillDisappear()會終止這一模塊。

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里十绑,我們首先要導(dǎo)入Alamofire 和 SwiftyJSON聚至。

接著,我們聲明一些變量以便存儲從Discogs返回的原始數(shù)據(jù)本橙。根據(jù)Bionik6的建議扳躬,我們巧妙地使用private(set)函數(shù)避免了用戶可能導(dǎo)致的阻塞問題。

然后甚亭,建立Alamofire GET請求贷币。在這里JSON會被解析,從而獲得專輯的title(名稱)和year(發(fā)行年份)亏狰。將原始的title和year字符串賦給ALBUM_FROM_DISCOGSYEAR_FROM_DISCOGS役纹,在后文將會用到它們來初始化我們的專輯。

現(xiàn)在暇唾,我們擁有了來自Discogs的數(shù)據(jù)促脉,我們可以正式開秀了辰斋;隨之我們通知AlbumDetailsViewController.swift模塊捕捉到的信息。

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) {
    // The URL we will use to get out album data from Discogs 使用URL獲得數(shù)據(jù)
    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

            // Post a notification to let AlbumDetailsViewController know we have some data. 通知AlbumDetailsViewController
            NSNotificationCenter.defaultCenter().postNotificationName("AlbumNotification", object: nil)
    }
}

}

專輯模塊

在專輯模塊Album.swift中瘸味,我們會處理專輯數(shù)據(jù)以便符合我們的要求宫仗。這個模塊將會獲取原始的artistAlbumalbumYear字符串然后將它們用戶友好化。在AlbumDetailsViewController.swift我們展示加工后的albumyear信息旁仿。

import Foundation

class Album {        

private(set) var album: String!
private(set) var year: String!

init(artistAlbum: String, albumYear: String) {

    // Add a little extra text to the album information 添加額外專輯信息
    self.album = "Album: \n\(artistAlbum)"
    self.year = "Released in: \(albumYear)"
}

}

專輯展示時間藕夫!

viewDidLoad()模塊中,設(shè)置好指向條形碼閱讀器的標(biāo)簽(label)枯冈。然后毅贮,我們需要在NSNotification添加觀察者(Observer),以便我們已經(jīng)展示的提示能夠集群尘奏。在deinit中滩褥,我們會移除觀察者(Observer)。

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)通知出現(xiàn)時罪既,setLabels()函數(shù)將會被調(diào)用铸题。在這里,我們會使用來自DataService.swift的原始數(shù)據(jù)初始化Album琢感。標(biāo)簽將會展示加工后的字符串。

func setLabels(notification: NSNotification){

    // Use the data from DataService.swift to initialize the Album.
    let albumInfo = Album(artistAlbum: DataService.dataService.ALBUM_FROM_DISCOGS, albumYear: DataService.dataService.YEAR_FROM_DISCOGS)
    artistAlbumLabel.text = "\(albumInfo.album)"
    yearLabel.text = "\(albumInfo.year)"
}

測試 CDBarcodes

應(yīng)用搭建完畢探熔,掃一下CD的條形碼我們就能確定專輯的名稱驹针,藝人和發(fā)行年份信息,這很有意思诀艰!為了更好的測試CDBarcodes柬甥,我們可以隨機找一些CD或是黑膠唱片。這樣我們就更有機會同時遇到EAN-13和UPC-A兩種條形碼格式的案例其垄。目前我們兩者都能處理苛蒲!

為了使應(yīng)用順利運行至BarcodeReaderViewController模塊,注意避免閃光以確保相機能捕捉到條形碼信息绿满。

這里是完整代碼的下載鏈接臂外。

結(jié)論

不管是商人,機智的消費者還是一般人士喇颁,這個條形碼閱讀器都很實用漏健。因此,開發(fā)者拿這個案例來練練手是極好的橘霎。

但是我們也看到有趣的僅僅是掃碼部分蔫浆。在獲得數(shù)據(jù)后,我們遇到了一點小問題姐叁,如EAN-13 和 UPC-A格式問題瓦盛。我們找到了解決問題應(yīng)對需求的辦法洗显。

接下來,我們可以探討一些其他的metadataObjectTypes以及一些新API原环。機會無窮墙懂,經(jīng)驗無價。

本文系 OneAPM 工程師編譯整理扮念。OneAPM Mobile Insight真實用戶體驗為度量標(biāo)準(zhǔn)進行 Crash 分析损搬,監(jiān)控網(wǎng)絡(luò)請求及網(wǎng)絡(luò)錯誤,提升用戶留存柜与。訪問 OneAPM 官方網(wǎng)站感受更多應(yīng)用性能優(yōu)化體驗巧勤,想閱讀更多技術(shù)文章,請訪問 OneAPM 官方技術(shù)博客弄匕。

本文轉(zhuǎn)自 OneAPM 官方博客

原文鏈接:http://www.appcoda.com/simple-barcode-reader-app-swift/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末颅悉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子迁匠,更是在濱河造成了極大的恐慌剩瓶,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件城丧,死亡現(xiàn)場離奇詭異延曙,居然都是意外死亡,警方通過查閱死者的電腦和手機亡哄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門枝缔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蚊惯,你說我怎么就攤上這事愿卸。” “怎么了截型?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵趴荸,是天一觀的道長。 經(jīng)常有香客問我宦焦,道長发钝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任赶诊,我火速辦了婚禮笼平,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舔痪。我一直安慰自己寓调,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布锄码。 她就那樣靜靜地躺著夺英,像睡著了一般晌涕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上痛悯,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天余黎,我揣著相機與錄音,去河邊找鬼载萌。 笑死惧财,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的扭仁。 我是一名探鬼主播垮衷,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼乖坠!你這毒婦竟也來了搀突?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤熊泵,失蹤者是張志新(化名)和其女友劉穎仰迁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體顽分,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡徐许,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了怯邪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绊寻。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖悬秉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情冰蘑,我是刑警寧澤和泌,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站祠肥,受9級特大地震影響武氓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜仇箱,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一县恕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧剂桥,春花似錦忠烛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冤议。三九已至,卻和暖如春师坎,著一層夾襖步出監(jiān)牢的瞬間恕酸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工胯陋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蕊温,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓遏乔,卻偏偏與公主長得像义矛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子按灶,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345

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