UITableViewCell

UITableViewCell

  1. 父类是UIView
  2. UITableView的每一行都是一个UITableViewCell

UITableViewCell的类型

  1. 按创建来源区分:
    • 系统cell
    • 自定义cell
  • 按高度区分
    • 等高的cell:所有cell的高度是一样的
    • 不等高的cell:每一个cell的高度并非都一样
  • 按加载来区分
    • 动态cell:cell的内容在运行时才能确定
    • 静态cell:cell的内容一开始已经确定
  • 按是否分组区分
    • plain:不分组(各组cell之间不相隔)
    • grouped:分组(各组cell之间明显相隔)

tableViewCell的常见属性

// 设置右边的指示样式
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

// 设置右边的指示控件
cell.accessoryView = [[UISwitch alloc] init];

// 设置cell的选中样式
// 颜色就灰色和无色两种
cell.selectionStyle = UITableViewCellSelectionStyleNone;

// backgroundView优先级 > backgroundColor
// 设置背景色
cell.backgroundColor = [UIColor redColor];
// 设置背景view
UIView *bg = [[UIView alloc] init];
bg.backgroundColor = [UIColor blueColor];
cell.backgroundView = bg;

// 设置选中的背景view
UIView *selectedBg = [[UIView alloc] init];
selectedBg.backgroundColor = [UIColor purpleColor];
cell.selectedBackgroundView = selectedBg;

UITableViewCell的contentView

  1. UITableViewCell内部有个默认的子视图:contentView,是一个纯粹的view,作用是用于存放所有需要显示子控件,方便管理
  • UITableViewCell的contentView定制了下述3个子控件用于显示
    1. textLabel
    • detailTextLabel
    • imageView
  • UITableViewCell的UITableViewCellStyle属性,用于决定使用contentView的哪些子视图显示,以及这些子视图在contentView中的位置
    • 初始化时设置,一旦设置,修改无效
Snip20160306_9.png

UITableViewCell的辅助指示视图

  • 辅助指示视图的作用是显示一个表示动作的图标,可以通过UITableViewCell的accessoryType属性设置
    UITableViewCellAccessory.png
序号 属性 含义
1 UITableViewCellAccessoryDisclosureIndicator 箭头
2 UITableViewCellAccessoryDetailButton 帮助按钮
3 UITableViewCellAccessoryCheckmark 打钩
4 UITableViewCellAccessoryDetailDisclosureButton 帮助按钮 + 箭头
5 UITableViewCellAccessoryNone 不显示辅助指示视图;默认
  • 也可以通过cell的accessoryView属性来自定义辅助指示视图,优先级比accessoryType
    cell.accessoryView = button;
    
    // 优先级高于accessoryType,要让accessoryType中的类型显示,要清空accessoryView,设置对应的accessoryType
    cell.accessoryView = nil;
    cell.accessoryType = UITableViewCellAccessoryDetailButton;
    

UITableViewCell的重用原理

  1. iOS设备的内存有限,如果用UITableView显示成千上万条数据,就需要成千上万个UITableViewCell对象的话,那将会耗尽iOS设备的内存。
  • 重用原理:当滚动列表时,部分UITableViewCell会移出窗口,UITableView会将窗口外的UITableViewCell放入一个对象池中,等待重用。当UITableView要求dataSource返回UITableViewCell时,dataSource会先查看这个对象池,如果池中有未使用的UITableViewCell,dataSource会用新的数据配置这个UITableViewCell,然后返回给UITableView,重新显示到窗口中,从而避免创建新对象

  • 还有一个非常重要的问题:有时候需要自定义UITableViewCell(用一个子类继承UITableViewCell),而且每一行用的不一定是同一种UITableViewCell,所以一个UITableView可能拥有不同类型的UITableViewCell,对象池中也会有很多不同类型的UITableViewCell,那么UITableView在重用UITableViewCell时可能会得到错误类型的UITableViewCell

  • 解决方案:UITableViewCell有个NSString *reuseIdentifier属性,可以在初始化UITableViewCell的时候传入一个特定的字符串标识来设置reuseIdentifier(一般用UITableViewCell的类名)。当UITableView要求dataSource返回UITableViewCell时,先通过一个字符串标识到对象池中查找对应类型的UITableViewCell对象,如果有,就重用,如果没有,就传入这个字符串标识来初始化一个UITableViewCell对象

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 1.定义一个cell的标识,注意用static修饰
      static NSString *ID = @"czcell";

    // 2.从缓存池中取出cell
    // reuseIdentifier是只读属性。必须在创建时通过传参的方法设置
      UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    // 3.如果缓存池中没有cell
      if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
        }

    // 4.设置数据
    cell.textLabel.text = [NSString stringWithFormat:@"%zd行的数据", indexPath.row];

    return cell;
}

