iOS-多控制器

一. 主流界面

1. 第一种主流界面

iOS项目主流界面是将UITabBarController作为Window的根控制器,UITabBarController的viewControllers数组里面存放包装着UINavigationController,UINavigationController的根控制器是子控制器。

我们按照主流界面的搭建方式创建项目,页面层级如下:
屏幕快照 2019-10-28 下午11.20.54.png

可以看出UITabBarController是父控制器,UINavigationController是子控制器。

为了验证,我们在子控制器里写如下代码:

UIViewController *vc1 = self.navigationController;
UIViewController *vc2 = self.tabBarController;
    
UIViewController *vc3 = [self.navigationController parentViewController];
UIViewController *vc4 = [[self.tabBarController childViewControllers] firstObject];
    
NSArray *arr1 = self.navigationController.viewControllers;
NSArray *arr2 = self.tabBarController.viewControllers;

打断点,信息如下:
屏幕快照 2019-10-28 下午11.23.52.png

可以发现:vc1和vc4地址相同,vc2和vc3地址相同,验证了UITabBarController是最终的父控制器。

2. 第二种主流界面

第二种方式是将UINavigationController作为Window的根控制器,UITabBarController作为UINavigationController的跟控制器,UITabBarController的viewControllers数组里面存放着viewController(不包装UINavigationController)。

self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
    
MainMenuTabBarVCtr *mainMenuTabBarVCtr = [[MainMenuTabBarVCtr alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:mainMenuTabBarVCtr];
self.window.rootViewController = nav;
    
[self.window makeKeyAndVisible];

页面层级如下,这个就不验证了。

层级.png

总结:

  1. 对于第一种界面: window的根控制器是tabBarController, tabBarController管理四个导航控制器,四个导航控制器的根控制器分别是他们的子控制器, 切换tabar的时候只是在四个导航控制器之间切换, 在子页面通过self.navigationController分别获取的是他们自己的导航控制器, 然后分别用他们自己的导航控制器进行push操作, 所以父控制器tabBarController底部的tabbar会存在, 需要设置viewController.hidesBottomBarWhenPushed = YES。
    由于子页面的父控制器分别是四个导航控制器,所以设置标题的时候可以:
- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationItem.title = @"首页";
}
  1. 对于第二种界面: window的跟控制器是导航控制器, 导航控制器的根控制器是tabBarController, tabBarController管理着四个子控制器, 在子页面通过self.navigationController获取的是唯一一个导航控制器, 所有界面都是通过这一个导航控制器进行push操作, 所以子页面进行push的时候连tabBarController也一块push掉了, 不用设置viewController.hidesBottomBarWhenPushed = YES。
    由于子页面的父控制器都是一个tabBarController,所以设置标题的时候可以:
- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    self.tabBarController.title = @"分类";
}

二. 自定义tabbar的方式

自定义tabbar的方式目前有三种:

1. 自定义tabbar继承于系统的UITabBar

老版微博tabbar界面如下图:
微博.png

中间的➕按钮用系统的肯定实现不了, 这时候我们就要自定义tabbar, 自定义tabbar继承于系统的UITabBar, 代码如下:

XUTabBar.h文件

#import <UIKit/UIKit.h>

@class XUTabBar;

#warning 因为XUTabBar继承自UITabBar,所以称为XUTabBar的代理,也必须实现UITabBar的代理协议
@protocol XUTabBarDelegate <UITabBarDelegate>
@optional
- (void)tabBarDidClickPlusButton:(XUTabBar *)tabBar;
@end

@interface XUTabBar : UITabBar
@property (nonatomic, weak) id<XUTabBarDelegate> delegate;
@end

XUTabBar.m文件

#import "XUTabBar.h"

@interface XUTabBar()

@property (nonatomic, weak) UIButton *plusBtn;

@end

@implementation XUTabBar

- (id)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        //添加一个➕按钮
        UIButton *plusBtn = [[UIButton alloc] init];
        [plusBtn setBackgroundImage:[UIImage imageNamed:@"tabbar_compose_button"] forState:UIControlStateNormal];
        [plusBtn setBackgroundImage:[UIImage imageNamed:@"tabbar_compose_button_highlighted"] forState:UIControlStateHighlighted];
        [plusBtn setImage:[UIImage imageNamed:@"tabbar_compose_icon_add"] forState:UIControlStateNormal];
        [plusBtn setImage:[UIImage imageNamed:@"tabbar_compose_icon_add_highlighted"] forState:UIControlStateHighlighted];
        plusBtn.size = plusBtn.currentBackgroundImage.size;
        [plusBtn addTarget:self action:@selector(plusClick) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:plusBtn];
        self.plusBtn = plusBtn;
    }
    return self;
}
/**
 *  加号按钮点击
 */
