iOS_最全的二维码篇


二维码扫面当下很流行,也带来了很多的便利。我也不是在吹捧,你也许你也发现,几乎凡是个APP,都有扫描二维码的功能。现在网上的资料很多,我也是闲的蛋疼,在做一个美团的高仿项目,涉及到二维码的扫描功能,iOS7之前,开发者进行扫码编程时,一般会借助第三方库。常用的是ZBarSDKa和ZXingObjC,iOS7之后,系统的AVMetadataObject类中,为我们提供了解析二维码的接口。经过测试,使用原生API扫描和处理的效率非常高,远远高于第三方库。我查了很多的资料,网上关于用系统原生类来实现二维码扫描的资料还是很多的,但都不是很全。所以我把一套功能的实现分享出来,希望你们喜欢。(刚来简书,文章的排版还不是很熟,蛋疼!,还有就是打开项目时,要是不是真机仿真,要先注释掉beginScanning()方法,要不程序会崩溃)

这里是Swift版的,如果你想使用OC来实现,翻译过去我相信也不是难事。

OC也可以参考:http://yimouleng.com/2016/01/13/ios-QRCode/

这里包含:

  • 1、窗口的搭建
  • 2、二维码的扫描
  • 3、从相册中读取二维码
  • 4、生成二维码
  • 5、长按识别二维码

其中二维码的扫描主要是用到AVFoundation这个框架中的AVCaptureSession,读取二维码和识别二维码是同个原理,只是处理方式不同,主要用到的是CIDetector类,而生成二维码要用到CoreImage框架中的CIFilter滤镜类,值得一提的是CoreImage非常强大,这是iOS中关于图片处理的几乎所有内容集合,即图片的处理几乎都在CoreImage中,CIFilter也非常值得一究,mac上应该在CoreQuartz中。CoreImageCIFilter的内容很多,我可能会在另外的文章中去分享。(我并不是大神,不敢吹牛逼说给大家讲解,只是分享和大家相互学习)

详情看看我的源码:
美团的高仿项目 更多模块中的“扫一扫”


1、窗口的搭建

这是UI的东西,很简单,想怎么弄就怎么弄,只要你happy就ok,这是我的代码:

1.1、设置导航条:setupNavView(),editItemAction()中因为在iOS9+平台,所以用UIAlertController来列出多个ActionSheet功能

