iOS 循环滚动视图,包含文字滚动,banner 滚动

iOS 循环滚动视图,包含文字滚动,banner 滚动

效果图:

Demo.gif

demo 地址:
https://github.com/karosLi/KLRecycleScrollView

功能:

  • 支持上下滚动和左右滚动
  • 支持自定义子视图
  • 支持分页和不分页
  • 支持自动滚动

实现代码:

//
//  KLRecycleScrollView.m
//  KLRecycleScrollView
//
//  Created by karos li on 2017/12/25.
//  Copyright © 2017年 karos. All rights reserved.
//

#import "KLRecycleScrollView.h"

@class KLInfiniteScrollView;
@protocol KLInfiniteScrollViewDelegate <NSObject>

- (UIView *)infiniteScrollView:(KLInfiniteScrollView *)infiniteScrollView viewForItemAtIndex:(NSInteger)index;
- (void)infiniteScrollView:(KLInfiniteScrollView *)infiniteScrollView didSelectView:(UIView *)view forItemAtIndex:(NSInteger)index;

@end

@interface KLInfiniteScrollView : UIScrollView

@property (nonatomic, strong, readonly) NSMutableArray *visibleViews;
@property (nonatomic, weak) id<KLInfiniteScrollViewDelegate> infiniteDelegate;

// 是否是水平布局
@property (nonatomic, assign) BOOL isHorLayout;

- (void)reloadData:(NSInteger)numberOfItems;

// 获取子视图距离最左边的距离
- (CGFloat)getDistanceToLeftEdge:(UIView *)view;

// 获取子视图距离最上边的距离
- (CGFloat)getDistanceToTopEdge:(UIView *)view;

@end

@interface KLInfiniteScrollView ()

@property (nonatomic, strong) NSMutableArray *visibleViews;
@property (nonatomic, strong) UIView *containerView;

@property (nonatomic, assign) NSInteger numberOfItems;
@property (nonatomic, assign) NSInteger rightMostVisibleViewIndex;
@property (nonatomic, assign) NSInteger leftMostVisibleViewIndex;
@property (nonatomic, assign) NSInteger topMostVisibleViewIndex;
@property (nonatomic, assign) NSInteger bottomMostVisibleViewIndex;

@end

@implementation KLInfiniteScrollView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    self.isHorLayout = YES;
    self.contentSize = CGSizeMake(5000, self.frame.size.height);
    self.visibleViews = [[NSMutableArray alloc] init];
    
    self.containerView = [[UIView alloc] init];
    self.containerView.frame = CGRectMake(0, 0, self.contentSize.width, self.contentSize.height);
    [self addSubview:self.containerView];
    
    self.bounces = NO;
    self.showsHorizontalScrollIndicator = NO;
    self.showsVerticalScrollIndicator = NO;
    
    return self;
}

#pragma mark - public
- (void)reloadData:(NSInteger)numberOfItems {
    self.numberOfItems = numberOfItems;
    [self setNeedsLayout];
}

- (void)setIsHorLayout:(BOOL)isHorLayout {
    _isHorLayout = isHorLayout;
    [self.visibleViews removeAllObjects];
    if (isHorLayout) {
        self.contentSize = CGSizeMake(5000, self.frame.size.height);
        self.containerView.frame = CGRectMake(0, 0, self.contentSize.width, self.contentSize.height);
    } else {
        self.contentSize = CGSizeMake(self.frame.size.width, 5000);
        self.containerView.frame = CGRectMake(0, 0, self.contentSize.width, self.contentSize.height);
    }
}

- (CGFloat)getDistanceToLeftEdge:(UIView *)view {
    CGRect visibleBounds = [self convertRect:[self bounds] toView:self.containerView];
    return CGRectGetMinX(view.frame) - CGRectGetMinX(visibleBounds);
}

- (CGFloat)getDistanceToTopEdge:(UIView *)view {
    CGRect visibleBounds = [self convertRect:[self bounds] toView:self.containerView];
    return CGRectGetMinY(view.frame) - CGRectGetMinY(visibleBounds);
}