- (void)plusClick
{
    // 通知代理
    if ([self.delegate respondsToSelector:@selector(tabBarDidClickPlusButton:)]) {
        [self.delegate tabBarDidClickPlusButton:self];
    }
}

//重新排列按钮的位置
- (void)layoutSubviews
{
    [super layoutSubviews];
    
    //设置➕按钮的位置
    self.plusBtn.centerX = self.width * 0.5;
    self.plusBtn.centerY = self.height * 0.5;
    
    //设置其他按钮的位置
    CGFloat tabbarButtonW = self.width / 5;
    CGFloat tabbarButtonIndex = 0;
    
    for (UIView *child in self.subviews) {
        Class class = NSClassFromString(@"UITabBarButton");
        if ([child isKindOfClass:class]) {
            child.width = tabbarButtonW;
            child.x = tabbarButtonW * tabbarButtonIndex;
            
            tabbarButtonIndex ++;
            //中间按钮的时候直接跳过
            if (tabbarButtonIndex == 2) {
                tabbarButtonIndex ++;
            }
        }
    }
}
@end

在XUTabBarViewController.m里面设置, 代码如下:

//更换tabBar
XUTabBar *tabBar = [[XUTabBar alloc] init];
[self setValue:tabBar forKeyPath:@"tabBar"];
/*
[self setValue:tabBar forKeyPath:@"tabBar"];相当于self.tabBar = tabBar;
[self setValue:tabBar forKeyPath:@"tabBar"];这行代码过后,tabBar的delegate就是XUTabBarViewController
说明,不用再设置tabBar.delegate = self;
*/

/*
1.如果tabBar设置完delegate后,再执行下面代码修改delegate,就会报错
tabBar.delegate = self;

2.如果再次修改tabBar的delegate属性,就会报下面的错误
错误信息:Changing the delegate of a tab bar managed by a tab bar controller is not allowed.
错误意思:不允许修改TabBar的delegate属性(这个TabBar是被TabBarViewController所管理的)
*/

这样就实现了自定义微博的➕按钮。

2. 移除系统tabbar所有子控件,重新添加控件

首先自定义TabBarButtton继承于UIButton:

#import "TabBarButtton.h"

@implementation TabBarButtton

- (void)layoutSubviews{
    [super layoutSubviews];
    
    float imageWidth  = 64;
    float imageheight = 50;
    
    self.titleLabel.hidden = YES;
    [self.imageView setFrame:CGRectMake(0, (self.frame.size.width-imageWidth)/2, imageWidth, imageheight)];
}
@end

然后自定义tabBar继承于UIView:

- (void)createEocTabBar{
    float tabbarWidth  = self.tabBar.frame.size.width;
    float tabbarHeight = self.tabBar.frame.size.height;
    _eocTabBar = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tabbarWidth, tabbarHeight)];
    _eocTabBar.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1];
    
    float tabbarItemWidth = tabbarWidth/tabbarTitleAry.count;
    for (NSInteger i = 0; i < tabbarTitleAry.count; i++){
        
        TabBarButtton *tabbarBt = [[TabBarButtton alloc] init];
        [tabbarBt setFrame:CGRectMake(i*tabbarItemWidth, 0, tabbarItemWidth, tabbarHeight)];
        [tabbarBt addTarget:self action:@selector(selectMenuVC:) forControlEvents:UIControlEventTouchUpInside];
        tabbarBt.tag = i;
        
        [tabbarBt setBackgroundImage:[UIImage imageNamed:tabbarTitleImageAry[i]] forState:UIControlStateNormal];
        [tabbarBt setBackgroundImage:[UIImage imageNamed:tabbarTitleHightImageAry[i]] forState:UIControlStateSelected];
        
        [_eocTabBar addSubview:tabbarBt];
        if (i == 0) {
            _selectTabBarBt = tabbarBt;
            _selectTabBarBt.selected = YES;
        }
    }
    
    [self tabbarStyleOne];
    //[self tabbarStyleTwo];
}

最后,移除系统tabar所有子控件,添加自定义tabar

