【支持iOS11】UITableView左滑删除自定义 - 实现多选项并使用自定义图片

写在开头:本文所介绍的方法使用的是iOS8-10中的API,不过同样支持在iOS11上运行。
<最近更新时间:2017-09-11>


目录:

  1. 效果展示
  2. 系统默认左滑菜单实现
  3. 自定义多个左滑菜单选项
  4. 自定义左滑菜单选项图标
    4.1 实现方法 (支持iOS8-10, 11)
    4.2 实现原理:UITableView视图层级(iOS8-10, 11)
  5. TableCell上有其他按钮的处理方法

1. 效果展示

本文介绍两种UITableView左滑菜单的实现方法,效果如下:

系统默认效果

swipe-default.PNG

自定义图标效果 (类似“邮件”应用)

swipe-customize-1.PNG


2. 系统默认左滑菜单实现

如果只需要使用默认图标,只需要在对应的TableViewController里实现数据源方法tableView:commitEditingStyle:forRowAtIndexPath就行了:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete)
    {
        [self removeAction:indexPath.section]; // 在此处自定义删除行为
    }
    else
    {
        DEBUG_OUT(@"Unhandled editing style: %ld", (long) editingStyle);
    }
}

向左滑动table cell,该cell会自动进入编辑模式(cell.isEditing = 1),并在右边出现删除按钮,红底白字,按钮上的文字会根据系统语言自动改变;点击该按钮则触发commitEditingStyle执行相应的动作。

如果不进行自定义,默认的左滑菜单只会有一个按钮,不过按钮上的文字可以用随意进行更改,按钮的宽度会根据文字标题长度自动调整,需要自己支持多语言:

- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
{    
    return @"想写什么都行";    
} 

效果如下:
change-title.PNG

PS:如果除了文字内容外,还想调整其它,比如文字颜色,背景颜色,选项的宽高等,则可以拿到对应的UIButton以后直接修改,具体方法参照本文2) b部分。


3. 自定义多个左滑菜单选项

如果需要超过一个左滑选项,需要实现代理方法tableView:editActionsForRowAtIndexPath,在里面创建多个UITableViewRowAction:

- (NSArray*)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{    
    // delete action
    UITableViewRowAction *deleteAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:NSLocalizedString(@"DeleteLabel", @"") handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
    {
        [tableView setEditing:NO animated:YES];  // 这句很重要,退出编辑模式,隐藏左滑菜单
        [self removeNotificationAction:index];
    }];
    
    // read action
    // 根据cell当前的状态改变选项文字
    NSInteger index = indexPath.section; 
    BOOL isRead = [[NotificationManager instance] read:index];
    NSString *readTitle = isRead ? @"Unread" : @"Read";

    // 创建action
    UITableViewRowAction *readAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:readTitle handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) 
    {        
        [tableView setEditing:NO animated:YES];  // 这句很重要,退出编辑模式,隐藏左滑菜单
        [[NotificationManager instance] setRead:!isRead index:index];
    }];
    
    return @[deleteAction, readAction];
}

可以看到这里我们创建了delete和read两个action。这是因为实现了该方法以后,1) 中用的commitEditingStyle:forRowAtIndexPath就不会被触发了,所以删除按钮也需要自己定义。

[tableView setEditing:NO animated:YES]; 这一行代码很重要,它的效果是在点击之后退出编辑模式,关闭左滑菜单。如果忘了加这一句的话,即使点击了按钮cell也不会还原。在1) 中使用默认模式的时候,系统会自动帮我们调用这一句,现在则需要手动调用。

对创建并返回的每个action,apple library会自动帮我们生成一个对应按钮,配置好基本的交互,并添加到左滑菜单中。

效果如下:
two-actions-2.PNG

上图我们对两个action都指定了UITableViewRowActionStyleNormal(灰底白字),不过其实有几种不同的预设外观 (不要问我为啥有两个都是红底白字。。。)
  UITableViewRowActionStyleNormal:灰底白字
  UITableViewRowActionStyleDefault:红底白字
  UITableViewRowActionStyleDestructive:红底白字

我们还可以更改action button的背景色,在创建action的时候添加一行代码即可:

deleteAction.backgroundColor = [UIColor orangeColor];
readAction.backgroundColor = [UIColor blueColor];

效果如下:
change-background-2.PNG



4. 自定义左滑菜单选项外观

