iOS拦截导航栏返回按钮的方法

has updated for iOS 13

项目中的页面(webview)分成两种,一种是比较简单的,对于这种页面,浏览完毕后点返回,就是真的返回,退到上一层;另一种是复杂的页面,在页面内部会有一些跳转的情况,但是用户点返回并不是真的想要返回上一层。也就是说,当用户从list页或者首页等进入到A,再从A进入到B(A和B实际上对于webview来说是一个页面)后,点返回,只是想返回A,而不是回到list页。如果点了一次返回就直接从B跳会到list页,用户的体验会很差。一开始web的同事都是设计成在网页(A和B所在的网页)中放置返回箭头,用户直接点网页中的返回进行控制。但是领导觉得这样很不好(领导做安卓的),至于这样有什么不好,我倒是没觉得。不得已只能想办法拦截导航栏按钮动作了。

当然这种拦截的使用方式不限于我上面描述的这个场景。
在此做个总结吧

一般情况下,如果需要对导航栏左侧的返回按钮进行特殊处理,都会选择在导航栏放置UIBarButtonItem,然后实现它的action。这种方法也被称为是自定义返回按钮。

我没使用这个方法,主要是因为加入这个功能的时候,项目已经很复杂了,如果抛弃了iOS原生的返回按钮,不论是显示的样式还是在pad这种大尺寸屏幕上出现的返回按钮title会自动变成上一层的title这个特点 都会有变化,所以不想重新自定义这个按钮,造成不必要的麻烦。

于是我选择了下面这种方式,闲话不多说了。由于最近又实现swift版本,这里放置两个版本的代码吧。

此方法参考:
1: [Custom backBarButtonItem]: http://www.jianshu.com/p/a502d363c998
2: [iOS拦截导航栏返回按钮事件的正确方式]: http://www.jianshu.com/p/25fd027916fa
3: https://stackoverflow.com/questions/1214965/setting-action-for-back-button-in-navigation-controller

OBJC 的实现方法:

UINavigationBarDelegate中定义了下面4个函数:

@protocol UINavigationBarDelegate <UIBarPositioningDelegate>

@optional

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPushItem:(UINavigationItem *)item; // called to push. return NO not to.
- (void)navigationBar:(UINavigationBar *)navigationBar didPushItem:(UINavigationItem *)item;    // called at end of animation of push or immediately if not animated
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;  // same as push methods
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item;

@end

其中,navigationBar: shouldPopItem: 返回YES则pop到上一层,返回NO则不进行pop。就利用这一点,实现拦截返回按钮的功能。代码如下:

// 1, 实现自己的NavigationController
// 它是UINavigationController的子类,并且实现了UINavigationBarDelegate中的navigationBar:shouldPopItem:
// NavigationController.h
#import <UIKit/UIKit.h>
// 定义一个protocol,实现此协议的类提供它自己的返回规则或者进行相应的个性化处理
@protocol NavigationControllerDelegate <NSObject>
@optional
- (BOOL) shouldPopOnBackButtonPress;
@end
@interface NavigationController : UINavigationController
@end
// 2,NavigationController.m
// 遵循协议UINavigationBarDelegate
@interface NavigationController () <UINavigationBarDelegate>
@end

@implementation NavigationController
// pragma mark - UINavigationBarDelegate
- (BOOL) navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item{
    BOOL shouldPop = YES;

    // 已修改(标记1)
    NSUInteger count = self.viewControllers.count;
    NSUInteger itemsCount = navigationBar.items.count;
    if(count < itemsCount){
        return shouldPop;
    }

   // 通过点击返回键和直接调用popViewController,得到的topViewController不同
    UIViewController *vc = self.topViewController;
    if([vc respondsToSelector:@selector(shouldPopOnBackButtonPress)]){
        shouldPop = [vc performSelector:@selector(shouldPopOnBackButtonPress)];
    }
    if(shouldPop == NO){
      // 返回NO后,返回按钮中的 < 会置灰(文字恢复为黑色)通过设置NavigationBarHidden属性使它恢复
        [self setNavigationBarHidden:YES];
        [self setNavigationBarHidden:NO];
    }else{
        // 不能直接调用pop,如果是通过popViewController调起,会造成循环调用此方法
        // 如果是通过调用[navigationController popViewControllerAnimated:]导致的shouldPop delegate被调用,
        // 此时已经完成了viewController的pop, viewControllers.count 会比 navigationBar.items.count小1
        // 这种情况就不必再次调用popViewController,否则会导致循环
        if(count >= itemsCount){
            dispatch_async(dispatch_get_main_queue(), ^{
                [self popViewControllerAnimated: YES];
            });
        }
    }
    return shouldPop;
}
@end