UITableViewCell创建的底层方法调用

  1. 通过init方法或注册类方式创建,底层调用:
    - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier;
    
  2. 通过storyboard和xib创建,底层调用:
    // 第一步:
    - (instancetype)initWithCoder:(NSCoder *)aDecoder;
    
    // 第二步:
    - (id)awakeAfterUsingCoder:(NSCoder *)aDecoder;
    
    // 最后一步:初始化子控件在此操作
    - (void)awakeFromNib;
    

代码实现不等高cell -> 通过frame计算及布局控件

  1. 新建一个继承自UITableViewCell的子类,比如XXXCell

    @interface XXXCell : UITableViewCell
    
    @end
    
  2. 在XXXCell.m文件中添加子控件并初始化

    - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
    {
        if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
    
            // 添加子控件代码(建议封装成一个方法)
    
            // 初始化子控件(建议封装成一个方法,方法名为setup)
            [self setup];
        }
        return self;
    }
    
    - (void)setup
    {
        // 初始化子控件属性
        self.textLabel.font = [UIFont systemFontOfSize:18];
        self.textLabel.textColor = [UIColor whiteColor];
        self.textLabellabel.numberOfLines = 0; // 最好设置这个属性,不等高cell的主要原因是由于label的高度随内容而变化,设置了自动换行,后面计算就能合理算出label的高度,从而使cell自动适配
        // 其他子控件属性
    }
    
  3. 在XXXCell.h文件中提供一个模型属性,比如XXXModel

    @class XXXModel;
    
    @interface XXXCell : UITableViewCell
    /** 模型数据 */
    @property (nonatomic, strong) XXXModel *model;
    @end
    
  4. 在模型.h文件中设置模型数据,并增加所有子控件的frame数据及cell的高度属性

    @interface XXXStatus : NSObject
    /**** 模型数据 ****/
    /** 名字 */
    @property (nonatomic ,strong) NSString *name;
    /** 图片 */
    @property (nonatomic ,strong) NSString *icon;
    // 其他模型属性
    
    /**** 所有子控件的frame数据 ****/
    /** 头像的frame */
    @property (nonatomic, assign) CGRect iconFrame;
    // 其他子控件的frame
    
    /** cell的高度 */
    @property (nonatomic, assign) CGFloat cellHeight;
    @end
    
  5. 在模型.m文件中重写模型cellHeight属性的get方法(核心),主要是label的包裹效果

    - (CGFloat)cellHeight
    {
        // 必须有这个判断,否则每次刷新cell都会不断重算cellHeight
        if (_cellHeight == 0) {
            // ... 计算所有子控件的frame、cell的高度,并记录下来
    
            // 尺寸不变的控件frame计算,例如图片
            CGFloat iconX = margin;
            CGFloat iconY = margin;
            CGFloat iconW = 50;
            CGFloat iconH = 50;
            self.iconFrame =CGRectMake(iconX, iconY, iconW, iconH);// 记录控件大小
    
            // 尺寸变化的控件frame计算,例如label(随文字内容多寡变化)
        // 计算方式一:文本虽然变化,但不超过1行
            // 文本控件名:nameLabel;图片控件名:iconImageView
            CGFloat nameX = CGRectGetMaxX(self.iconImageViewFrame) + margin;
            CGFloat nameY = iconY ;
            NSDictionary *nameAttributes = @{NSFontAttributeName : [UIFont systemFontOfSize:17]};
            CGSize nameSize = [self.name sizeWithAttributes:nameAttributes];
            self.nameLabelFrame =CGRectMake(nameX, nameY, nameSize.width, nameSize.height);
    
        // 计算方式二:文本不断变化,且可能超过1行
            // nameLabel,最好在初始化时设置
            // nameLabel.numberOfLines = 0;
            CGFloat nameX = CGRectGetMaxX(self.iconImageViewFrame) + margin;
            CGFloat nameY = CGRectGetMaxY(self.iconImageViewFrame) + margin;
            CGFloat nameW = [UIScreen mainScreen].bounds.size.width - 2 * nameX;
            NSDictionary *nameAttributes = @{NSFontAttributeName : [UIFont systemFontOfSize:14]};
            CGSize nameMaxSize = CGSizeMake(TEXTW, MAXFLOAT);
            CGFloat nameH = [self.text boundingRectWithSize:TEXTMaxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:nameAttributes context:nil].size.height;
            self.nameLabelFrame =CGRectMake(nameX, nameY, nameW, nameH);
    
            // cellHeight等于最后一个控件的最大Y值 + 间隔值
            // _cellHeight = ...
                }
        return _cellHeight;
    }
    
  6. 在XXXCell.m中重写模型属性的set方法

    - (void)setModel:(XXXModel *)model
    {
        _model = model;
    
        // 给子控件设置模型数据
        self.imageView.image = [UIImage imageNamed:self.model.icon];
        // .......
    }
    
  7. 在XXXCell.m中重写-layoutSubviews方法,布局子控件

    /**
     *  在这个方法中设置所有子控件的frame
     */
    - (void)layoutSubviews
    {
        // 一定要调用`[super layoutSubviews]`
        [super layoutSubviews];
    
        // 布局子控件的代码,根据模型中计算出的frame设置控件frame
        self.imageView.frame = self.model.iconFrame;
        // ...
    }
    
  8. 在控制器中,添加模型数组属性,并将数据源(字典数组)转化为模型数组

    #pragma mark - lzay load
    - (NSMutableArray *)models
    {
        if (!_models) {
    
            // 获取字典数组
            // 字典数组转模型数组代码
        }
        return _models;
    }
    
  9. 在控制器中,注册cell的类型(可选,不注册,则通过alloc init方法创建)

    [self.tableView registerClass:[XXXTgCell class] forCellReuseIdentifier:ID];
    
  10. 在控制器中,实现数据源方法

    // 多少行cell
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return self.models.count;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString * const ID = @"model";// Identifier一般用cell的类名
    
        // 访问缓存池
        XXXCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
        // 创建cell,若第8步注册了,此处可省略
        // if (!cell) {
        cell = [[XXXCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:ID];
        }
    
        // 设置数据(传递模型数据)
        cell.model = self.models[indexPath.row];
    
        return cell;
    }
    
  11. 在控制器中,实现一个返回cell高度的代理方法

    // 返回indexPath位置对应cell的高度
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        XXXModel *model = self.models[indexPath.row];
        return model.cellHeight;
    }
    