自定义左滑选项外观的资料很少,我做的时候找得相当辛苦。不过后来理解了UITableView的视图层级,一切就变得很简单了。先放实现方法,再放原理解释。

4.1 实现方法 (支持iOS8-10, 11)

为了同时支持iOS8-10和iOS11, 我把操作选项外观的代码统一放在UITableView的ViewController的- (void)viewDidLayoutSubviews实现。

这样做的原因有两个:

  1. 原本因为iOS8-10中,左滑选项是UITableViewCell的子视图,而在iOS11中,左滑选项变成了UITableView的子视图。虽然可以用tabelCell.superview来获取tableView,不过我认为最好从高层级去操作低层级。所以统一在UITableView层处理。
  2. iOS8-10的UITableViewCellDeleteConfirmationView子视图出现得较晚。在代理方法willBeginEditingRowAtIndexPath中还没有出现,而在viewDidLayoutSubviews则可以保证子视图出现。

首先我们遍历UITableView的子视图拿到选项按钮(UIButton)的reference,对iOS8-10和iOS11做不同处理:

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)

@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (strong, nonatomic) NSIndexPath* editingIndexPath;  //当前左滑cell的index,在代理方法中设置

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    
    if (self.editingIndexPath)
    {
        [self configSwipeButtons];
    }
}

Xcode 8 编译版本:(如果你使用的是Xcode 9,参见下面)

- (void)configSwipeButtons
{
    // 获取选项按钮的reference
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"11.0"))
    {
        // iOS 11层级 (Xcode 8编译): UITableView -> UITableViewWrapperView -> UISwipeActionPullView
        for (UIView *subview in self.tableView.subviews)
        {
            if ([subview isKindOfClass:NSClassFromString(@"UITableViewWrapperView")])
            {
                for (UIView *subsubview in subview.subviews)
                {
                    if ([subsubview isKindOfClass:NSClassFromString(@"UISwipeActionPullView")] && [subsubview.subviews count] >= 2)
                    {
                        // 和iOS 10的按钮顺序相反
                        UIButton *deleteButton = subsubview.subviews[1];
                        UIButton *readButton = subsubview.subviews[0];
                        
                        [self configDeleteButton:deleteButton];
                        [self configReadButton:readButton];
                    }
                }
            }
        }
    }
    else
    {
        // iOS 8-10层级: UITableView -> UITableViewCell -> UITableViewCellDeleteConfirmationView
        NotificationCell *tableCell = [self.tableView cellForRowAtIndexPath:self.editingIndexPath];
        for (UIView *subview in tableCell.subviews)
        {
            if ([subview isKindOfClass:NSClassFromString(@"UITableViewCellDeleteConfirmationView")] && [subview.subviews count] >= 2)
            {
                UIButton *deleteButton = subview.subviews[0];
                UIButton *readButton = subview.subviews[1];
                
                [self configDeleteButton:deleteButton];
                [self configReadButton:readButton];
                [subview setBackgroundColor:[[ColorUtil instance] colorWithHexString:@"E5E8E8"]];
            }
        }
    }
    
    [self configDeleteButton:deleteButton];
    [self configReadButton:readButton];
}

Xcode 9 编译版本:(比Xcode 8编译出来少一层)

- (void)configSwipeButtons
{
    // 获取选项按钮的reference
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"11.0"))
    {
        // iOS 11层级 (Xcode 9编译): UITableView -> UISwipeActionPullView
        for (UIView *subview in self.tableView.subviews)
        {
              if ([subview isKindOfClass:NSClassFromString(@"UISwipeActionPullView")] && [subview.subviews count] >= 2)
              {
                   // 和iOS 10的按钮顺序相反
                   UIButton *deleteButton = subsubview.subviews[1];
                   UIButton *readButton = subsubview.subviews[0];
                        
                   [self configDeleteButton:deleteButton];
                   [self configReadButton:readButton];
              }
        }
    }
    else
    {
        // iOS 8-10层级: UITableView -> UITableViewCell -> UITableViewCellDeleteConfirmationView
        NotificationCell *tableCell = [self.tableView cellForRowAtIndexPath:self.editingIndexPath];
        for (UIView *subview in tableCell.subviews)
        {
            if ([subview isKindOfClass:NSClassFromString(@"UITableViewCellDeleteConfirmationView")] && [subview.subviews count] >= 2)
            {
                UIButton *deleteButton = subview.subviews[0];
                UIButton *readButton = subview.subviews[1];
                
                [self configDeleteButton:deleteButton];
                [self configReadButton:readButton];
                [subview setBackgroundColor:[[ColorUtil instance] colorWithHexString:@"E5E8E8"]];
            }
        }
    }
    
    [self configDeleteButton:deleteButton];
    [self configReadButton:readButton];
}