#pragma mark - gesture
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UIView *hitView;
    for (UIView *view in self.containerView.subviews) {
        CGPoint point = [touches.anyObject locationInView:view];
        BOOL hasHit = [view pointInside:point withEvent:event];
        if (hasHit) {
            hitView = view;
            break;
        }
    }
    
    if (hitView.tag >= 20000) {
        NSInteger index = hitView.tag - 20000;
        if ([self.infiniteDelegate respondsToSelector:@selector(infiniteScrollView:didSelectView:forItemAtIndex:)]) {
            [self.infiniteDelegate infiniteScrollView:self didSelectView:hitView forItemAtIndex:index];
        }
    }
}

#pragma mark - Layout

// recenter content periodically to achieve impression of infinite scrolling
- (void)recenterInHorIfNecessary {
    CGPoint currentOffset = [self contentOffset];
    CGFloat contentWidth = [self contentSize].width;
    CGFloat centerOffsetX = (contentWidth - [self bounds].size.width) / 2.0;
    CGFloat distanceFromCenter = fabs(currentOffset.x - centerOffsetX);
    
    if (distanceFromCenter > (contentWidth / 4.0)) {
        self.contentOffset = CGPointMake(centerOffsetX, currentOffset.y);
        
        // move content by the same amount so it appears to stay still
        for (UIView *label in self.visibleViews) {
            CGPoint center = [self.containerView convertPoint:label.center toView:self];
            center.x += (centerOffsetX - currentOffset.x);
            label.center = [self convertPoint:center toView:self.containerView];
        }
    }
}

- (void)recenterInVerIfNecessary {
    CGPoint currentOffset = [self contentOffset];
    CGFloat contentHeight = [self contentSize].height;
    CGFloat centerOffsetY = (contentHeight - [self bounds].size.height) / 2.0;
    CGFloat distanceFromCenter = fabs(currentOffset.y - centerOffsetY);
    
    if (distanceFromCenter > (contentHeight / 4.0)) {
        self.contentOffset = CGPointMake(currentOffset.x, centerOffsetY);
        
        // move content by the same amount so it appears to stay still
        for (UIView *label in self.visibleViews) {
            CGPoint center = [self.containerView convertPoint:label.center toView:self];
            center.y += (centerOffsetY - currentOffset.y);
            label.center = [self convertPoint:center toView:self.containerView];
        }
    }
}


- (void)layoutSubviews {
    if (self.numberOfItems > 0) {
        if (self.isHorLayout) {
            [self recenterInHorIfNecessary];
            
            // tile content in visible bounds
            CGRect visibleBounds = [self convertRect:[self bounds] toView:self.containerView];
            CGFloat minimumVisibleX = CGRectGetMinX(visibleBounds);
            CGFloat maximumVisibleX = CGRectGetMaxX(visibleBounds);
            
            [self tileViewsFromMinX:minimumVisibleX toMaxX:maximumVisibleX];
        } else {
            [self recenterInVerIfNecessary];
            
            // tile content in visible bounds
            CGRect visibleBounds = [self convertRect:[self bounds] toView:self.containerView];
            CGFloat minimumVisibleY = CGRectGetMinY(visibleBounds);
            CGFloat maximumVisibleY = CGRectGetMaxY(visibleBounds);
            
            [self tileViewsFromMinY:minimumVisibleY toMaxY:maximumVisibleY];
        }
    }
    
    [super layoutSubviews];
}

#pragma mark - hor View Tiling

- (CGFloat)placeNewViewOnRight:(CGFloat)rightEdge {
    _rightMostVisibleViewIndex++;
    if (_rightMostVisibleViewIndex == self.numberOfItems) {
        _rightMostVisibleViewIndex = 0;
    }
    
    UIView *view;
    if ([self.infiniteDelegate respondsToSelector:@selector(infiniteScrollView:viewForItemAtIndex:)]) {
        view = [self.infiniteDelegate infiniteScrollView:self viewForItemAtIndex:_rightMostVisibleViewIndex];
    }
    
    if (!view) {
        view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
    }
    
    view.tag = 20000 + _rightMostVisibleViewIndex;
    [_containerView addSubview:view];
    [_visibleViews addObject:view]; // add rightmost label at the end of the array
    
    CGRect frame = [view frame];
    frame.origin.x = rightEdge;
    frame.origin.y = 0;
    [view setFrame:frame];
    return CGRectGetMaxX(frame);
}