代码实现不等高cell -> 通过约束布局控件

  1. 同“代码实现不等高cell -> 通过frame计算及布局控件”第1步(新建一个继承自UITableViewCell的子类)

    @interface XXXCell : UITableViewCell
    
    @end
    
  2. 在“代码实现不等高cell -> 通过frame计算及布局控件”第2步(添加子控件并初始化)的基础上:

    • 添加子控件约束
      - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
      {
          if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
      
              // 添加子控件代码(建议封装成一个方法)
      
              // 初始化子控件(建议封装成一个方法,方法名为setup)
              [self setup];
      
              // 添加子控件约束(建议封装成一个方法)
              // 不是在layoutSubview中设置,因为只需要设置一次,而layoutSubview是系统自动调用,调用次数不确定
          }
          return self;
      }
      
      - (void)setup
      {
          // 初始化子控件属性
          self.textLabel.font = [UIFont systemFontOfSize:18];
          self.textLabel.textColor = [UIColor whiteColor];
          self.textLabellabel.numberOfLines = 0; // 最好设置这个属性,不等高cell的主要原因是由于label的高度随内容而变化,设置自动换行
      
          // 其他子控件属性
      }
      
  3. 同“代码实现不等高cell -> 通过frame计算及布局控件”第3步(提供模型属性)

    @class XXXModel;
    
    @interface XXXCell : UITableViewCell
    /** 模型数据 */
    @property (nonatomic, strong) XXXModel *model;
    @end
    
  4. 大致同“代码实现不等高cell -> 通过frame计算及布局控件”第4步(添加模型属性),但不需要计算及记录子控件的frame数据及cell的高度属性

    @interface XXXStatus : NSObject
    /**** 模型属性 ****/
    /** 名字 */
    @property (nonatomic ,strong) NSString *name;
    /** 图片 */
    @property (nonatomic ,strong) NSString *icon;
    // 其他模型属性
    
  5. 不需要“代码实现不等高cell -> 通过frame计算及布局控件”第5步(计算cellHeigth),因为约束会自动处理控件高度

  6. 同“代码实现不等高cell -> 通过frame计算及布局控件”第6步(重写模型属性的set方法,给子控件设置模型数据)

    - (void)setModel:(XXXModel *)model
    {
        _model = model;
    
        // 给子控件设置模型数据
        self.imageView.image = [UIImage imageNamed:self.model.icon];
        // .......
    }
    
  7. 不需要“代码实现不等高cell -> 通过frame计算及布局控件”第7步(在layoutSubviews布局子控件)

  8. 同“代码实现不等高cell -> 通过frame计算及布局控件”第8步(在控制器中,添加模型数组属性,并将数据源(字典数组)转化为模型数组)

    #pragma mark - lzay load
    - (NSMutableArray *)models
    {
        if (!_models) {
    
            // 获取字典数组
            // 字典数组转模型数组代码
        }
        return _models;
    }
    
  9. 同“代码实现不等高cell -> 通过frame计算及布局控件”第9步(注册cell的类型)(此步可选)

    [self.tableView registerClass:[XXXTgCell class] forCellReuseIdentifier:ID];
    
  10. 同“代码实现不等高cell -> 通过frame计算及布局控件”第10步(在控制器中,实现数据源方法)

    // 多少行cell
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return self.models.count;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString * const ID = @"model";// Identifier一般用cell的类名
    
        // 访问缓存池
        XXXCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
        // 创建cell,若第8步注册了,此处可省略
        // if (!cell) {
        cell = [[XXXCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:ID];
        }
    
        // 设置数据(传递模型数据)
        cell.model = self.models[indexPath.row];
    
        return cell;
    }
    
  11. 不需要“代码实现不等高cell -> 通过frame计算及布局控件”第11步(告诉tableView每行cell的高度)