//清空子控件
- (void)tabbarStyleOne{
    // tabbar 原生子试图 all remove
    NSArray *tabbarViewsAry = [self.tabBar subviews];
    for (int i = 0; i < tabbarViewsAry.count; i++) {
        UIView *view = tabbarViewsAry[I];
        [view removeFromSuperview];
    }
    
    [self.tabBar insertSubview:_eocTabBar atIndex:0];
}

效果图如下:
自定义tabbar.png

当我们使用主流界面一的时候, 发现购物车和我多了两个按钮, 检查层级结构发现, UITabBar里面多了两个UITabBarButton。
自定义tabbar.png

这是因为我们在子页面设置标题的时候直接设置self.title = @"购物车",在我们切换tabbar的时候会刷新tabbar,就多了两个UITabBarButton。

如何解决?不想让它刷新
我们可以直接操作导航条标题 self.navigationItem.title = @"购物车",或者按照下面这个方法,移除系统的tabbar,也会解决这个bug。

3. 移除系统的tabbar,将自定义的tabbar放到系统tabbar的位置

自定义tabar的代码如上不变,移除系统的tabbar

//直接移除
- (void)tabbarStyleTwo{
    // 直接remove tabbar
    UIView *superV = [self.tabBar superview];
    _eocTabBar.frame = self.tabBar.frame;
    [self.tabBar removeFromSuperview];
    [superV addSubview:_eocTabBar];
}

这样会有另外一个问题:viewController.hidesBottomBarWhenPushed = YES; 这句代码没效果了,因为我们把系统的tabbar干掉了。

三. 启动页延迟的推荐方式

有时候我们有广告界面或者登陆界面,如果没有保存登录账号和密码,那么第一个界面是登录界面,如果保存了登录账号和密码,直接显示主页,这里涉及到切换主界面。

如果第一个界面不确定的话,不要修改window的根控制器,因为如果根控制器不确定,后面很多逻辑都要加 if 或者判断(打个比方,回到mainMenuTabBarVCtr的主页)。

解决方案:

  1. 判断是否需要展示push/present登录界面,如果需要展示就利用启动页延迟。
  2. 需要我们重新创建第二个window,第二个window的根控制器是启动页或者登录页面,要显示的时候先让第一个self.window调用makeKeyAndVisible,再让第二个window调用makeKeyAndVisible,这样第二个window就在第一个window上面盖着,事情做完之后再把第二个window移除。

层级结果图如下:
启动延迟

代码如下:

#import "AppDelegate.h"
#import "MainMenuTabBarVCtr.h"

@interface AppDelegate (){
    UIWindow *eocWindow;
}

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [self styleOne];

    [self delayLaunchImage];
  
    return YES;
}

- (void)cancelLaunImage{
    
    [eocWindow resignKeyWindow];
    eocWindow = nil;
}

// 耦合度比较低
- (void)delayLaunchImage{
    
    eocWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    eocWindow.rootViewController = [UIViewController new];
    
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    [imageView setImage:[UIImage imageNamed:@"11.PNG"]];
    [eocWindow addSubview:imageView];
    [eocWindow makeKeyAndVisible];
    
    [self performSelector:@selector(cancelLaunImage) withObject:nil afterDelay:5];
}

- (void)styleOne{
    
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.window.backgroundColor = [UIColor whiteColor];
    
    MainMenuTabBarVCtr *mainMenuTabBarVCtr = [[MainMenuTabBarVCtr alloc] init];
    self.window.rootViewController = mainMenuTabBarVCtr;
    
    [self.window makeKeyAndVisible];
}
@end

当我们把第二个window取消keyWindow的时候,self.window又会默认成为keyWindow,因为总要有一个是keyWindow,我们可以po进行验证。

(lldb) po self.window
<UIWindow: 0x7f8596c02300; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x600001705740>; layer = <UIWindowLayer: 0x6000019093e0>>

(lldb) po eocWindow
<UIWindow: 0x7f8596e28210; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x60000172fdb0>; layer = <UIWindowLayer: 0x60000191d680>>

(lldb) po [UIApplication sharedApplication].keyWindow
<UIWindow: 0x7f8596e28210; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x60000172fdb0>; layer = <UIWindowLayer: 0x60000191d680>>

(lldb) po [UIApplication sharedApplication].keyWindow
<UIWindow: 0x7f8596c02300; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x600001705740>; layer = <UIWindowLayer: 0x6000019093e0>>

四. MVVM和MVCC

关于架构,建议先看完iOS-架构再往下看。

1. MVVM