- (CGFloat)placeNewViewOnLeft:(CGFloat)leftEdge {
    _leftMostVisibleViewIndex--;
    if (_leftMostVisibleViewIndex < 0) {
        _leftMostVisibleViewIndex = self.numberOfItems - 1;
    }

    UIView *view;
    if ([self.infiniteDelegate respondsToSelector:@selector(infiniteScrollView:viewForItemAtIndex:)]) {
        view = [self.infiniteDelegate infiniteScrollView:self viewForItemAtIndex:_leftMostVisibleViewIndex];
    }
    
    if (!view) {
        view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
    }
    
    view.tag = 20000 + _leftMostVisibleViewIndex;
    [_containerView addSubview:view];
    [_visibleViews insertObject:view atIndex:0]; // add leftmost label at the beginning of the array
    
    CGRect frame = [view frame];
    frame.origin.x = leftEdge - frame.size.width;
    frame.origin.y = 0;
    [view setFrame:frame];
    
    return CGRectGetMinX(frame);
}

- (void)tileViewsFromMinX:(CGFloat)minimumVisibleX toMaxX:(CGFloat)maximumVisibleX {
    // the upcoming tiling logic depends on there already being at least one label in the visibleLabels array, so
    // to kick off the tiling we need to make sure there's at least one label
    if ([_visibleViews count] == 0) {
        _rightMostVisibleViewIndex = -1;
        _leftMostVisibleViewIndex = 0;
        [self placeNewViewOnRight:minimumVisibleX];
    }
    
    // add labels that are missing on right side
    UIView *lastView = [_visibleViews lastObject];
    CGFloat rightEdge = CGRectGetMaxX([lastView frame]);
    
    while (rightEdge < maximumVisibleX) {
        rightEdge = [self placeNewViewOnRight:rightEdge];
    }
    
    // add labels that are missing on left side
    UIView *firstView = _visibleViews[0];
    CGFloat leftEdge = CGRectGetMinX([firstView frame]);
    while (leftEdge > minimumVisibleX) {
        leftEdge = [self placeNewViewOnLeft:leftEdge];
    }
    
    // remove labels that have fallen off right edge
    lastView = [_visibleViews lastObject];
    while ([lastView frame].origin.x > maximumVisibleX) {
        [lastView removeFromSuperview];
        [_visibleViews removeLastObject];
        lastView = [_visibleViews lastObject];
        
        _rightMostVisibleViewIndex--;
        if (_rightMostVisibleViewIndex < 0) {
            _rightMostVisibleViewIndex = self.numberOfItems - 1;
        }
    }
    
    // remove labels that have fallen off left edge
    firstView = _visibleViews[0];
    while (CGRectGetMaxX([firstView frame]) < minimumVisibleX) {
        [firstView removeFromSuperview];
        [_visibleViews removeObjectAtIndex:0];
        firstView = _visibleViews[0];
        
        _leftMostVisibleViewIndex++;
        if (_leftMostVisibleViewIndex == self.numberOfItems) {
            _leftMostVisibleViewIndex = 0;
        }
    }
}

#pragma mark - ver View Tiling

- (CGFloat)placeNewViewOnBottom:(CGFloat)bottomEdge {
    _bottomMostVisibleViewIndex++;
    if (_bottomMostVisibleViewIndex == self.numberOfItems) {
        _bottomMostVisibleViewIndex = 0;
    }
    
    UIView *view;
    if ([self.infiniteDelegate respondsToSelector:@selector(infiniteScrollView:viewForItemAtIndex:)]) {
        view = [self.infiniteDelegate infiniteScrollView:self viewForItemAtIndex:_bottomMostVisibleViewIndex];
    }
    
    if (!view) {
        view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
    }
    
    view.tag = 20000 + _bottomMostVisibleViewIndex;
    [_containerView addSubview:view];
    [_visibleViews addObject:view]; // add rightmost label at the end of the array
    
    CGRect frame = [view frame];
    frame.origin.x = 0;
    frame.origin.y = bottomEdge;
    [view setFrame:frame];
    return CGRectGetMaxY(frame);
}