注意一下这里我们用到了一个变量self.editingIndexPath,这代表着当前左滑的cell的index,方便我们获取iOS8-10上面的tableCell的reference。分别在控制进入和退出编辑模式的代理方法中设置的:

- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
    self.editingIndexPath = indexPath;
    [self.view setNeedsLayout];   // 触发-(void)viewDidLayoutSubviews
}

- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
    self.editingIndexPath = nil;
}

[self.view setNeedsLayout]; 这一句非常重要,它的作用强制UITableView重新绘图。只有添加了这一句,- (void)viewDidLayoutSubviews才会被调用,才能使我们的自定义外观生效。


好了,我们已经拿到了按钮(UIButton)的reference,然后就可以给按钮添加了图片,并且设置文本的字体和颜色了。

- (void)configDeleteButton:(UIButton*)deleteButton
{
    if (deleteButton)
    {
        [deleteButton.titleLabel setFont:[UIFont fontWithName:@"SFUIText-Regular" size:12.0]];
        [deleteButton setTitleColor:[[ColorUtil instance] colorWithHexString:@"D0021B"] forState:UIControlStateNormal];
        [deleteButton setImage:[UIImage imageNamed:@"Delete_icon_.png"] forState:UIControlStateNormal];
        [deleteButton setBackgroundColor:[[ColorUtil instance] colorWithHexString:@"E5E8E8"]];
        // 调整按钮上图片和文字的相对位置(该方法的实现在下面)
        [self centerImageAndTextOnButton:deleteButton]; 
    }
}

- (void)configReadButton:(UIButton*)readButton
{
    if (readButton)
    {
        [readButton.titleLabel setFont:[UIFont fontWithName:@"SFUIText-Regular" size:12.0]];
        [readButton setTitleColor:[[ColorUtil instance] colorWithHexString:@"4A90E2"] forState:UIControlStateNormal];
        // 根据当前状态选择不同图片
        BOOL isRead = [[NotificationManager instance] read:self.editingIndexPath.row];
        UIImage *readButtonImage = [UIImage imageNamed: isRead ? @"Mark_as_unread_icon_.png" : @"Mark_as_read_icon_.png"];
        [readButton setImage:readButtonImage forState:UIControlStateNormal];

        [readButton setBackgroundColor:[[ColorUtil instance] colorWithHexString:@"E5E8E8"]];
        // 调整按钮上图片和文字的相对位置(该方法的实现在下面)
        [self centerImageAndTextOnButton:readButton];
    }
}

如果没有[self centerImageAndTextOnButton:readButton],则效果如下:
after-layoutsubviews-1.PNG

可以看到图标在左,文字在右,还互相重合。这就是我们熟悉的UIButton的外观处理,需要分别修改UILabel和UIImageView的frame:

- (void)centerImageAndTextOnButton:(UIButton*)button
{
    // this is to center the image and text on button.
    // the space between the image and text
    CGFloat spacing = 35.0;
    
    // lower the text and push it left so it appears centered below the image
    CGSize imageSize = button.imageView.image.size;
    button.titleEdgeInsets = UIEdgeInsetsMake(0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);
    
    // raise the image and push it right so it appears centered above the text
    CGSize titleSize = [button.titleLabel.text sizeWithAttributes:@{NSFontAttributeName: button.titleLabel.font}];
    button.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + spacing), 0.0, 0.0, - titleSize.width);
    
    // increase the content height to avoid clipping
    CGFloat edgeOffset = (titleSize.height - imageSize.height) / 2.0;
    button.contentEdgeInsets = UIEdgeInsetsMake(edgeOffset, 0.0, edgeOffset, 0.0);
    
    // move whole button down, apple placed the button too high in iOS 10
    if (SYSTEM_VERSION_LESS_THAN(@"11.0"))
    {
        CGRect btnFrame = button.frame;
        btnFrame.origin.y = 18;
        button.frame = btnFrame;
    }
}

调整过后就可以做到文章开头展示的效果了:
swipe-customize-2.PNG

PS:假如仅支持iOS8-10的话,我个人是倾向于创建一个custom class,继承UITableViewCell,然后在该custom class中-(void)layoutSubviews来实现的。那样代码更干净,不需要特意去调用[self.view setNeedsLayout]; 不过为了支持新版本总是要有所牺牲的。


