TextKit入門? 表情鍵盤? 圖文混排? --看我就夠了

前言

現在市場上大部分信息展示類應用都有用到圖文混排,我之前對這塊也是一知半解,最近放假正好深入了解一下這塊,在這里對此做一個總結,廢話不多說,先上 demo 地址
TextKit入門 demo地址
精仿手工課 demo地址
精仿手工課博客地址

背景知識

在正式開始學習之前,我們先來了解一下圖文混排在 iOS 上面的發展,拋棄 coretext (涉及到一些底層,這里不討論)不說,蘋果在 iOS6給出了*** NSMutableAttributedString這個類,簡單的說就是帶屬性的字符串,用它可以實現圖文混排,在 iOS7蘋果則給出了更加強大的 API--textKit,用 textKit***可以實現更加復雜的界面,接下來讓我們開始浪起來吧

1.gif

富文本(NSMutableAttributedString)

NSMutableAttributedString和數組一樣分為可變字符串和不可變字符串, NSAttributedString就是不可變字符串
NSMutableAttributedString簡單的說就是一個帶屬性的字符串,因此它的使用非常簡單,

  • 1.初始化字符串
  • 2.初始化字符串所需要的屬性
  • 3.將屬性賦值給字符串
    初始化方法
    - (instancetype)initWithString:(NSString *)str;
    - (instancetype)initWithString:(NSString *)str attributes:(nullable NSDictionary<NSString *, id> *)attrs;
    - (instancetype)initWithAttributedString:(NSAttributedString *)attrStr;
    初始化屬性
  • NSFontAttributeName 字體
  • NSForegroundColorAttributeName 字體顏色
  • NSBackgroundColorAttributeName 背景顏色
  • NSLigatureAttributeName 連字符
    該屬性所對應的值是一個 NSNumber 對象(整數)。連體字符是指某些連在一起的字符,它們采用單個的圖元符號。
    0 表示沒有連體字符。
    1 表示使用默認的連體字符。
    2 表示使用所有連體符號。默認值為 1(注意,iOS 不支持值為 2)。
  • NSParagraphStyleAttributeName 段落
    該屬性所對應的值是一個 NSParagraphStyle 對象。該屬性在一段文本上應用多個屬性。如果不指定該屬性,則默認為 NSParagraphStyle 的defaultParagraphStyle 方法返回的默認段落屬性
  • NSKernAttributeName 字間距
    該屬性所對應的值是一個 NSNumber 對象(整數)。字母緊排指定了用于調整字距的像素點數。字母緊排的效果依賴于字體。值為 0 表示不使用字母緊排。默認值為0
  • NSStrikethroughStyleAttributeName 刪除線
  • NSUnderlineStyleAttributeName 下劃線
  • NSStrokeColorAttributeName 邊線顏色
    該屬性所對應的值是一個 UIColor 對象。如果該屬性不指定(默認),則等同于 NSForegroundColorAttributeName。否則,指定為刪除線或下劃線顏色。更多細節見“Drawing attributedstrings that are both filled and stroked”
  • NSStrokeWidthAttributeName 邊線寬度
  • NSShadowAttributeName 陰影
  • NSVerticalGlyphFormAttributeName 橫豎排版
    實例
 // 設置顏色等
    NSMutableDictionary *arrDic = [NSMutableDictionary dictionary];
    arrDic[NSForegroundColorAttributeName] = [UIColor purpleColor];
    arrDic[NSBackgroundColorAttributeName] = [UIColor greenColor];
    arrDic[NSKernAttributeName] = @10;
    arrDic[NSUnderlineStyleAttributeName] = @1;
    
    NSMutableAttributedString *attriOneStr = [[NSMutableAttributedString alloc]initWithString:@"來呀,快活呀,反正有大把時光" attributes:arrDic];
    self.oneLabel.attributedText = attriOneStr;
    
    // 簡單的圖文混排
    NSMutableAttributedString *arrTwoStr = [[NSMutableAttributedString alloc]init];
    NSMutableAttributedString *TwoChildStr = [[NSMutableAttributedString alloc]initWithString:@"你好啊"];
    [arrTwoStr appendAttributedString:TwoChildStr];
    
    NSTextAttachment *attachMent = [[NSTextAttachment alloc]init];
    attachMent.image = [UIImage imageNamed:@"2"];
    attachMent.bounds = CGRectMake(0, -5, 20, 20);
    NSAttributedString *picStr = [NSAttributedString attributedStringWithAttachment:attachMent];
    [arrTwoStr appendAttributedString:picStr];
    
    NSAttributedString *TwooStr = [[NSAttributedString alloc]initWithString:@"我是小菜鳥"];
    [arrTwoStr appendAttributedString:TwooStr];
    self.twoLabel.attributedText = arrTwoStr;