- (CGFloat)placeNewViewOnTop:(CGFloat)topEdge {
    _topMostVisibleViewIndex--;
    if (_topMostVisibleViewIndex < 0) {
        _topMostVisibleViewIndex = self.numberOfItems - 1;
    }
    
    UIView *view;
    if ([self.infiniteDelegate respondsToSelector:@selector(infiniteScrollView:viewForItemAtIndex:)]) {
        view = [self.infiniteDelegate infiniteScrollView:self viewForItemAtIndex:_topMostVisibleViewIndex];
    }
    
    if (!view) {
        view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
    }
    
    view.tag = 20000 + _topMostVisibleViewIndex;
    [_containerView addSubview:view];
    [_visibleViews insertObject:view atIndex:0]; // add leftmost label at the beginning of the array
    
    CGRect frame = [view frame];
    frame.origin.x = 0;
    frame.origin.y = topEdge - frame.size.height;
    [view setFrame:frame];
    
    return CGRectGetMinY(frame);
}

- (void)tileViewsFromMinY:(CGFloat)minimumVisibleY toMaxY:(CGFloat)maximumVisibleY {
    // the upcoming tiling logic depends on there already being at least one label in the visibleLabels array, so
    // to kick off the tiling we need to make sure there's at least one label
    if ([_visibleViews count] == 0) {
        _bottomMostVisibleViewIndex = -1;
        _topMostVisibleViewIndex = 0;
        [self placeNewViewOnBottom:minimumVisibleY];
    }
    
    // add labels that are missing on right side
    UIView *lastView = [_visibleViews lastObject];
    CGFloat bottomEdge = CGRectGetMaxY([lastView frame]);
    
    while (bottomEdge < maximumVisibleY) {
        bottomEdge = [self placeNewViewOnBottom:bottomEdge];
    }
    
    // add labels that are missing on left side
    UIView *firstView = _visibleViews[0];
    CGFloat topEdge = CGRectGetMinY([firstView frame]);
    while (topEdge > minimumVisibleY) {
        topEdge = [self placeNewViewOnTop:topEdge];
    }
    
    // remove labels that have fallen off right edge
    lastView = [_visibleViews lastObject];
    while ([lastView frame].origin.y > maximumVisibleY) {
        [lastView removeFromSuperview];
        [_visibleViews removeLastObject];
        lastView = [_visibleViews lastObject];
        
        _bottomMostVisibleViewIndex--;
        if (_bottomMostVisibleViewIndex < 0) {
            _bottomMostVisibleViewIndex = self.numberOfItems - 1;
        }
    }
    
    // remove labels that have fallen off left edge
    firstView = _visibleViews[0];
    while (CGRectGetMaxY([firstView frame]) < minimumVisibleY) {
        [firstView removeFromSuperview];
        [_visibleViews removeObjectAtIndex:0];
        firstView = _visibleViews[0];
        
        _topMostVisibleViewIndex++;
        if (_topMostVisibleViewIndex == self.numberOfItems) {
            _topMostVisibleViewIndex = 0;
        }
    }
}

@end

@interface KLRecycleScrollView() <UIScrollViewDelegate, KLInfiniteScrollViewDelegate>

@property (strong, nonatomic) KLInfiniteScrollView *scrollView;
@property (strong, nonatomic) NSMutableArray *containerViews;

@property (assign, nonatomic) NSInteger totalItemsCount;
@property (strong, nonatomic) NSTimer *timer;

@end

@implementation KLRecycleScrollView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    self.scrollInterval = 3.5;
    [self setupView];
    
    return self;
}