通过storyBoard/xib实现UITableViewCell布局

通过storyBoard实现等高cell

  1. 新建一个继承自UITableViewCell的子类,比如XXXCell

    @interface XXXCell : UITableViewCell
    @end
    
  2. 在storyboard文件中,找到UITableView里面的cell(动态cell)

    • 修改cell的class为XXXCell


      Snip20150629_245.png
  1. 绑定循环利用标识


    Snip20150629_246.png
  1. 添加子控件,设置子控件约束


    Snip20150629_330.png
  1. 将子控件连线到类扩展中

    @interface XXXCell()
    @property (weak, nonatomic) IBOutlet UIImageView *iconImageView;
    @property (weak, nonatomic) IBOutlet UILabel *titleLabel;
    @property (weak, nonatomic) IBOutlet UILabel *priceLabel;
    @property (weak, nonatomic) IBOutlet UILabel *buyCountLabel;
    @end
    
  2. 在XXXCell.h文件中提供一个模型属性,比如XXXModel

    @class XXXModel;
    
    @interface XXXCell : UITableViewCell
    /** 团购模型数据 */
    @property (nonatomic, strong) XXXModel *model;
    @end
    
  3. 在XXXCell.m中重写模型属性的set方法

    - (void)setModel:(XXXModel *)model
    {
       _model = model;
    
       // 给子控件设置模型数据
       self.imageView.image = [UIImage imageNamed:self.model.icon];
       // 其他子控件属性设置
    }
    
  4. 在控制器中,添加模型数组属性,并将数据源(字典数组)转化为模型数组

    #pragma mark - lzay load
    - (NSMutableArray *)models
    {
        if (!_models) {
    
            // 获取字典数组
            // 字典数组转模型数组代码
        }
        return _models;
    }
    
  5. 在控制器中,实现数据源方法(不需要注册cell)

    // 多少行cell
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return self.models.count;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString * const ID = @"model";// Identifier一般用cell的类名
    
        // 访问缓存池
        XXXCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
        // 设置数据(传递模型数据)
        cell.model = self.models[indexPath.row];
    
        return cell;
    }
    