func setupNavView() {
    
self.navigationController?.navigationBar.barTintColor = UIColor.clearColor()
        self.title = "二维码/条形码"
        //设置标题颜色
        let navigationTitleAttribute = NSDictionary(object: UIColor.whiteColor(), forKey: NSForegroundColorAttributeName)
        self.navigationController?.navigationBar.titleTextAttributes = navigationTitleAttribute as? [String : AnyObject]
        
        //1.返回
        let backBtn = UIButton(type: UIButtonType.Custom)
        backBtn.frame = CGRectMake(20, 30, 25, 25);
        
        backBtn.setBackgroundImage(UIImage(named: "qrcode_scan_titlebar_back_nor"), forState:UIControlState.Normal);
        backBtn.contentMode = UIViewContentMode.ScaleAspectFit
        backBtn.addTarget(self, action: #selector(QRCodeScanViewController.backBtnAction), forControlEvents: UIControlEvents.TouchUpInside)
        
        let backItem = UIBarButtonItem(customView: backBtn)
        self.navigationItem.leftBarButtonItem = backItem
        
        let editItem = UIBarButtonItem(barButtonSystemItem: .Edit, target: self, action: #selector(QRCodeScanViewController.editItemAction))
        self.navigationItem.rightBarButtonItem = editItem
        
        
        
       //  deprecated  //
//        //2.相册
//        let albumBtn = UIButton(type: UIButtonType.Custom)
//        albumBtn.frame = CGRectMake(0, 0, 35, 49)
//        albumBtn.center = CGPointMake(self.view.bounds.width / 2, 20 + 49 / 2.0)
//        albumBtn.setBackgroundImage(UIImage(named: "qrcode_scan_btn_photo_down"), forState: UIControlState.Normal)
//        albumBtn.contentMode=UIViewContentMode.ScaleAspectFit
//        albumBtn.addTarget(self, action: #selector(QRCodeScanViewController.openAlbum), forControlEvents: UIControlEvents.TouchUpInside)//        self.view.addSubview(albumBtn)
//        
//        //3.闪光灯
//        let flashBtn = UIButton(type: UIButtonType.Custom)
//        flashBtn.frame = CGRectMake(self.view.bounds.width - 55, 20, 35, 49)
//        flashBtn.setBackgroundImage(UIImage(named: "qrcode_scan_btn_flash_down"), forState: UIControlState.Normal)
//        flashBtn.contentMode=UIViewContentMode.ScaleAspectFit
//        flashBtn.addTarget(self, action: #selector(QRCodeScanViewController.openFlash(_:)), forControlEvents: UIControlEvents.TouchUpInside)
//        self.view.addSubview(flashBtn)
func editItemAction() {
        
        let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .ActionSheet)
        let action1 = UIAlertAction(title: "从相册选取二维码", style: .Default) {
            [unowned self]
            (act) in
            self.openAlbum()
        }
        
        let action2 = UIAlertAction(title: "打开闪光灯", style: .Default) {
            [unowned self]
            (act) in
            self.openFlash()
        }
        
        let action3 = UIAlertAction(title: "生成二维码", style: .Default) {
            [unowned self]
            (act) in
            
            let inputVC = UIAlertController(title: "输入信息", message: nil, preferredStyle: .Alert)
            let cancelAction = UIAlertAction(title: "取消", style: .Cancel, handler: nil)
            let okayAction = UIAlertAction(title: "确定", style: .Default){
                [unowned self]
                (act) in
                ///生成二维码图片
                let QRCodeImage = self.createQRCodeImage(withImage: UIImage(named: "icon_mine_default_portrait")!, string: self.textInfo!)
                
                ///展示在界面上
                let imageBtn = UIButton(frame: CGRectMake(0, 64, SCREENWIDTH, SCREENHEIGHT - 64))
                imageBtn.setImage(QRCodeImage, forState: .Normal)
                imageBtn.addTarget(self, action: #selector(self.imageBtnAction(_:)), forControlEvents: .TouchUpInside)
                imageBtn.backgroundColor = THEMECOLOR
                self.view.addSubview(imageBtn)
                
                ///保存到相册
                //UIImageWriteToSavedPhotosAlbum(QRCodeImage, self, #selector(self.image(_: didFinishSavingWithError: contextInfo: )), nil)
                
                inputVC.dismissViewControllerAnimated(true, completion: { 
                    print("生成二维码")
                })
            }
            
            inputVC.addAction(cancelAction)
            inputVC.addAction(okayAction)
            
            inputVC.addTextFieldWithConfigurationHandler({ (textField) in
                textField.borderStyle = .None
                textField.placeholder = "输入需要保存的信息"
                textField.delegate = self
                textField.becomeFirstResponder()
            })
            
            self.presentViewController(inputVC, animated: true, completion: {
                print("输入信息可以生成二维码")
            })
        }
        
        let action4 = UIAlertAction(title: "取消", style: .Cancel, handler: nil)
        
        actionSheet.addAction(action1)
        actionSheet.addAction(action2)
        actionSheet.addAction(action3)
        actionSheet.addAction(action4)
        
        self.presentViewController(actionSheet, animated: true, completion: nil)
    }
    

1.3、遮罩(蒙板)的设置:setupMaskView()

 let kMargin = CGFloat(50)
    
    
    func setupMaskView() {
        
        maskView = UIView()
        maskView.layer.borderColor = UIColor(red: CGFloat(0), green: CGFloat(0), blue: CGFloat(0), alpha: CGFloat(0.7)).CGColor
        maskView.layer.borderWidth = kMargin
        
        let maskViewSize = CGSizeMake(self.view.extWidth(), self.view.extWidth())///正方形,下面会露出来,还要添加补充遮罩
        maskView.frame = CGRectMake(0, 64, maskViewSize.width, maskViewSize.height)
        self.view.addSubview(maskView)
        
        ///补充遮罩
        let mask = UIView(frame: CGRectMake(0, maskView.extY() + maskView.extHeight(), SCREENWIDTH, SCREENHEIGHT - (maskView.extY() + maskView.extHeight())))
        mask.backgroundColor = UIColor(red: CGFloat(0), green: CGFloat(0), blue: CGFloat(0), alpha: CGFloat(0.7))
        self.view.addSubview(mask)
        
    }

1.4、扫描区域的设置:setupScanWindowView()

 func setupScanWindowView() {
        
        let scanWindowH = maskView.extWidth() - kMargin * 2 ///kMargin为黑色边框的宽度,
        let scanWindowW = scanWindowH
        
        scanWindow =  UIView(frame: CGRectMake(kMargin, kMargin + 64, scanWindowW, scanWindowH))///隐形的框
        scanWindow.clipsToBounds = true
        self.view.addSubview(scanWindow)
        
        scanNetImageView = UIImageView(image: UIImage(named: "scan_net"))
        scanNetImageView.extSetY(-1 * scanNetImageView.extHeight())
        scanWindow.addSubview(scanNetImageView)
        
        let buttonWH = CGFloat(18)
        let topLift = UIImageView(frame: CGRectMake(0, 0, buttonWH, buttonWH))
        topLift.image = UIImage(named: "scan_1")
        let topRight = UIImageView(frame: CGRectMake(scanWindowW - buttonWH, 0, buttonWH, buttonWH))
        topRight.image = UIImage(named: "scan_2")
        let bottomLeft = UIImageView(frame: CGRectMake(0, scanWindowH - buttonWH, buttonWH, buttonWH))
        bottomLeft.image = UIImage(named: "scan_3")
        let bottomRight = UIImageView(frame: CGRectMake(topRight.frame.origin.x, bottomLeft.frame.origin.y, buttonWH, buttonWH))
        bottomRight.image = UIImage(named: "scan_4")
        
        scanWindow.addSubview(topLift)
        scanWindow.addSubview(topRight)
        scanWindow.addSubview(bottomLeft)
        scanWindow.addSubview(bottomRight)
        
        self.view.addSubview(scanWindow)
    }
    

2、二维码的扫描

主要用的的是AVCaptureSession类,然后设置inputoutput即可,值得一提的是扫码支持的类型:output.metadataObjectTypes。iOS已经提供了很多,但是并不全,不过包含了常用的二维码AVMetadataObjectTypeQRCode和条形码AVMetadataObjectTypeCode128Code

func beginScanning() {///要真机
        ///模拟图片动起来
        ///way 1、UIView Animation
        UIView.animateWithDuration(1.5, delay: 0, options: UIViewAnimationOptions.Repeat, animations: {
            [unowned self] in
            self.scanNetImageView.transform = CGAffineTransformTranslate(self.scanNetImageView.transform, 0, self.scanWindow.extHeight())
            }, completion: nil)
        
        ///way 2、coreAnimation
        
        
        //初始化链接对象
        session = AVCaptureSession()
        //高质量采集率
        session.sessionPreset = AVCaptureSessionPresetHigh
        
        let preLayer = AVCaptureVideoPreviewLayer(session: session)///注意session存放的地方
        preLayer.frame = self.view.bounds
        self.view.layer.insertSublayer(preLayer, atIndex: 0)
        
        /*
         * AVCaptureDevice 获取摄像设备
         * AVCaptureDeviceInput 创建输入流
         * AVCaptureMetadataOutput 创建输出了
         */
        
        let device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
        var input: AVCaptureDeviceInput?
        do {
            
            input = try AVCaptureDeviceInput(device: device)
            ///add input
            
        }catch let error as NSError {
            // 发生了错误
            print(error.localizedDescription)
        }
        catch {
            print("--input未知错误--")
        }
        ///add input
        session.addInput(input)
        
        let output = AVCaptureMetadataOutput()
        ///add delegate
        output.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())//主队列(主线程)
        
        ///设置“感兴趣”区域(敏感区域)
        let interestRect = preLayer.metadataOutputRectOfInterestForRect(scanWindow.frame)///扫描区 到 metadata输出区
        ///值等于CGRectMake(scanWindow.extY() / SCREENHEIGHT, scanWindow.extX() / SCREENWIDTH, scanWindow.extHeight() / SCREENHEIGHT, scanWindow.extWidth() / SCREENWIDTH)
        ///把一个在 preview layer 坐标系中的rect 转换成一个在 metadata output 坐标系中的rect
        
        output.rectOfInterest = interestRect ///注意,这个并不是扫描区的坐标尺寸
        
        session.addOutput(output)
        
        //设置扫码支持的类型
        output.metadataObjectTypes = [AVMetadataObjectTypeDataMatrixCode,
                                      AVMetadataObjectTypeAztecCode,
                                      AVMetadataObjectTypeQRCode,
                                      AVMetadataObjectTypePDF417Code, 
                                      AVMetadataObjectTypeEAN13Code,
                                      AVMetadataObjectTypeEAN8Code,
                                      AVMetadataObjectTypeCode128Code]
        
        ///常用的码制有:PDF417二维条码、Datamatrix二维条码、QR Code、Code 49、Code 16K、Code one等,
        ///除了这些常见的二维条码之外,还有Vericode条码、Maxicode条码、CP条码、Codablock F条码、 Ultracode条码及Aztec条码。
        
        
        
        ///start grab
        session.startRunning()
   
    }

3、从相册中读取二维码

主要用到的是CIDetector检测器类,然后获取feature对象。


/***********照片读取**************/
    
    func openAlbum(){//相册
        
        if(UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary)){
        
            ///1.初始化相册拾取器
            let pikController = UIImagePickerController()
            ///2.设置代理
            pikController.delegate = self//两个代理
            //3.设置资源:
            /**
            UIImagePickerControllerSourceTypePhotoLibrary,相册
            UIImagePickerControllerSourceTypeCamera,相机
            UIImagePickerControllerSourceTypeSavedPhotosAlbum,照片库
            */
            pikController.sourceType = UIImagePickerControllerSourceType.SavedPhotosAlbum
            //4.随便给他一个转场动画
            pikController.modalTransitionStyle = UIModalTransitionStyle.FlipHorizontal
            self.presentViewController(pikController, animated: true, completion: nil)
            
        }else{
            
            let alertVC = UIAlertController(title: "提示", message: "设备不支持访问相册,请在设置->隐私->照片中进行设置!", preferredStyle: UIAlertControllerStyle.Alert)
            let action = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
            alertVC.addAction(action)
            
            self.presentViewController(alertVC, animated: true, completion: nil)
        
        }
        
    }
    
    ///imagePickerControllerdelegate func
    func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
        ///1.获取选择的图片
        let image = info[UIImagePickerControllerOriginalImage]
        ///2.初始化一个监测器
        let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [ CIDetectorAccuracy : CIDetectorAccuracyHigh ])
        picker.dismissViewControllerAnimated(true) { () -> Void in
            ///监测到的结果数组
            let features = detector.featuresInImage(CIImage(CGImage: (image?.CGImage)!))
            
            if features.count >= 1 {
                /**结果对象 */
                ///CIQRCodeFeature
                let feature = features[0] as! CIQRCodeFeature
                let scannedResult = feature.messageString
                
                let alertVC = UIAlertController(title: "提示", message: scannedResult, preferredStyle: UIAlertControllerStyle.Alert)
                self.presentViewController(alertVC, animated: true, completion: nil)

            }else {
                let alertVC = UIAlertController(title: "提示", message: "该图片没有包含一个二维码!", preferredStyle: UIAlertControllerStyle.Alert)
                let action = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
                alertVC.addAction(action)
                
                self.presentViewController(alertVC, animated: true, completion: nil)
            }
        }
    }
    