如果希望在处理完自定义的pop逻辑后,通过调用父类的navigationBar: shouldPopItem: 方法进行pop,则使用下面的方式完成:

//NavigationController.m:
// 为UINavigationController写一个Category, 用于暴露父类的navigationBar: shouldPopItem:
@interface UINavigationController (UINavigationControllerNeedShouldPopItem)
- (BOOL) navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
@end

@implementation UINavigationController (UINavigationControllerNeedShouldPopItem)
@end


@interface NavigationController () <UINavigationBarDelegate>
@end

@implementation NavigationController
// UINavigationBarDelegate
- (BOOL) navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item{

 NSUInteger count = self.viewControllers.count;
    NSUInteger itemsCount = navigationBar.items.count;

    if(count < itemsCount){
        return YES;
    }

    UIViewController *vc = self.topViewController;
    if([vc respondsToSelector:@selector(shouldPopOnBackButtonPress)]){
        if([vc performSelector:@selector(shouldPopOnBackButtonPress)]){
            // 此处调用父类的navigationBar: shouldPopItem:,但是父类并没有暴露此方法,在这里可以调用因为上面的Category  - UINavigationControllerNeedShouldPopItem
            return [super navigationBar:navigationBar shouldPopItem:item];
        }else{
            [self setNavigationBarHidden:YES];
            [self setNavigationBarHidden:NO];
            return NO;
        }
    }else{
        return [super navigationBar:navigationBar shouldPopItem:item];
    }
}
@end
// 3,在一个需要此拦截处理的view controller中,实现NavigationControllerDelegate,提供它自己的pop规则
// MyViewController.h
@interface MyViewController : UIViewController
@end

// MyViewController.m
#import "NavigationController.h"
@interface MyViewController () <NavigationControllerDelegate>
@end

@implementation MyViewController
// pragma mark - NavigationControllerDelegate
- (BOOL) shouldPopOnBackButtonPress {
    // 实现自己的返回逻辑
    if(self.customiseBack == YES){
       // ... do something private 
        return NO;
    }else{
        return YES;
    }
}
@end

SWIFT 的实现方法:

基于UINavigationBarDelegate中定义了下面4个协议:

public protocol UINavigationBarDelegate : UIBarPositioningDelegate {

    
    @available(iOS 2.0, *)
    optional public func navigationBar(_ navigationBar: UINavigationBar, shouldPush item: UINavigationItem) -> Bool // called to push. return NO not to.

    @available(iOS 2.0, *)
    optional public func navigationBar(_ navigationBar: UINavigationBar, didPush item: UINavigationItem) // called at end of animation of push or immediately if not animated

    @available(iOS 2.0, *)
    optional public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool // same as push methods

    @available(iOS 2.0, *)
    optional public func navigationBar(_ navigationBar: UINavigationBar, didPop item: UINavigationItem)
}

通过完成protocol中的navigationBar(_:shouldPop:)方法完成拦截导航栏按钮的功能。

// 1,NavigationController.swift
import Foundation
// 定义一个protocol,实现它的类,自定义pop规则、逻辑或方法
protocol NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool
}

// 实现自己的NavigationController,它是UINavigationController的子类,并且遵循UINavigationBarDelegate 
class NavigationController: UINavigationController, UINavigationBarDelegate {
    // 实现navigationBar(_: shouldPop:)
    func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        var shouldPop = true
        
       // 已修改(标记1)
        let viewControllersCount = self.viewControllers.count
        let navigationItemsCount = navigationBar.items?.count
        