通过xib实现等高cell

  1. 新建一个继承自UITableViewCell的子类,比如XXXTgCell

    @interface XXXTgCell : UITableViewCell
    @end
    
  2. 新建一个xib文件(文件名最好跟类名一致,比如XXXCell.xib)

    1. 修改cell的class为XXXCell


      Snip20150629_245.png
  1. 绑定循环利用标识


    Snip20150629_246.png
  1. 添加子控件,设置子控件约束


    Snip20150629_251.png
  1. 将子控件连线到类扩展中

    @interface XXXCell()
    @property (weak, nonatomic) IBOutlet UIImageView *iconImageView;
    @property (weak, nonatomic) IBOutlet UILabel *titleLabel;
    @property (weak, nonatomic) IBOutlet UILabel *priceLabel;
    @property (weak, nonatomic) IBOutlet UILabel *buyCountLabel;
    @end
    
  2. 在XXXCell.h文件中提供一个模型属性,比如XXXModel

    @class XXXModel;
    
    @interface XXXCell : UITableViewCell
    /** 团购模型数据 */
    @property (nonatomic, strong) XXXModel *model;
    @end
    
  3. 在XXXCell.m中重写模型属性的set方法

    - (void)setModel:(XXXModel *)model
    {
       _model = model;
    
       // 给子控件设置模型数据
       self.imageView.image = [UIImage imageNamed:self.model.icon];
       // 其他子控件属性设置
    }
    
  4. 在控制器中,添加模型数组属性,并将数据源(字典数组)转化为模型数组

    #pragma mark - lzay load
    - (NSMutableArray *)models
    {
        if (!_models) {
    
            // 获取字典数组
            // 字典数组转模型数组代码
        }
        return _models;
    }
    
  5. 在控制器中,注册xib文件(必须)

    [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([XXXCell class]) bundle:nil] forCellReuseIdentifier:ID];
    
  6. 在控制器中,实现数据源方法

    // 多少行cell
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return self.models.count;
    }
    
    // 需要将ID定义抽出来
    static NSString * const ID = @"model";// ID一般用cell的类名
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        // 访问缓存池
        XXXCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
        // 设置数据(传递模型数据)
        cell.model = self.models[indexPath.row];
    
        return cell;
    }
    

通过storyBoard/xib实现不等高cell

对比通过storyBoard/xib实现等高cell,需要几个额外的步骤
  1. IOS8之后
    1. 在实现等高cell的第4步,还需要添加子控件和contentView之间的间距约束


      Snip20150701_88.png
2. 在控制器的viewDidLoad中设置tableViewCell的真实行高和估算行高
    ```objc
    // 告诉tableView所有cell的真实高度是自动计算(根据设置的约束来计算)
    self.tableView.rowHeight = UITableViewAutomaticDimension;
    // 告诉tableView所有cell的估算高度
    self.tableView.estimatedRowHeight = 44;
    ```
  1. iOS8之前
    1. 如果cell内部有自动换行的label,需要设置preferredMaxLayoutWidth属性

      - (void)awakeFromNib
      {
          // 手动设置文字的最大宽度(目的是:让label知道自己文字的最大宽度,进而能够计算出自己的frame)
          self.text_label.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;
      }
      
    2. 设置tableView的cell估算高度

      // 告诉tableView所有cell的估算高度(设置了估算高度,就可以减少tableView:heightForRowAtIndexPath:方法的调用次数)
      self.tableView.estimatedRowHeight = 200; // IOS7启用estimatedRowHeight
      
    3. 在代理方法中计算cell的高度

      XXXCell *cell;
      - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
      {
          // 创建一个cell(cell的作用:根据模型数据布局所有的子控件,进而计算出cell的高度)
          if (!cell) {
              cell = [tableView dequeueReusableCellWithIdentifier:ID];
          }
      
          // 设置模型数据
          cell.model = self.models[indexPath.row];
      
          return cell.height;
      }
      
      // 在XXXCell.h中添加height属性,在.m文件中实现计算
      - (CGFloat)height
      {
          // 强制布局cell内部的所有子控件(label根据文字多少计算出自己最真实的尺寸)
          [self layoutIfNeeded];
      
          // 计算cell的高度
          // 这里的场景是有些cell有大图片,有些cell没有大图片,cell的高度需要根据情况判断
          if (self.model.picture) {
              return CGRectGetMaxY(self.pictureImageView.frame) + 10;
          } else {
              return CGRectGetMaxY(self.text_label.frame) + 10;
          }
      }
      
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容