4、生成二维码

生成二维码处理过程有点复杂,首先要导入CoreImage,然后用CIFilter生成图片,nameCIQRCodeGenerator就是二维码滤镜,CICode128BarcodeGenerator就是条形码滤镜,当然还有很多滤镜,如下面会用到的颜色滤镜nameCIFalseColor

4.1、原生二维码的生成

 /***********生成二维码图片**************/ ///coreImage
    
    func createQRCodeImage(withImage image: UIImage, string: String) -> UIImage {
        
        /// 1. 实例化二维码滤镜
        let filter = CIFilter(name: "CIQRCodeGenerator")///CICode128BarcodeGenerator ///条形码
        ///注意
        
        /// 2. 恢复滤镜的默认属性
        filter?.setDefaults()
        
        /// 3. 将字符串转换成二进制数据,(生成二维码所需的数据)
        let data = string.dataUsingEncoding(NSUTF8StringEncoding)
        
        /// 4. 通过KVO把二进制数据添加到滤镜inputMessage中
        filter?.setValue(data, forKey: "inputMessage")
        filter?.setValue("H", forKey: "inputCorrectionLevel")
        
        /// 5. 获得滤镜输出的图像
        let outputImage = filter?.outputImage ///CIImage
        
        /// 6. 将CIImage转换成UIImage,并放大显示
        //let originQRCodeImage = UIImage(CIImage: outputImage!, scale: 0.07, orientation: UIImageOrientation.Up) ///原生二维码图片 ///这样将图片放大会变得模糊
        //return originQRCodeImage
        
        ///进行重绘
        let newQRCodeImage = createUIimageWithCGImage(ciImage: outputImage!, widthAndHeightValue: 300)
        
        return newQRCodeImage
    }