        if(viewControllersCount < navigationItemsCount!){
            return shouldPop
        }
        if let topViewController: UIViewController = self.topViewController {
            if(topViewController is NavigationControllerBackButtonDelegate){
                let delegate = topViewController as! NavigationControllerBackButtonDelegate
                shouldPop = delegate.shouldPopOnBackButtonPress()
            }
        }
        if(shouldPop == false){
            isNavigationBarHidden = true
            isNavigationBarHidden = false
        }else{
            if(viewControllerCount >= navigationItemCount!){
                DispatchQueue.main.async { () -> Void in
                    self.popViewController(animated: true)
                }
            }
        }
        return shouldPop
    }
}
// 2, MyViewController.swift中,完成自定义的pop规则。实现NavigationControllerBackButtonDelegate
class MyViewController: UIViewController, NavigationControllerBackButtonDelegate {
  // MARK: NavigationController delegate
    func shouldPopOnBackButtonPress() -> Bool {
        var shouldPop = true
        if(self.customiseBack == true){
            // do something private
            shouldPop = false
        }
        return shouldPop
    }
}

注意:如果使用storyboard,需要将其中Navigation Controller对应的class修改为自定义的NavigationController,如果使用代码进行的UINavigationController的初始化,要将UINavigationController修改为NavigationController.

补充一下(标记1:代码已修改):
[Custom backBarButtonItem]: http://www.jianshu.com/p/a502d363c998
中提到一点,之前一直忽略的问题,就是在navigationBar:shouldPopItem中获取topViewController『不稳定』问题。这点没理解透,果然造成我一个大bug,耗费了不少精力。
对于这个问题,我是这样理解的:navigationBar:shouldPopItem 可以由两种方式触发:
1)在UI上点『返回』按钮;
2)在代码中调用 popViewControllerAnimated:
但是,通过这两种方法调用之后,在navigationBar:shouldPopItem中,获取到的topViewController(topViewController, visibleViewController, viewControllers.lastObject)不同。
通过1)方法,获取到的topViewController,是pop前的VC;而通过2)方法,获取到的topViewController是pop之后出现的VC。

注意:我们要拦截的,是 导航栏 『返回』按钮,而不是 popViewControllerAnimated。

就因为这个区别,导致出现了我遇到的问题,简单描述一下,有A->B->C三个VC,其中B和C都遵循协议,实现了相应的是否pop的判断方法,称为shouldPop_B, shouldPop_C。考虑下面两种情况:
情况一:在B中,点返回按钮,返回事件被拦截,调用navigationBar:shouldPopItem,由于是通过点按钮触发的,所以topViewController 是 B,于是调用B->shouldPop_B,根据执行结果,对B进行pop或者保持。
情况二:在B中通过2)方法,调用 [self.navigationController popViewControllerAnimated:YES]; 关闭自己(即B),那么也会触发navigationBar:shouldPopItem,此时,topViewController是A,A并不遵循协议,正常执行pop,关闭B。注意,此时,navigationBar:shouldPopItem 中的 popViewControllerAnimated 也不应当被执行。
以上是在B中的操作,是完全正常的。

但是,如果上述操作换到C中,则会出现下面的问题:
情况一:与在B的情况相同;
情况二:在C中通过2)方法,调用[self.navigationController popViewControllerAnimated:YES]; 关闭自己(即C),触发navigationBar:shouldPopItem,此时,topViewController是B,而B也遵循协议,那么在 navigationBar:shouldPopItem中就执行了B->shouldPop_B,这显然是不对的。

修改这个问题的方法也很简单,因为通过2) 方法,调用popViewControllerAnimated 触发的 navigationBar:shouldPopItem 中,viewControllers.count 与 navigationBar.items.count 不同(通过点击按钮触发则这两个值相同)。所以,通过在 navigationBar:shouldPopItem 最初判断是否为 popViewControllerAnimated 触发,也就是比较上面两个count的大小,如果topViewController不是我们预期的VC,那么直接返回YES,进行正常的pop动作即可。确认了能够获取到预期的topViewController,再对shouldPop进行调用,才能保证调用到了预期的VC上实现的判断方法。(上面的代码已经修改了这个问题)

======= 以下是最近测试发现的问题和解决方法 =======
问题:连击 『返回』按钮,会导致navigation bar异常。异常表现在,当前VC返回了,但是返回后的VC没有了『返回』按钮,或者有时候没有了title。

跟踪发现,连击『返回』按钮时,控制台会打出如下log:

2018-01-03 14:27:33.808220+0800 xxxx[4658:1856459] **** shouldpop shouldPopItem in
2018-01-03 14:27:33.808622+0800 xxxx[4658:1856459] **** shouldpop 2, 1
2018-01-03 14:27:33.862542+0800 xxxx[4658:1856459] **** shouldpop shouldPopItem in
2018-01-03 14:27:33.862769+0800 xxxx[4658:1856459] **** shouldpop 2, 1
2018-01-03 14:27:33.862904+0800 xxxx[4658:1856459] nested pop animation can result in a corrupted navigation bar
2018-01-03 14:27:33.866050+0800 xxxx[4658:1856459] Attempting to begin a transition on navigation bar (<UINavigationBar: 0x10190c0c0; frame = (0 20; 375 44); opaque = NO; autoresize = W; tintColor = UIExtendedGrayColorSpace 1 1; gestureRecognizers = <NSArray: 0x1c4250b90>; layer = <CALayer: 0x1c402a780>>) while a transition is in progress.

可见,函数

  • (BOOL) navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
    在短时间被调用了两次。google 『nested pop animation can result in a corrupted navigation bar』这段log,大致的解释为:在A中pop B,B已经完成pop后,又调用A pop B造成的了异常。但是控制台也没有其他的信息。APP没有crash,可见不是个fatal exception,但是却已经不能正常使用了。

由于使用的『返回』按钮不是自己添加的UIBarButtonItem,获取不到它的点击事件(点击动作),因此不能在『控制点击』这个点进行处理。于是,我想到的只能是一个比较投机取巧的方法,就是使用delay,在一段时间内不处理pop的动作,从而保证navigation bar的正常。

下面是加入的代码:

// 加入一个属性,标记是否正在执行pop
@property (nonatomic) BOOL isAnimating;
- (BOOL) navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item{
    NSLog(@"**** shouldpop shouldPopItem in");
    BOOL shouldPop = YES;
    NSUInteger count = self.viewControllers.count;
    NSUInteger itemsCount = navigationBar.items.count;
    if(self.isAnimating == YES){
        if(count < itemsCount){
            self.isAnimating = NO;
            NSLog(@"**** shouldpop isAnimating==yes,  set isAnimating=no, %d", shouldPop);
            return shouldPop;
        }else{
            NSLog(@"**** shouldpop isAnimating==yes, shouldpop 3, 0");
            return NO;
        }
    }
    self.isAnimating = YES;
    NSLog(@"**** shouldpop set isAnimating=yes");

    if(count < itemsCount){
        self.isAnimating = NO;
        NSLog(@"**** shouldpop set isAnimating=no, %d", shouldPop);
        return shouldPop;
    }
    
    UIViewController *vc = self.topViewController;
    if([vc respondsToSelector:@selector(shouldPopOnBackButtonPress)]){
        shouldPop = [vc performSelector:@selector(shouldPopOnBackButtonPress)];
    }
    if(shouldPop == NO){
        [self setNavigationBarHidden:YES];
        [self setNavigationBarHidden:NO];
    }else{
        // 不能直接调用pop,如果是通过popViewController调起,会造成循环调用此方法
        // 如果是通过调用[navigationController popViewControllerAnimated:]导致的shouldPop delegate,
        // 此时已经完成了viewController的pop, viewControllers.count 会比 navigationBar.items.count小1
        // 这种情况就不必再次调用popViewController
        if(count >= itemsCount){
            dispatch_async(dispatch_get_main_queue(), ^{
                [self popViewControllerAnimated: YES];
            });
        }
    }
    [self resumeAnimationAfter: 0.2];
    NSLog(@"**** shouldpop 2, %d", shouldPop);
    return shouldPop;
}

- (void)resumeAnimationAfter: (CGFloat)delay {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        self.isAnimating = NO;
        NSLog(@"**** shouldpop set isAnimating=no");
    });
}

每次执行pop时,都delay 0.2s再置回状态,保证在这0.2s期间是不进行正常响应的。0.2只是个经验值,因为我的工程中大多数都是h5页面,所以0.2s并不会有明显的延迟。

连续快速点击『返回』按钮,控制台输出的log为:

2018-01-04 15:09:30.434872+0800 xxxx[5838:2009875] **** shouldpop shouldPopItem in
2018-01-04 15:09:30.435076+0800 xxxx[5838:2009875] **** shouldpop set isAnimating=yes
2018-01-04 15:09:30.435248+0800 xxxx[5838:2009875] **** shouldpop 2, 1
2018-01-04 15:09:30.503845+0800 xxxx[5838:2009875] **** shouldpop shouldPopItem in
2018-01-04 15:09:30.504052+0800 xxxx[5838:2009875] **** shouldpop isAnimating==yes, shouldpop 3, 0
2018-01-04 15:09:30.505148+0800 xxxx[5838:2009875] **** shouldpop shouldPopItem in
2018-01-04 15:09:30.505310+0800 xxxx[5838:2009875] **** shouldpop isAnimating==yes, shouldpop 3, 0
2018-01-04 15:09:30.506152+0800 xxxx[5838:2009875] **** shouldpop shouldPopItem in
2018-01-04 15:09:30.506547+0800 xxxx[5838:2009875] **** shouldpop isAnimating==yes, shouldpop 3, 0
2018-01-04 15:09:30.507221+0800 xxxx[5838:2009875] **** shouldpop shouldPopItem in
2018-01-04 15:09:30.507322+0800 xxxx[5838:2009875] **** shouldpop isAnimating==yes, shouldpop 3, 0
2018-01-04 15:09:30.507999+0800 xxxx[5838:2009875] **** shouldpop shouldPopItem in
2018-01-04 15:09:30.508113+0800 xxxx[5838:2009875] **** shouldpop isAnimating==yes, shouldpop 3, 0
2018-01-04 15:09:30.649635+0800 xxxx[5838:2009875] **** shouldpop set isAnimating=no

需要说明的一点是,当下面的判断if(count < itemsCount)成立时,需要函数返回yes,执行返回动作。

SWIFT代码:

    // delay为延迟时间 milliseconds,毫秒,代码中设置为200
    fileprivate func resumeAnimationAfter(_ delay: Int) {
        DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(delay)) {
            self.isAnimating = false
            print("**** shouldpop set isAnimating=no")
        }
    }

===== 以下是iOS 13 上发现的问题 =====

升级iOS 13后,发现部分页面出现了点击一次『返回』,会连续返回两个page的问题。
针对此问题有两点需要说明,只能简单说说现象,深层次的原因暂时是没时间研究了:
1,在代码里直接通过调用popViewController关闭页面,即使用:

self.navigationController?.popViewController(animated: true)

时,iOS 13并不会调用shouldPopitem delegate:

optional public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool // same as push methods

,页面会被正常pop。而 iOS 12以下会调用,并且在这个delegate起始位置, 如下判断结果为真,也可以正常执行pop动作:

let viewControllersCount = self.viewControllers.count
let navigationItemsCount = navigationBar.items?.count
if(viewControllersCount < navigationItemsCount!){
}

2,点击『返回』后,在iOS 13 和 iOS 12上,上述代码log相同,如下:

navigationItemsCount: 3
**** shouldpop set isAnimating=yes
viewControllersCount: 3, navigationItemsCount: 3
**** shouldpop 2, true
**** shouldpop set isAnimating=no

但是,在iOS 13上,却会连续返回两个page,并且第二个page返回时没有进shouldPop delegate。
在代码方面造成这个现象的原因是下述语句:

NSUInteger count = self.viewControllers.count;
NSUInteger itemsCount = navigationBar.items.count;

if(count >= itemsCount){
    dispatch_async(dispatch_get_main_queue(), ^{
        [self popViewControllerAnimated: YES];
    });
}

此处,判断count>=itemsCount 为真时,执行pop的动作,否则不执行。
就是这个判断,导致在iOS 13上出现连续返回两个page的问题。猜测是navigationbar和viewcontroller是分开的两部分,分别返回,顺序或者层级关系导致。暂时的处理是加入版本判断:

// 2019-10-9: fix: iOS 13, 返回会连续两次
print("viewControllersCount: \(viewControllersCount), navigationItemsCount: \(navigationItemsCount!)")
var doPop = false
let systemVersion = UIDevice.current.systemVersion
let equalOrUpper = systemVersion.compare("13.0", options: NSString.CompareOptions.numeric)
if equalOrUpper == ComparisonResult.orderedAscending {  // 小于 13.0
      if viewControllersCount >= navigationItemsCount! {
             doPop = true
      }
}else{
       if viewControllersCount > navigationItemsCount! {
             doPop = true
       }
}
            
if doPop {
       DispatchQueue.main.async { () -> Void in
              self.popViewController(animated: true)
       }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容