如果使用标准的MVC框架, 时间久了会发现C里面的代码特别多, 这时候你可能会想到添加工具类, 比如最常见的添加数据请求工具类。

  1. 在MVVM中, 一个View对应一个ViewModel, 它采用双向绑定:View的变动,自动反应到ViewModel上,ViewModel的变动,自动反应到View上, View和Model不再有任何沟通。
  2. MVVM并不是没有C,只是把C弱化了,关于MVVM可以看下图:
MVVM.png

下面讲述MVVM怎么使用:

如下图,想要实现"推荐"这个界面, 标准的MVC代码是什么样的我就不需要多说了, 现在我们要使用MVVM拆分C的代码。

推荐.png

当前界面有两个主要的View,一个是self.view一个是tableView,按照MVVM结构,需要两个ViewModel,由于这个界面比较简单,写两个ViewModel又会把简单问题复杂化,我们这里只使用一个ViewModel,ViewModel代码如下:

RecommendViewModel.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@class MessageModel;

typedef void(^finishLoadBlock)(id infoDict);

@interface RecommendViewModel : NSObject


@property(nonatomic, assign) NSInteger rowNumber;


- (CGFloat)messageHeightForRow:(NSInteger)row;

- (MessageModel*)messageModelForRow:(NSInteger)row;

- (NSString*)messageIdForRow:(NSInteger)row;


- (void)loadDatafromNetWithPage:(NSInteger)page finishNet:(finishLoadBlock)finishBlock;

- (void)deleteAdView:(UIView*)deleteView headView:(UIView*)headView tableview:(UITableView*)tableView;

- (void)pushMessageDetailIndex:(NSIndexPath *)indexPath viewCtr:(UIViewController*)targetVCtr;
@end

RecommendViewModel.m

#import "RecommendViewModel.h"
#import "MessageModel.h"
#import "MessageDetailViewCtr.h"

@interface RecommendViewModel (){
    
}
@property (nonatomic, strong)NSMutableArray *messageAry;
@end

@implementation RecommendViewModel

- (NSInteger)rowNumber{
    
    return self.messageAry.count;
}

- (CGFloat)messageHeightForRow:(NSInteger)row{
    if (row < self.messageAry.count){
        MessageModel *messageModel = self.messageAry[row];
        return messageModel.messageHeight;
    }
    return 0;
}

- (MessageModel*)messageModelForRow:(NSInteger)row{
    
    if (row < self.messageAry.count) {
        return self.messageAry[row];
    }else{
        NSLog(@"row越界messageAry:%ld--%ld", row, _messageAry.count);
    }
    return nil;
}

- (NSString*)messageIdForRow:(NSInteger)row{
    
    if (row < self.messageAry.count) {
        MessageModel *messageModel = self.messageAry[row];
        return messageModel.messageId;
    }else{
        NSLog(@"row越界messageAry:%ld--%ld", row, _messageAry.count);
    }
    return nil;
    
}

- (void)deleteAdView:(UIView*)deleteView headView:(UIView*)headView tableview:(UITableView*)tableView{
    
    [deleteView removeFromSuperview];
    headView.frame = ({
        CGRect frame = headView.frame;
        frame.size.height = frame.size.height - deleteView.frame.size.height;
        frame;
    });
    [tableView setTableHeaderView:nil];
    [tableView setTableHeaderView:deleteView];
}

- (void)pushMessageDetailIndex:(NSIndexPath *)indexPath viewCtr:(UIViewController*)targetVCtr{
    
    MessageDetailViewCtr *messageDetailVCtr = [[MessageDetailViewCtr alloc] initWithMessageID:[self messageIdForRow:indexPath.row]];
    
    [targetVCtr.navigationController pushViewController:messageDetailVCtr animated:YES];
    
}

- (void)loadDatafromNetWithPage:(NSInteger)page finishNet:(finishLoadBlock)finishBlock{
    
    for (int i = 0; i< 10; i++) {
        
        MessageModel *messageModel = [MessageModel new];
        messageModel.messageTitle = [NSString stringWithFormat:@"消息::%d", I];
        [self.messageAry addObject:messageModel];
        
    }
    
    finishBlock(nil);
}

#pragma mark - lazy loading

- (NSMutableArray*)messageAry{
    if (!_messageAry) {
        _messageAry = [NSMutableArray array];
    }
    return _messageAry;
}
@end

推荐控制器的代码如下:

RecommendVCtr.h

#import <UIKit/UIKit.h>
/*
 C 给弱化
 */