如果你直接用这行代码返回图片

let originQRCodeImage = UIImage(CIImage: outputImage!, scale: 0.07, orientation: UIImageOrientation.Up) ///原生二维码图片 ///这样将图片放大会变得模糊
return originQRCodeImage

得到的图片将会很模糊,因为这是将图片进行放大了,但图片将0.07改为了1,看到的图片将会很小。

所以还得处理一下:

func createUIimageWithCGImage(ciImage image: CIImage, widthAndHeightValue wh: CGFloat) -> UIImage {
        let ciRect = CGRectIntegral(image.extent)///根据容器得到适合的尺寸
        let scale = min(wh / ciRect.width, wh / ciRect.height)
        
        ///获取bitmap
        
        let width  = size_t(ciRect.width * scale)
        let height  = size_t(ciRect.height * scale)
        let cs = CGColorSpaceCreateDeviceGray()///灰度颜色通道 ///CGColorSpaceRef
        
        let info_UInt32 = CGImageAlphaInfo.None.rawValue
        let bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, cs, info_UInt32)
        
        let contex = CIContext(options: nil)
        let bitmapImageRef = contex.createCGImage(image, fromRect: CGRectMake(ciRect.origin.x, ciRect.origin.y, ciRect.size.width, ciRect.size.height)) ///CGImageRef
        
        CGContextSetInterpolationQuality(bitmapRef, CGInterpolationQuality.High)///写入质量高,时间长
        CGContextScaleCTM(bitmapRef, scale, scale) ///调整“画布”的缩放
        CGContextDrawImage(bitmapRef, ciRect, bitmapImageRef) ///绘制图片
        
        ///保存
        let scaledImage = CGBitmapContextCreateImage(bitmapRef)
        
        ///bitmapRef和bitmapImageRef不用主动释放,Core Foundation自动管理
        
        //let originImage = UIImage(CGImage: scaledImage!) ///原生灰度图片(灰色)
        
        let ciImage = CIImage(CGImage: scaledImage!)
        
        ///添加滤镜
        let colorFilter = CIFilter(name: "CIFalseColor")///颜色滤镜
        colorFilter!.setDefaults()
        colorFilter!.setValue(ciImage, forKey:kCIInputImageKey)
        
        colorFilter!.setValue(CIColor(red: 33.0 / 225.0, green: 192.0 / 225.0, blue: 174.0 / 225.0, alpha: 1.0), forKey:"inputColor0")///二维码元素(像素)
        colorFilter!.setValue(CIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1), forKey:"inputColor1")///背景
        
        let colorImgae = colorFilter!.outputImage
        let newQRCodeImage = UIImage(CIImage: colorImgae!)
        
        return newQRCodeImage
        
    }

这样将会得到彩色的二维码,效果也还不错,这里又添加colorFilter这样滤镜,达到着色的效果。iOS其实可以混合多个滤镜来实现很多不能的效果,手机自带的图片处理就是基于滤镜来完成,如果你有使用过PS,或了解过知识,这就很容易理解了。如果去掉这部分代码得到的将会是灰色的图片。

其实重新着色,还可以通过,context来重绘或者通过改变每个像素的的颜色值来达到目的,但是难度会远比添加一个滤镜要复杂的多,而且,Swift中通过指针来处理数据,写法将会更加复杂,这是因为Swift是一门语法安全性语言。也没有*P这样的写法。

关于CIFilter的更多知识,我就会在后续文章中分享,你们也可以通过资料去学习,总之,这部分知识还是很容易理解的,也比较有趣。

更新的CIFilter文章:点击这里 会教你获取更清晰的二维码。

最后,想了解更多详情:请查看我的demo,记得给个Star,😝😝😝

下载:点击这里

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

推荐阅读更多精彩内容