4.2 实现原理:UITableView视图层级(iOS8-10, 11)

不想看原理可以跳过这部分。

和iOS8-10相比,iOS11的左滑选项的视图层级有了较大改变。最显著的改变是从是UITableViewCell的子视图变成了UITableView的子视图。总结一下就是:

iOS 8-10: UITableView -> UITableViewCell -> UITableViewCellDeleteConfirmationView -> _UITableViewCellActionButton
iOS 11 (Xcode 8编译): UITableView -> UITableViewWrapperView -> UISwipeActionPullView -> UISwipeActionStandardButton
iOS 11 (Xcode 9编译): UITableView -> UISwipeActionPullView -> UISwipeActionStandardButton

--

iOS8-10下的层级:

在tableView代理方法里设置断点打印发现,正常状态下cell上只有两个subview:

(lldb) po [tableCell subviews]
<__NSArrayM 0x14de75670>(
<UITableViewCellContentView: 0x14dd56940; frame = (0 0; 440 105); opaque = NO; gestureRecognizers = <NSArray: 0x14dd65ff0>; layer = <CALayer: 0x14dd56ac0>>,
<_UITableViewCellSeparatorView: 0x14dd73810; frame = (15 154.5; 347 0.5); layer = <CALayer: 0x14dd736f0>>
)

而在左滑进入editing mode之后,就变成了3个,多出来一个 叫做UITableViewCellDeleteConfirmationView的子视图:

(lldb) po [tableCell subviews]
<__NSArrayM 0x14de75670>(
<UITableViewCellDeleteConfirmationView: 0x14de737d0; frame = (375 0; 0 105); clipsToBounds = YES; autoresize = H; animations = { bounds.origin=<CASpringAnimation: 0x14db56aa0>; bounds.size=<CASpringAnimation: 0x14db5b2e0>; position=<CASpringAnimation: 0x14db498e0>; bounds.origin-2=<CASpringAnimation: 0x14db8a3d0>; bounds.size-2=<CASpringAnimation: 0x14db26c10>; }; layer = <CALayer: 0x14de72fc0>>,
<UITableViewCellContentView: 0x14dd56940; frame = (0 0; 375 105); opaque = NO; gestureRecognizers = <NSArray: 0x14dd65ff0>; layer = <CALayer: 0x14dd56ac0>>,
<_UITableViewCellSeparatorView: 0x14dd73810; frame = (15 154.5; 347 0.5); layer = <CALayer: 0x14dd736f0>>
)

再进一步查看这个多出来的UITableViewCellDeleteConfirmationView的子视图,发现两个UIButton:

(lldb) po tableCell.subviews[0].subviews
<__NSArrayM 0x14dea14c0>(
<_UITableViewCellActionButton: 0x14de93ea0; frame = (71.5 0; 80.5 105); opaque = NO; autoresize = H; layer = <CALayer: 0x14de93150>>,
<_UITableViewCellActionButton: 0x14de9d900; frame = (0 0; 71.5 105); opaque = NO; autoresize = H; layer = <CALayer: 0x14de9dbb0>>
)

最后再打印一下这两个UIButton的title,发现分别是“Read”和“Delete”。

也就是说,这两个UIButton,分别对应我们在a部分创建的两个UITableViewRowAction。所以我们只要遍历UITableViewCell的子视图,拿到对应UIButton的reference,什么修改高度,添加图片,修改字体,都是手到擒来。

--

iOS11下的层级 (用Xcode 8编译)

依然在tableView代理方法里设置断点打印发现,UITableViewCell下面没有UITableViewCellDeleteConfirmationView子视图了,不过在UITableViewWrapperView下面,多了一个UISwipeActionPullView。

(lldb) po self.tableView.subviews
<__NSArrayM 0x1c0652cf0>(
<UITableViewWrapperView: 0x105094200; frame = (0 0; 1000 1000); gestureRecognizers = <NSArray: 0x1c4457e50>; layer = <CALayer: 0x1c4224de0>; contentOffset: {0, 0}; contentSize: {1000, 1000}; adjustedContentInset: {0, 0, 0, 0}>,
<UIImageView: 0x10466bb20; frame = (994.5 733; 2.5 220); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x1c0426900>>
)