@interface RecommendVCtr : UIViewController{
    IBOutlet UIView *_headView;
    IBOutlet UIView *_isDeleteAdView;
    IBOutlet UITableView *_tableView;
}

- (IBAction)removeSelectV:(UIButton*)sender;

@end

RecommendVCtr.m

#import "RecommendVCtr.h"
#import "RecommendCell.h"
#import "RecommendTopNewCell.h"
#import "RecommendViewModel.h"
#import "MessageDetailViewCtr.h"
#import "MessageModel.h"
#import "ReTableViewModel.h"
#import "ReMenuMoreVCtr.h"

@interface RecommendVCtr ()

@property (nonatomic, strong)RecommendViewModel *recomendViewModel;
@property (nonatomic, strong)ReTableViewModel *reTableViewModel;

@end

@implementation RecommendVCtr{
    
    ReMenuMoreVCtr *_reMenuMoreVCtr;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //self addChildViewController:<#(nonnull UIViewController *)#>
    self.automaticallyAdjustsScrollViewInsets = NO;
    [_headView removeFromSuperview];
    [_tableView setTableHeaderView:_headView];
    
    //[self.reTableViewModel configTable:_tableView];
    
    UITableView *tmpTableV = _tableView;
    // 下载数据业务
    [self.recomendViewModel loadDatafromNetWithPage:1 finishNet:^(id infoDict) {
        [tmpTableV reloadData];
    }];
}

- (void)viewDidLayoutSubviews{
    [super viewDidLayoutSubviews];

    [self.view bringSubviewToFront:_tableView];
    _tableView.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - SysNavHeigh - SysTabBarHeigh);
}

- (IBAction)removeSelectV:(UIButton*)sender{
    
    [self.recomendViewModel deleteAdView:_isDeleteAdView headView:_headView tableview:_tableView];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    
    // return [RecommendCell cellHeight];
    return 80.0;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    
    return self.recomendViewModel.rowNumber;
}

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    
    RecommendCell *cell = [tableView dequeueReusableCellWithIdentifier:@"RecommendCell"];
    if (!cell) {
        cell = [[[NSBundle mainBundle] loadNibNamed:@"RecommendCell" owner:nil options:nil] firstObject];
    }
    
     cell.messageModel = [self messageModelForRow:indexPath.row];
    
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    
     [self.recomendViewModel pushMessageDetailIndex:indexPath viewCtr:self];
}

- (MessageModel*)messageModelForRow:(NSInteger)row{
    
    //    if (row < self.messageAry.count) {
    //        return self.messageAry[row];
    //    }else{
    //        NSLog(@"row越界messageAry:%ld--%ld", row, _messageAry.count);
    //    }
    return nil;
}

#pragma mark - lazy loading

- (RecommendViewModel*)recomendViewModel{
    
    if (!_recomendViewModel) {
        _recomendViewModel = [RecommendViewModel new];
    }
    return _recomendViewModel;
}

- (ReTableViewModel*)reTableViewModel{
    
    if (!_reTableViewModel) {
        _reTableViewModel = [ReTableViewModel new];
    }
    return _reTableViewModel;
}
@end

可以发现,MVVM就是把所有关于View的操作交给ViewModel来做了(也有点MVC+工具类的感觉)这样就实现了控制器C代码的简化。

2. MVCC

MVCC是MVC加子控制器C的意思,比如上个界面我们想要把关于tableView的所有代码抽出来,可以创建一个tableVC,在tableVC里添加一个tableView,设置tableView的代理是tableVC,需要使用tableView的时候把tableVC的view添加到目标控制器上,并把tableVC设置为目标控制器的子控制器。

个人比较喜欢MVCC方式,反正无论什么方式,能合理清晰的抽出C的代码就可以。

其实在一些比较大的框架中,并不会强调什么MVC或者MVVM的,主要是站在业务的角度来拆分的,目标就是怎么合理怎么好怎么来,所以工作中要合理使用各种设计模式,不能太死板。

五. 路由

什么是路由?就是帮助我们进行页面跳转用的。

在一般的开发中,如果我们进行跳转,必须要导入目标控制器的头文件,然后进行跳转,如果当前界面需要跳转很多界面就会有很多头文件需要导入。
有了路由,我们只需要导入路由头文件就可以了,所有的跳转都在路由器里面处理,这样就能把业务转移,耦合度更低了。

Demo地址:https://github.com/iamkata/muti-VC

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

推荐阅读更多精彩内容