效果

簡單.png

表情鍵盤,富文本實現圖文混排

表情鍵盤

Snip20160917_5.png

平常大家用的 QQ,微信等APP中隨處可見表情鍵盤,在做表情鍵盤前,先來了解一下什么是表情.
日常生活中,所用到的表情一般為兩種--圖片表情,emoji表情
emoji表情的本質就是字符串,比如 0x1f601:,在顯示的時候我們需要將字符串轉成表情
圖片表情就是一張圖片,比如這是一個表情信息,我們根據png來加載緩存在本地的圖片,并顯示.
圖片表情.png

實現思路

考慮到表情的兩種表現形式,決定用 Button 來實現,這樣可以方便的顯示字體或者圖片

  • 初始化一個 ListView, 并添加一個 UIScrollview 子控件
  • UIScrollview添加n個(需要幾個表情種類添加幾個) gridView
  • gridView上面添加 Button來顯示表情

表情鍵盤代碼###

由于代碼量較大,這里上一些核心代碼,具體代碼可以下載,下來再看TextKit入門 demo地址
表情工具類加載所需表情

+ (NSArray *)defaultEmotions
{
    if (!_defaultEmotions) {
        NSString *plist = [[NSBundle mainBundle] pathForResource:@"EmotionIcons/default/info.plist" ofType:nil];
        _defaultEmotions = [GPEmotion mj_objectArrayWithFile:plist];
        [_defaultEmotions makeObjectsPerformSelector:@selector(setDirectory:) withObject:@"EmotionIcons/default"];
    }
    return _defaultEmotions;
}

+ (NSArray *)emojiEmotions
{
    if (!_emojiEmotions) {
        NSString *plist = [[NSBundle mainBundle] pathForResource:@"EmotionIcons/emoji/info.plist" ofType:nil];
        _emojiEmotions = [GPEmotion mj_objectArrayWithFile:plist];
        [_emojiEmotions makeObjectsPerformSelector:@selector(setDirectory:) withObject:@"EmotionIcons/emoji"];
    }
    return _emojiEmotions;
}

+ (NSArray *)lxhEmotions
{
    if (!_lxhEmotions) {
        NSString *plist = [[NSBundle mainBundle] pathForResource:@"EmotionIcons/lxh/info.plist" ofType:nil];
        _lxhEmotions = [GPEmotion mj_objectArrayWithFile:plist];
        [_lxhEmotions makeObjectsPerformSelector:@selector(setDirectory:) withObject:@"EmotionIcons/lxh"];
    }
    return _lxhEmotions;
}

+ (NSArray *)recentEmotions
{
    if (!_recentEmotions) {
        _recentEmotions = [NSKeyedUnarchiver unarchiveObjectWithFile:GPRecentFilepath];
        if (!_recentEmotions) {
            _recentEmotions = [NSMutableArray array];
        }
    }
    return _recentEmotions;
}