(lldb) po [self.tableView.subviews[0] subviews]
<__NSArrayM 0x1c04457f0>(
<UITableViewCell: 0x10e17d800; baseClass = UITableViewCell; frame = (0 277.5; 375 212.5); autoresize = W; layer = <CALayer: 0x1c0427c80>>,
<: UITableViewCell: 0x10e165200; baseClass = UITableViewCell; frame = (0 8; 375 261.5); autoresize = W; userInteractionEnabled = NO; layer = <CALayer: 0x1c0423300>>,
<UISwipeActionPullView: 0x10de126a0; cellEdge = UIRectEdgeRight, actions = <NSArray: 0x1c44430c0>>
)

再看一下这个UISwipeActionPullView子视图,发现了我们要找的选项按钮:

(lldb) po subview2.subviews
<__NSArrayM 0x1c0452ba0>(
<UISwipeActionStandardButton: 0x14deae190; frame = (0 0; 591 104.5); opaque = NO; autoresize = W+H; tintColor = UIExtendedGrayColorSpace 1 1; layer = <CALayer: 0x1c0435860>>,
<UISwipeActionStandardButton: 0x14debd7c0; frame = (0 0; 591 104.5); opaque = NO; autoresize = W+H; tintColor = UIExtendedGrayColorSpace 1 1; layer = <CALayer: 0x1c0621200>>
)

这两个button的title和action都和我们之前所创建的左滑选项相对应,所以我们可以用类似的方法遍历UITableView的子视图,拿到对应UIButton的reference进行修改。

--

iOS11下的层级 (用Xcode 9编译)

Xcode 9 默认使用iOS11 SDK来编译,添加打印后发现Xcode 9 编译出来的没有UITableViewWrapperView这一层,UISwipeActionPullView的子视图直接附属于UITableViewCell。

除了少了一层UITableViewWrapperView以外,其他和Xcode 8编译出来的一样。

放一下Xcode 8、9编译的对比图:

xcode8.png
xcode9.png



5. TableCell上有其它按钮的处理方法

我自己做的时候遇到了一种特殊情况,UITableViewCell上面带有比较显著的button,类似下图的这种情况:
swipe-button-1.PNG

这种情况比较尴尬的就是当你左滑的时候如果刚好碰到了YES或者NO button, 在进入左滑选项的同时会触发按钮选项,相当容易引发误操作。为了解决这个问题,我在TableViewController中实现了下面两个代理方法:

- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *tableCell = [tableView cellForRowAtIndexPath:indexPath];
    // disable button touch event during swipe
    for (UIView *view in [tableCell.contentView subviews])
    {
        if ([view isKindOfClass:[UIButton class]]) {
            [view setUserInteractionEnabled:NO];
        }
    }
}

- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *tableCell = [tableView cellForRowAtIndexPath:indexPath];
    for (UIView *view in [tableCell.contentView subviews])
    {
        if ([view isKindOfClass:[UIButton class]]) {
            [view setUserInteractionEnabled:YES];
        }
    }
}

tableView:willBeginEditingRowAtIndexPath是在cell进入editing mode之前调用的,在这里将contentView下面的所有按钮的交互设置为disabled。
tableView:didEndEditingRowAtIndexPath是在cell即将退出editing mode时调用的,在这里将之前被disable的所有button的交互重新设置为enabled。

这样就可以保证在左滑菜单出现的时候,原本cell上的那些按钮都处于不能点按的状态,也就不会触发误操作了。

PS: 试过直接disable 整个contentView不起作用,必须直接disable对应的UIButton才行,推测跟apple自己处理event的有限次序有关。

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

推荐阅读更多精彩内容

  • 之前写过一篇UITableView定制自定义cell左滑删除按钮小窍门,当时电脑未升级xcode还没有升级,所以当...
    flowerflower阅读 7,489评论 16 9
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,611评论 4 59
  • 人会长大三次。第一次是在发现自己不是世界中心的时候。第二次是在发现即使再怎么努力,终究还是有些事令人无能为力的时候...
    又一个小太阳阅读 181评论 2 0
  • 或许没有结局的结尾才让人刻骨铭心。现在他跟另一个女孩在一起,而我也在期待属于我的一辈子。曾经觉得除了他不会有人会让...
    錵錵阅读 378评论 0 1
  • 热气球,帝王谷,哈特谢普苏特神庙,卡纳克神庙,门农巨像。 人生中的第一次热气球,虽然降落不算浪漫但经历真的美好。然...
    乔乔乔木阅读 157评论 0 1