- (void)setupView {
    self.scrollView = [[KLInfiniteScrollView alloc] initWithFrame:self.bounds];
    self.scrollView.delegate = self;
    self.scrollView.infiniteDelegate = self;
    [self addSubview:self.scrollView];
}

#pragma mark - public methods
- (void)reloadData:(NSInteger)totalItemsCount {
    self.totalItemsCount = totalItemsCount;
    [self.scrollView reloadData:totalItemsCount];
    
    [self startTimer];
}

- (void)setPagingEnabled:(BOOL)pagingEnabled {
    _pagingEnabled = pagingEnabled;
    self.scrollView.decelerationRate = pagingEnabled ? UIScrollViewDecelerationRateFast : UIScrollViewDecelerationRateNormal;
}

- (void)setDirection:(KLRecycleScrollViewDirection)direction {
    _direction = direction;
    
    if (self.direction == KLRecycleScrollViewDirectionLeft || self.direction == KLRecycleScrollViewDirectionRight) {
        self.scrollView.isHorLayout = YES;
    } else if (self.direction == KLRecycleScrollViewDirectionTop || self.direction == KLRecycleScrollViewDirectionBottom) {
        self.scrollView.isHorLayout = NO;
    }
}

#pragma mark - timer
- (void)fireTimer {
    [UIView animateWithDuration:0.75 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        if (self.direction == KLRecycleScrollViewDirectionLeft) {
            CGPoint contentOffset = self.scrollView.contentOffset;
            [self.scrollView setContentOffset:CGPointMake(contentOffset.x + self.bounds.size.width, 0) animated:NO];
        } else if (self.direction == KLRecycleScrollViewDirectionRight) {
            CGPoint contentOffset = self.scrollView.contentOffset;
            [self.scrollView setContentOffset:CGPointMake(contentOffset.x - self.bounds.size.width, 0) animated:NO];
        } else if (self.direction == KLRecycleScrollViewDirectionTop) {
            CGPoint contentOffset = self.scrollView.contentOffset;
            [self.scrollView setContentOffset:CGPointMake(0, contentOffset.y + self.bounds.size.height) animated:NO];
        } else if (self.direction == KLRecycleScrollViewDirectionBottom) {
            CGPoint contentOffset = self.scrollView.contentOffset;
            [self.scrollView setContentOffset:CGPointMake(0, contentOffset.y - self.bounds.size.height) animated:NO];
        }
    } completion:^(BOOL finished) {
    }];
}

- (void)configTimer {
    self.timer = [NSTimer timerWithTimeInterval:self.scrollInterval target:self selector:@selector(fireTimer) userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}

- (void)startTimer {
    if (self.timerEnabled && !self.timer) {
        [self configTimer];
    }
}

- (void)stopTimer {
    if (self.timerEnabled) {
        [self.timer invalidate];
        self.timer = nil;
    }
}

#pragma mark - KLInfiniteScrollViewDelegate
- (UIView *)infiniteScrollView:(KLInfiniteScrollView *)infiniteScrollView viewForItemAtIndex:(NSInteger)index {
    UIView *subview;
    if ([self.delegate respondsToSelector:@selector(recycleScrollView:viewForItemAtIndex:)]) {
        subview = [self.delegate recycleScrollView:self viewForItemAtIndex:index];
    }
    
    subview.frame = self.bounds;
    return subview;
}

- (void)infiniteScrollView:(KLInfiniteScrollView *)infiniteScrollView didSelectView:(UIView *)view forItemAtIndex:(NSInteger)index {
    if ([self.delegate respondsToSelector:@selector(recycleScrollView:didSelectView:forItemAtIndex:)]) {
        [self.delegate recycleScrollView:self didSelectView:view forItemAtIndex:index];
    }
}

#pragma mark - override
- (void)setClipsToBounds:(BOOL)clipsToBounds {
    [super setClipsToBounds:clipsToBounds];
    self.scrollView.clipsToBounds = clipsToBounds;
}

#pragma mark - scroll view deleaget
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    if (self.pagingEnabled) {
        if (self.direction == KLRecycleScrollViewDirectionLeft || self.direction == KLRecycleScrollViewDirectionRight) {
            NSInteger width = self.bounds.size.width;
            NSInteger extra = 0;
            if (velocity.x != 0) {
                extra = velocity.x > 0 ? width : -width;
            }
            
            CGPoint targetOffset = [self getLeftestViewToLeftEdge];
            targetContentOffset->x = targetOffset.x + extra;
            targetContentOffset->y = targetOffset.y;
        } else if (self.direction == KLRecycleScrollViewDirectionTop || self.direction == KLRecycleScrollViewDirectionBottom) {
            NSInteger height = self.bounds.size.height;
            NSInteger extra = 0;
            if (velocity.y != 0) {
                extra = velocity.y > 0 ? height : -height;
            }
            
            CGPoint targetOffset = [self getTopestViewToTopEdge];
            targetContentOffset->x = targetOffset.x;
            targetContentOffset->y = targetOffset.y + extra;
        }
    }
}