表情數據傳遞

  • 點擊所需表情傳遞給 ListView
  - (void)setEmtiontype:(GPEmtionType)type
{
    switch (type) {
        case GPEmotionTypeRecent: {
            self.listView.emotions = [GPEmtionTool recentEmotions];
            break;
        }
        case GPEmotionTypeDefault: {
            self.listView.emotions = [GPEmtionTool defaultEmotions];
            
            break;
        }
        case GPEmotionTypeEmoji: {
            self.listView.emotions = [GPEmtionTool emojiEmotions];
            break;
        }
        case GPEmotionTypeLxh: {
            self.listView.emotions = [GPEmtionTool lxhEmotions];
            break;
        }
    }
}
  • LIstView 傳遞給GridView
  - (void)setEmotions:(NSArray *)emotions
{
    _emotions = emotions;
    
    NSInteger totlas = (emotions.count + GPEmotionMaxCountPerPage - 1) / GPEmotionMaxCountPerPage;
    NSInteger currrentGridViewCount = self.scrollView.subviews.count;
    
    self.pageControl.numberOfPages = totlas;
    self.pageControl.currentPage = 0;
    GPEmottionGridView *gridView = nil;
    for (NSInteger i = 0; i < totlas; i ++) 
        if (i >= currrentGridViewCount) {
            gridView = [[GPEmottionGridView alloc]init];
            [self.scrollView addSubview:gridView];
        } else {
            gridView = self.scrollView.subviews[i]
        }
        NSInteger loc = i * GPEmotionMaxCountPerPage;
        NSInteger len = GPEmotionMaxCountPerPage;
        if (loc + len > emotions.count) {
            len = emotions.count - loc;
        }
        NSRange range = NSMakeRange(loc, len);
        NSArray *gridViewemotionS = [emotions subarrayWithRange:range];
        gridView.emotions = gridViewemotionS;
        gridView.hidden = NO
    
    for (NSInteger i = totlas; i<currrentGridViewCount; i++) {
        GPEmottionGridView *gridView = self.scrollView.subviews[i];
        gridView.hidden = YES;
    
       [self setNeedsLayout];
    self.scrollView.contentOffset = CGPointZero
  • gridView 傳遞給 btn
  - (void)setEmotions:(NSArray *)emotions
{
    _emotions = emotions;
    
    NSInteger count = emotions.count;
    NSInteger currentEmotionViewCount = self.emotionViews.count;
    for (int i = 0; i<count; i++) {
        GPEmotionView *emotionView = nil;
        
        if (i >= currentEmotionViewCount) {
            emotionView = [[GPEmotionView alloc] init];
            [emotionView addTarget:self action:@selector(emotionClick:)           forControlEvents:UIControlEventTouchUpInside];
            [self addSubview:emotionView];
            [self.emotionViews addObject:emotionView];
        } else {
            emotionView = self.emotionViews[i];
        }
        // 傳遞模型數據
        emotionView.emotion = emotions[i];
       
      for NSInteger i = count; i<currentEmotionViewCount; i++) 
        UIButton *emotionView = self.emotionViews[i];
        emotionView.hidden = YES;
    ```
* Btn 展示表情
  • (void)setEmotion:(GPEmotion *)emotion
    {
    _emotion = emotion;

    if (emotion.code) {
    self.titleLabel.font = [UIFont systemFontOfSize:32];
    [self setTitle:emotion.emoji forState:UIControlStateNormal];
    [self setImage:nil forState:UIControlStateNormal];
    } else { // 圖片表情
    NSString *icon = [NSString stringWithFormat:@"%@/%@", emotion.directory, emotion.png];
    UIImage *image = [UIImage imageNamed:icon];
    [self setImage: [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]forState:UIControlStateNormal];
    [self setTitle:nil forState:UIControlStateNormal];
    }
    }

###圖文混排實現


![Uploading Snip20160917_5_983709.png . . .]](http://upload-images.jianshu.io/upload_images/694552-193ab88b3b973702.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

**實現思路**
* 獲得后臺的字符串,然后用正則表達式將字符串分割為表情和非表情兩部分,然后將其轉換為**富文本字符串**,并在匹配到超鏈接時候給其綁定一個 key, 隨后將分割后的結果按順序排好,

* 將之前轉換后的富文本字符串賦值為 `UITextView` 的`attributedText`屬性

* 點擊超鏈接,判斷當前點是否在鏈接范圍之內,若在就打開鏈接

**圖文混排核心代碼**

* 普通字符串轉換為富文本字符串
         - (NSAttributedString *)creatArrtext:(NSString *)text
      { NSArray *regexResults = [GPEmtionTool regexResultsWithText:text];
      NSMutableAttributedString *attributedString =   [[NSMutableAttributedString alloc] init];
       [regexResults enumerateObjectsUsingBlock:^(GPRegexResult *result,   NSUInteger idx, BOOL *stop) {
        GPEmotion *emotion = nil;
        if (result.isEmotion) { // 表情
            emotion = [GPEmtionTool emotionWithDesc:result.string];
        }
        
        if (emotion) { // 如果有表情
            // 創建附件對象
            GPEmotionAttachment *attach = [[GPEmotionAttachment alloc] init];
            
            // 傳遞表情
            attach.emotion = emotion;
            attach.bounds = CGRectMake(0, -3, GPStatusOrginalTextFont.lineHeight, GPStatusOrginalTextFont.lineHeight);
            
            // 將附件包裝成富文本
            NSAttributedString *attachString = [NSAttributedString attributedStringWithAttachment:attach];
            [attributedString appendAttributedString:attachString];
        } else { // 非表情(直接拼接普通文本)
            NSMutableAttributedString *substr = [[NSMutableAttributedString alloc] initWithString:result.string];
        
            // 匹配超鏈接
            NSString *httpRegex = @"http(s)?://([a-zA-Z|\\\\d]+\\\\.)+[a-zA-Z|\\\\d]+(/[a-zA-Z|\\\\d|\\\\-|\\\\+|_./?%&=]*)?";
            [result.string enumerateStringsMatchedByRegex:httpRegex usingBlock:^(NSInteger captureCount, NSString *const __unsafe_unretained *capturedStrings, const NSRange *capturedRanges, volatile BOOL *const stop) {
                [substr addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:*capturedRanges];
                
                // 綁定一個key
                [substr addAttribute:GPLinkText value:*capturedStrings range:*capturedRanges];
            }];
            
            [attributedString appendAttributedString:substr];
        }
      }];
    
      // 設置字體
      [attributedString addAttribute:NSFontAttributeName   value:GPStatusOrginalTextFont range:NSMakeRange(0,   attributedString.length)];
    
      return attributedString;
      }

* 點擊鏈接跳轉
  • (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:touch.view];
    CGPoint pp = [self convertPoint:point toView:self.textView];
    GPLink *touchingLink = [self touchingLinkWithPoint:pp];
if (touchingLink) {
    [[NSNotificationCenter defaultCenter] postNotificationName:GPLinkDidSelectedNotification object:nil userInfo:@{GPLinkText : touchingLink.text}];
}

[self touchesCancelled:touches withEvent:event];

}

  • (GPLink *)touchingLinkWithPoint:(CGPoint)point
    {
    __block GPLink *touchingLink = nil;
    [self.links enumerateObjectsUsingBlock:^(GPLink *link, NSUInteger idx, BOOL *stop) {
    for (UITextSelectionRect *selectionRect in link.rects) {
    if (CGRectContainsPoint(selectionRect.rect, point)) {
    NSLog(@"選中%@",NSStringFromCGRect(selectionRect.rect));
    touchingLink = link;
    break;
    }
    }
    }];
    return touchingLink;
    }
# TextKit
![TextKit.png](http://upload-images.jianshu.io/upload_images/694552-21b677318e171048.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
接下來,我們來看今天的重頭戲` TextKit`,首先來看看`textKit` 的類
* `NSTextStorage`: 平時用到的字符串 **string**,這個類里面包含著 string的屬性,比如顏色,大小等都是這個類來管理
* `NSLayoutManager`: 這個就是管理中心,負責布局渲染
* `NSTextContainer` : 就是可以渲染的范圍
* `UITextView` :就是我們平時用的控件,用來給用戶展示數據
那么,我么可以用這些類來做哪些事情呢,看一些實例
### 閱讀排版

![閱讀排版.gif](http://upload-images.jianshu.io/upload_images/694552-9463e4514a639b67.gif?imageMogr2/auto-orient/strip)


**閱讀排版實現思路**
* 創建n 個 `textView`,共用一套`NSLayoutManager`

**閱讀排版核心代碼**
  • (void)setupTextKit
    {
    NSURL *contentUrl = [[NSBundle mainBundle]URLForResource:@"content" withExtension:@"txt"];

    self.textStorage = [[NSTextStorage alloc]initWithFileURL:contentUrl options:[NSDictionary dictionary] documentAttributes:nil error:nil];
    self.layoutManager = [[NSLayoutManager alloc]init];

    [self.textStorage addLayoutManager:self.layoutManager];
    }

  • (void)layoutconter
    {
    NSInteger totlaGlyph = 0;
    CGFloat X = 0;
    while (totlaGlyph < self.layoutManager.numberOfGlyphs) {

      NSTextContainer *textContainer = [[NSTextContainer alloc]initWithSize:CGSizeMake(SCREEN_WIDTH, SCREEN_HEIGHT)];
      
      [self.layoutManager addTextContainer:textContainer];
      
      UITextView *textView = [[UITextView alloc]initWithFrame:CGRectMake(X, 0, SCREEN_WIDTH, self.pageScrollView.height) textContainer:textContainer];
      textView.scrollEnabled = NO;
      textView.font = [UIFont systemFontOfSize:20];
      [self.pageScrollView addSubview:textView];
      
      X += SCREEN_WIDTH;
      
      totlaGlyph = NSMaxRange([self.layoutManager glyphRangeForTextContainer:textContainer]);
    

    }

    CGSize contentSize = CGSizeMake(X, self.pageScrollView.height);
    self.pageScrollView.pagingEnabled = YES;
    self.pageScrollView.contentSize = contentSize;
    self.pageScrollView.showsHorizontalScrollIndicator = NO;

}

### 高亮文字,URL, 保持 URL 在一行是一個整體

**高亮文字,URL實現思路**
* 可以自定義`NSTextStorage`,這里注意`NSTextStorage`繼承自`NSMutableAttributedString`,所以必須實現以下方法:
`  - (NSString *)string;`
 ` - (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range;`
 ` - (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str;`
 ` - (void)setAttributes:(NSDictionary *)attrs range:(NSRange)range;`

* 在`- (void)processEditing`中匹配高亮字符,匹配 URL, 并高亮
* 在 `NSLayoutManagerDelegate`實現 url 保持整體不換行
**高亮文字,URL核心代碼**

  • (void)processEditing
    {

    NSRegularExpression *expression = [[NSRegularExpression alloc]initWithPattern:@"a[\\b{Alphabetic}&&\\b{Uppercase}][\\br{Alphabetic}]+" options:NSRegularExpressionCaseInsensitive error:nil];

    NSRange paragaphRange = [self.string paragraphRangeForRange: self.editedRange];
    [self removeAttribute:NSForegroundColorAttributeName range:paragaphRange];

    [expression enumerateMatchesInString:self.string options:0 range:paragaphRange usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
    [self addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:result.range];
    }];
    [super processEditing];
    }

  • (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str
    {
    [_imp replaceCharactersInRange:range withString:str];
    [self edited:NSTextStorageEditedCharacters range:range changeInLength:(NSInteger)str.length - (NSInteger)range.length];

    NSDataDetector *datector = [[NSDataDetector alloc]initWithTypes:NSTextCheckingTypeLink error:nil];

    NSRange paragaphRange = [self.string paragraphRangeForRange: NSMakeRange(range.location, str.length)];

    [self removeAttribute:NSLinkAttributeName range:paragaphRange];
    [self removeAttribute:NSForegroundColorAttributeName range:paragaphRange];

    [datector enumerateMatchesInString:self.string options:0 range:paragaphRange usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {

      [self addAttribute:NSLinkAttributeName value:result.URL range:result.range];
      [self addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:result.range];
    

    }];
    }


  • (BOOL)layoutManager:(NSLayoutManager *)layoutManager shouldBreakLineByWordBeforeCharacterAtIndex:(NSUInteger)charIndex
    {
    NSRange range;
    NSURL *linkURL = [layoutManager.textStorage attribute:NSLinkAttributeName atIndex:charIndex effectiveRange:&range];

    if (linkURL && charIndex > range.location && charIndex <= NSMaxRange(range))
    return NO;
    else
    return YES;
    }

**機智的你一定發現,其實利用 TextKit也可以實現圖文混排,機智的你自己試一試**
###環繞布局

![環繞布局.png](http://upload-images.jianshu.io/upload_images/694552-93d9a2037da33412.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
如圖所示;大家用 Word 的時候插入圖片,使得文字環繞在圖片周圍,哈哈,好消息來了, iOS 也可以實現哦
**環繞布局實現思路**
* 記得`textContainer`,我們可以直接給其屬性賦值一個` path`, 就相當于在一張紙上裁剪掉一部分,那么自然就不會在那部分渲染文字了
**環繞布局核心代碼**
  • (void)setupImage
    {

    UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"5"]];
    imageView.center = self.view.center;
    CGRect ofram = [self.textView convertRect:imageView.bounds fromView:imageView];
    ofram.origin.y = ofram.origin.y - 64;
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:ofram];

    self.textView.textContainer.exclusionPaths = @[path];

    [self.view addSubview:imageView];
    }

#總結
這只是一些簡單的使用,機智的你肯定有更多想法,互相學習吧

>參考
[obJc,初始 textKit](https://objccn.io/issue-5-1/)
[MJ](https://github.com/CoderMJLee/MJExtension)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,084評論 2 375

推薦閱讀更多精彩內容