- (CGPoint)getLeftestViewToLeftEdge {
    CGPoint offset = self.scrollView.contentOffset;

    __block CGFloat minDistanceFromLeftEdge = MAXFLOAT;
    __block UIView *minDistanceFromLeftEdgeView;

    __weak typeof(self) weakSelf = self;
    [self.scrollView.visibleViews enumerateObjectsUsingBlock:^(id  _Nonnull view, NSUInteger idx, BOOL * _Nonnull stop) {
        CGFloat distanceToLeftEdge = [weakSelf.scrollView getDistanceToLeftEdge:view];
        if (distanceToLeftEdge < fabs(minDistanceFromLeftEdge)) {
            minDistanceFromLeftEdge = distanceToLeftEdge;
            minDistanceFromLeftEdgeView = view;
        }
    }];

    CGFloat targetX = offset.x + minDistanceFromLeftEdge;
    CGPoint targetOffset = CGPointMake(targetX, offset.y);

    return targetOffset;
}

- (CGPoint)getTopestViewToTopEdge {
    CGPoint offset = self.scrollView.contentOffset;
    
    __block CGFloat minDistanceFromTopEdge = MAXFLOAT;
    __block UIView *minDistanceFromTopEdgeView;
    
    __weak typeof(self) weakSelf = self;
    [self.scrollView.visibleViews enumerateObjectsUsingBlock:^(id  _Nonnull view, NSUInteger idx, BOOL * _Nonnull stop) {
        CGFloat distanceToTopEdge = [weakSelf.scrollView getDistanceToTopEdge:view];
        if (distanceToTopEdge < fabs(minDistanceFromTopEdge)) {
            minDistanceFromTopEdge = distanceToTopEdge;
            minDistanceFromTopEdgeView = view;
        }
    }];
    
    CGFloat targetY = offset.y + minDistanceFromTopEdge;
    CGPoint targetOffset = CGPointMake(offset.x, targetY);
    
    return targetOffset;
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    [self stopTimer];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (scrollView == self.scrollView) {
        if (!decelerate) {
            [self startTimer];
        }
    }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    if (scrollView == self.scrollView) {
        [self startTimer];
    }
}

@end

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

推荐阅读更多精彩内容

  • 深度分销的本质依然是大量生产,经销商不关心卖什么,只关心卖什么赚钱,没有人站在消费者的角度思考问题。一旦市场压力过...
    殷春燕阅读 365评论 0 0
  • 晚上下班之后一个人去看房,过了年后的房价都涨了几番,贵的吓人,出了地铁站走了很久的路程,才到一个出租房,两室的老房...
    汐漫阅读 651评论 2 1
  • 今天去玩啦! 发现自己还是很不懂事的不喜欢开车… 额 说到这里感觉故事要说完了 为什么坚持一周了还是不会讲故事呢 ...
    兔子与鹿還有秋天阅读 142评论 0 0
  • 飞鸟掠过高高的钟塔 白色的塔尖上 钟鸣阵阵 我的耳边 人来人往 绿草的芳香 那是你的方向 略显灰暗的天空下 你掠过...
    孙浒胡阅读 238评论 0 2