RTL適配歷程

背景

阿拉伯語(yǔ)適配是一個(gè)比較麻煩的事情,不止在于它文案的適配裹赴,更多的是在于其語(yǔ)言習(xí)慣的變化。由從左到右(LeftToRight)的布局習(xí)慣變?yōu)榱藦挠蚁蜃?RightToLeft)的布局習(xí)慣。

針對(duì)iOS9之后的RTL(RightToLeft簡(jiǎn)稱RTL)適配棋返,系統(tǒng)有一個(gè)官方文檔教你怎么做適配延都。

定制RTL

當(dāng)系統(tǒng)語(yǔ)言切換成RTL語(yǔ)言(如阿拉伯語(yǔ))后,如果App支持這個(gè)語(yǔ)言睛竣,系統(tǒng)會(huì)自動(dòng)幫助App設(shè)置成RTL布局窄潭。但是很多時(shí)候,我們希望自己配置當(dāng)前是否是RTL酵颁,比如App內(nèi)部支持切換App語(yǔ)言嫉你,App語(yǔ)言不一定跟系統(tǒng)語(yǔ)言保持一致,這時(shí)候躏惋,也許系統(tǒng)是英文幽污,App內(nèi)部設(shè)置成了阿拉伯語(yǔ)。我們依然需要變成RTL布局簿姨,系統(tǒng)是不會(huì)幫我們完成這項(xiàng)任務(wù)的距误,我們只有自己來(lái)設(shè)置RTL。

幸運(yùn)的是扁位,iOS9之后系統(tǒng)提供了相應(yīng)的API幫助我們完成定制准潭。

typedef NS_ENUM(NSInteger, UISemanticContentAttribute) {
    UISemanticContentAttributeUnspecified = 0,
    UISemanticContentAttributePlayback, // for playback controls such as Play/RW/FF buttons and playhead scrubbers
    UISemanticContentAttributeSpatial, // for controls that result in some sort of directional change in the UI, e.g. a segmented control for text alignment or a D-pad in a game
    UISemanticContentAttributeForceLeftToRight,
    UISemanticContentAttributeForceRightToLeft
} NS_ENUM_AVAILABLE_IOS(9_0);

@property (nonatomic) UISemanticContentAttribute semanticContentAttribute NS_AVAILABLE_IOS(9_0);

UIView有一個(gè)semanticContentAttribute的屬性,當(dāng)我們將其設(shè)置成UISemanticContentAttributeForceRightToLeft之后域仇,UIView將強(qiáng)制變?yōu)镽TL布局刑然。當(dāng)然在非RTL語(yǔ)言下,我們需要設(shè)置它為UISemanticContentAttributeForceLeftToRight暇务,來(lái)適配系統(tǒng)是阿拉伯語(yǔ)泼掠,App是其他語(yǔ)言不需要RTL布局的情況。

讓一個(gè)App適配RTL垦细,我們需要給幾乎所有的View都設(shè)置這個(gè)屬性择镇,這種情況下,首先想到的是hook UIView的DESIGNATED_INITIALIZER括改,在里面設(shè)置semanticContentAttribute腻豌。但是這種辦法有坑,WKWebview雖然繼承于UIView嘱能,但是它的setSemanticContentAttribute:會(huì)有問(wèn)題吝梅,會(huì)導(dǎo)致Crash:

wkcrash.png

這應(yīng)該是系統(tǒng)的坑,為了繞開(kāi)這個(gè)坑焰檩,我們發(fā)現(xiàn)使用[UIView appearance]來(lái)設(shè)置能達(dá)到差不多的效果:

[UIView appearance].semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;

使用[UIView appearance]設(shè)置后憔涉,大部分的View看上去正常了。除了搜索欄析苫。使用[UIView appearance]設(shè)置后兜叨,搜索欄是不生效的穿扳。不過(guò)不用擔(dān)心,我們只需要設(shè)置一下[UISearchBar appearance]即可国旷。

[UISearchBar appearance].semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;

布局

Autolayout

設(shè)置完view的semanticContentAttribute后矛物,如果使用的是Autolayout布局,并且Autolayout下跪但,使用的是leading和trailing履羞,系統(tǒng)會(huì)自動(dòng)幫助我們調(diào)整布局,將其適配RTL屡久。但是如果使用的是left和right忆首,系統(tǒng)是不會(huì)這么做的。

所以為了適配布局被环,我們需要將所有的left,right替換成leading和trailing糙及。

Frame

對(duì)于frame布局,系統(tǒng)就沒(méi)這么友好了筛欢,frame的布局需要我們自己去適配浸锨。 探究RTL的布局,實(shí)際上只是調(diào)整了frame.origin.x版姑,y和size是不會(huì)變的柱搜。而且對(duì)于靜態(tài)view,如果知道了父view的width剥险,是可以直接算出字view RTL下的frame的聪蘸,所以我們封了一個(gè)category,來(lái)滿足大部分靜態(tài)布局的情況

@implementation UIView (HTSRTL)

- (void)setRTLFrame:(CGRect)frame width:(CGFloat)width
{
    if (isRTL()) {
        if (self.superview == nil) {
            NSAssert(0, @"must invoke after have superView");
        }
        CGFloat x = width - frame.origin.x - frame.size.width;
        frame.origin.x = x;
    }
    self.frame = frame;
}

- (void)setRTLFrame:(CGRect)frame
{
    [self setRTLFrame:frame width:self.superview.frame.size.width];
}

- (void)resetFrameToFitRTL;
{
    [self setRTLFrame:self.frame];
}

@end

對(duì)于已經(jīng)完成frame布局的view炒嘲,我們只需要在最后對(duì)view調(diào)用resetFrameToFitRTL宇姚,即可適配RTL。

整體上夫凸,frame適配RTL還是比autolayout麻煩很多。所以對(duì)于新代碼阱持,我們團(tuán)隊(duì)中約定夭拌,布局盡量使用autolayout。除非一些非常特殊的情況衷咽,比如需要考慮性能鸽扁。

手勢(shì)

滑動(dòng)返回

RTL下,除了布局需要調(diào)整镶骗,手勢(shì)的方向也是需要調(diào)整的

正常的滑動(dòng)返回手勢(shì)是右滑桶现,在RTL下,是需要變成左滑返回的鼎姊。為了讓滑動(dòng)返回也適配RTL骡和,我們需要修改navigationBar和UINavigationController.view的semanticContentAttribute相赁。使用[UIView appearance]修改semanticContentAttribute并不能使手勢(shì)隨之改變,我們需要手動(dòng)修改慰于。為了讓所有的UINavigationController都生效钮科。我們hook了UINavigationController的initWithNibName:bundle:

+ (void)load
{
    [self hts_swizzleMethod:@selector(initWithNibName:bundle:) withMethod:@selector(rtl_initWithNibName:bundle:)];
}

- (instancetype)rtl_initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil
{
    if ([self rtl_initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        if (@available(iOS 9.0, *)) {
            self.navigationBar.semanticContentAttribute = [UIView appearance].semanticContentAttribute;
            self.view.semanticContentAttribute = [UIView appearance].semanticContentAttribute;
        }
    }
    return self;
}

在所有的UINavigationController創(chuàng)建時(shí),我們?cè)O(shè)置了navigationBar和UINavigationController.view的semanticContentAttribute婆赠。這樣系統(tǒng)的手勢(shì)就可以適配RTL了绵脯。

其他手勢(shì)

跟方向有關(guān)的手勢(shì)有2個(gè):UISwipeGestureRecognizer和UIPanGestureRecognizer

UIPanGestureRecognizer是無(wú)法直接設(shè)置有效方向的。為了設(shè)置只對(duì)某個(gè)方向有效休里,一般都是通過(guò)實(shí)現(xiàn)它的delegate中的gestureRecognizerShouldBegin:方法蛆挫,來(lái)指定是否生效。對(duì)于這種情況妙黍,我們只能手動(dòng)修gestureRecognizerShouldBegin:中的邏輯悴侵,來(lái)適配RTL

UISwipeGestureRecognizer有一個(gè)direction的屬性,可以設(shè)置有效方向废境。為了適配RTL畜挨,我們可以hook它的setter方法,達(dá)到自動(dòng)適配的目的:

@implementation UISwipeGestureRecognizer (HTSRTL)

+ (void)load
{
    [self hts_swizzleMethod:@selector(setDirection:) withMethod:@selector(rtl_setDirection:)];
}

- (void)rtl_setDirection:(UISwipeGestureRecognizerDirection)direction
{
    if (isRTL()) {
        if (direction == UISwipeGestureRecognizerDirectionRight) {
            direction = UISwipeGestureRecognizerDirectionLeft;
        } else if (direction == UISwipeGestureRecognizerDirectionLeft) {
            direction = UISwipeGestureRecognizerDirectionRight;
        }
    }
    [self rtl_setDirection:direction];
}

@end

圖片鏡像

在RTL下噩凹,某些圖片是需要鏡像的巴元,比如帶箭頭的返回按鈕。正常情況下驮宴,箭頭是朝左的逮刨,RTL下,箭頭就需要鏡像成朝右堵泽。系統(tǒng)對(duì)這種情況提供了一個(gè)鏡像的方法:

// Creates a version of this image that, when assigned to a UIImageView’s image property, draws its underlying image contents horizontally mirrored when running under a right-to-left language. Affects the flipsForRightToLeftLayoutDirection property; does not affect the imageOrientation property.
- (UIImage *)imageFlippedForRightToLeftLayoutDirection NS_AVAILABLE_IOS(9_0);

然而....這個(gè)方法并不好用修己。通過(guò)切換系統(tǒng)語(yǔ)言,來(lái)適配RTL應(yīng)該是沒(méi)問(wèn)題的迎罗。但是在App內(nèi)部切換語(yǔ)言睬愤,手動(dòng)修改RTL布局,系統(tǒng)的這個(gè)方法就經(jīng)常出現(xiàn)錯(cuò)誤鏡像的情況纹安。無(wú)奈尤辱,我們只好自己寫(xiě)一個(gè)方法,來(lái)達(dá)到這個(gè)目的:

@implementation UIImage (HTSFlipped)
- (UIImage *)hts_imageFlippedForRightToLeftLayoutDirection
{
    if (isRTL()) {
        return [UIImage imageWithCGImage:self.CGImage
                                   scale:self.scale
                             orientation:UIImageOrientationUpMirrored];
    }

    return self;
}
@end

對(duì)于需要在RTL下鏡像的圖片厢岂,手動(dòng)對(duì)image調(diào)用hts_imageFlippedForRightToLeftLayoutDirection即可

UIEdgeInsets

UI上跟左右方向有關(guān)的還有UIEdgeInsets光督,特別是UIButton的imageEdgeInsets和titleEdgeInsets。正常的時(shí)候塔粒,我們?cè)O(shè)置一個(gè)titleEdgeInsets的left结借。但是當(dāng)RTL的情況下,因?yàn)樗械臇|西都左右鏡像了卒茬,應(yīng)該設(shè)置titleEdgeInsets的right布局才會(huì)正常船老。然而系統(tǒng)卻不會(huì)自動(dòng)幫我們將left和right調(diào)換咖熟。我們需要手動(dòng)去適配它。

為了快速適配努隙,我們hook了UIButton的setContentEdgeInsets球恤,setImageEdgeInsets,setTitleEdgeInsets方法在RTL情況下荸镊,手動(dòng)調(diào)換left <-> right咽斧。

UIEdgeInsets RTLEdgeInsetsWithInsets(UIEdgeInsets insets) {
    if (insets.left != insets.right && isRTL()) {
        CGFloat temp = insets.left;
        insets.left = insets.right;
        insets.right = temp;
    }
    return insets;
}

@implementation UIButton (HTSRTL)

+ (void)load
{
    RTLMethodSwizzling(self, @selector(setContentEdgeInsets:), @selector(rtl_setContentEdgeInsets:));
    RTLMethodSwizzling(self, @selector(setImageEdgeInsets:), @selector(rtl_setImageEdgeInsets:));
    RTLMethodSwizzling(self, @selector(setTitleEdgeInsets:), @selector(rtl_setTitleEdgeInsets:));
}

- (void)rtl_setContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets {
    [self rtl_setContentEdgeInsets:RTLEdgeInsetsWithInsets(contentEdgeInsets)];
}

- (void)rtl_setImageEdgeInsets:(UIEdgeInsets)imageEdgeInsets {
    [self rtl_setImageEdgeInsets:RTLEdgeInsetsWithInsets(imageEdgeInsets)];
}

- (void)rtl_setTitleEdgeInsets:(UIEdgeInsets)titleEdgeInsets {
    [self rtl_setTitleEdgeInsets:RTLEdgeInsetsWithInsets(titleEdgeInsets)];
}

@end

然而我們不可能hook住所有的使用EdgeInsets的地方,我們只對(duì)常用的入口進(jìn)行hook躬存,對(duì)某些不常見(jiàn)的地方张惹,我們也提供是rtl_EdgeInsetsMake方法,用它代替UIEdgeInsetsMake岭洲,進(jìn)行適配

UIEdgeInsets RTLEdgeInsetsMake(CGFloat top, CGFloat left, CGFloat bottom, CGFloat right) {
    if (left != right && isRTL()) {
        CGFloat temp = left;
        left = right;
        right = temp;
    }
    return UIEdgeInsetsMake(top, left, bottom, right);
}

TextAlignment

RTL下textAlignment也是需要調(diào)整的宛逗,官方文檔中默認(rèn)textAlignment是NSTextAlignmentNatural,并且NSTextAlignmentNatural可用自動(dòng)適配RTL

By default, text alignment in iOS is natural; in OS X, it’s left. Using natural text alignment aligns text on the left in a left-to-right language, and automatically mirrors the alignment for right-to-left languages

然而盾剩,情況并沒(méi)有文檔描述的那么好雷激,當(dāng)我們?cè)谙到y(tǒng)內(nèi)切換語(yǔ)言的時(shí)候,系統(tǒng)經(jīng)常會(huì)錯(cuò)誤的設(shè)置textAlignment告私。沒(méi)有辦法屎暇,我們只有自己去適配textAlignment.

以UILabel為例,我們hook它的setter的方法驻粟,根據(jù)當(dāng)前是否是RTL根悼,來(lái)設(shè)置正確的textAlignment,如果UILabel從未調(diào)用setTextAlignment:蜀撑,我們還需要給它一個(gè)正確的默認(rèn)值挤巡。

@implementation UILabel (HTSRTL)

+ (void)load
{
    RTLMethodSwizzling(self, @selector(initWithFrame:), @selector(rtl_initWithFrame:));
    RTLMethodSwizzling(self, @selector(setTextAlignment:), @selector(rtl_setTextAlignment:));
}

- (instancetype)rtl_initWithFrame:(CGRect)frame
{
    if ([self rtl_initWithFrame:frame]) {
        self.textAlignment = NSTextAlignmentNatural;
    }
    return self;
}

- (void)rtl_setTextAlignment:(NSTextAlignment)textAlignment
{
    if (isRTL()) {
        if (textAlignment == NSTextAlignmentNatural || textAlignment == NSTextAlignmentLeft) {
            textAlignment = NSTextAlignmentRight;
        } else if (textAlignment == NSTextAlignmentRight) {
            textAlignment = NSTextAlignmentLeft;
        }
    }
    [self rtl_setTextAlignment:textAlignment];
}

@end

AttributeString

以UILabel為例,對(duì)于AttributeString酷麦,UILabel的textAlignment是不生效的矿卑,因?yàn)锳ttributeString自帶attributes。為了讓attributeString也能自動(dòng)適配RTL沃饶。我們需要在RTL下粪摘,將Alignment的left和right互換。
attributeString的alignment一般使用NSMutableParagraphStyle設(shè)置绍坝,所以我們首先hook NSMutableParagraphStyle,在setAlignment的時(shí)候設(shè)上正確的alignment:

@implementation NSMutableParagraphStyle (HTSRTL)

+ (void)load
{
    RTLMethodSwizzling(self, @selector(setAlignment:), @selector(rtl_setAlignment:));
}


- (void)rtl_setAlignment:(NSTextAlignment)alignment
{
    if (isRTL()) {
        if (alignment == NSTextAlignmentLeft || alignment == NSTextAlignmentNatural) {
            alignment = NSTextAlignmentRight;
        } else if (alignment == NSTextAlignmentRight) {
            alignment = NSTextAlignmentLeft;
        }
    }
    [self rtl_setAlignment:alignment];
}

@end

然而如果attributeString不設(shè)置ParagraphStyle苔悦,或者ParagraphStyle沒(méi)有調(diào)用setAlignment轩褐,hook是無(wú)效的。

適配這種情況玖详,有2種辦法:

  • 一種是hook NSAttributedString的初始化方法把介,在里面給attributeString加上合適的alignment勤讽。
  • 一種是hook UILabel的setAttributeString,在里面對(duì)attributeString做處理拗踢。

兩種hook都無(wú)法處理好所有的情況:

  • NSAttributedString是類族脚牍,類族是對(duì)外屏蔽真實(shí)class的,我們很難完全覆蓋到所有NSAttributedString的class巢墅,更何況還有NSMutableAttributedString等子類的類族诸狭。
  • 可以使用AttributeString的地方非常多,除了UILabel還有UITextView等君纫,這里也無(wú)法處理到所有的情況

基于這種情況驯遇,由于使用AttributeString的地方,90%是UILabel蓄髓,我們最終選擇hook UILabel的setAttributeString:

NSAttributedString *RTLAttributeString(NSAttributedString *attributeString) {
    if (attributeString.length == 0) {
        return attributeString;
    }
    NSRange range;
    NSDictionary *originAttributes = [attributeString attributesAtIndex:0 effectiveRange:&range];
    NSParagraphStyle *style = [originAttributes objectForKey:NSParagraphStyleAttributeName];

    if (style && isRTLString(attributeString.string)) {
        return attributeString;
    }

    NSMutableDictionary *attributes = originAttributes ? [originAttributes mutableCopy] : [NSMutableDictionary new];

    if (!style) {
        NSMutableParagraphStyle *mutableParagraphStyle = [[NSMutableParagraphStyle alloc] init];
        mutableParagraphStyle.alignment = NSTextAlignmentLeft;
        style = mutableParagraphStyle;
        [attributes setValue:mutableParagraphStyle forKey:NSParagraphStyleAttributeName];
    }
    NSString *string = RTLString(attributeString.string);
    return [[NSAttributedString alloc] initWithString:string attributes:attributes];
}

@implementation UILabel (HTSRTL)

+ (void)load
{
    RTLMethodSwizzling(self, @selector(setAttributedText:), @selector(rtl_setAttributedText:));
}

- (void)rtl_setAttributedText:(NSAttributedString *)attributedText
{
    NSAttributedString *attributeString = RTLAttributeString(attributedText);
    [self rtl_setAttributedText:attributeString];
}

@end

Unicode字符串

由于閱讀習(xí)慣的差異(阿拉伯語(yǔ)從右往左閱讀叉庐,其他語(yǔ)言從左往右閱讀),所以字符的排序是不一樣的会喝,普通語(yǔ)言左邊是第一個(gè)字符陡叠,阿拉伯語(yǔ)右邊是第一個(gè)字符。

如果是單純某種文字肢执,不管是阿拉伯語(yǔ)還是英文枉阵,系統(tǒng)都是已經(jīng)幫助我們做好適配了的。然而混排的情況下蔚万,系統(tǒng)的適配是有問(wèn)題的岭妖。對(duì)于一個(gè)string,系統(tǒng)會(huì)用第一個(gè)字符來(lái)決定當(dāng)前是LTR還是RTL反璃。

那么坑來(lái)了昵慌,假設(shè)有一個(gè)這樣的字符串@"小明??? ?? ???????"(翻譯過(guò)來(lái)為:小明關(guān)注了你),在阿拉伯語(yǔ)的情況下淮蜈,由于閱讀順序是從右往左斋攀,我們希望他顯示為@"??? ?? ???????小明"。然而按照系統(tǒng)的適配方案梧田,是永遠(yuǎn)無(wú)法達(dá)到我們期望的淳蔼。

  • 如果"小明"放前面,第一個(gè)字符是中文裁眯,系統(tǒng)識(shí)別為L(zhǎng)TR鹉梨,從左往右排序,顯示為@"小明??? ?? ???????"穿稳。
  • 如果"小明"放后面存皂,第一個(gè)字符是阿拉伯語(yǔ),系統(tǒng)識(shí)別為RTL,從右往左排序旦袋,依然顯示為@"小明??? ?? ???????"骤菠。

為了適配這種情況,可以在字符串前面加一些不會(huì)顯示的字符疤孕,強(qiáng)制將字符串變?yōu)長(zhǎng)TR或者RTL商乎。

In a few cases, the default behavior produces incorrect results. To handle these cases, the Unicode Bidirectional Algorithm provides a number of invisible characters that can be used to force the correct behavior.

在字符串前面添加"\u202B"表示RTL,加"\u202A"LTR祭阀。為了統(tǒng)一適配剛剛的情況鹉戚,我們hook了UILabel的setText:方法

BOOL isRTLString(NSString *string) {
    if ([string hasPrefix:@"\u202B"] || [string hasPrefix:@"\u202A"]) {
        return YES;
    }
    return NO;
}

NSString *RTLString(NSString *string) {
    if (string.length == 0 || isRTLString(string)) {
        return string;
    }
    if (isRTL()) {
        string = [@"\u202B" stringByAppendingString:string];
    } else {
        string = [@"\u202A" stringByAppendingString:string];
    }
    return string;
}

@implementation UILabel (HTSRTL)

+ (void)load
{
    RTLMethodSwizzling(self, @selector(setText:), @selector(rtl_setText:));
}

- (void)rtl_setText:(NSString *)text
{
    [self rtl_setText:RTLString(text)];
}
@end

這種方法雖然能適配RTL,但是由于修改了原來(lái)字符串柬讨,雖然不會(huì)顯示出來(lái)崩瓤,但是畢竟多加了字符,會(huì)改變?cè)瓉?lái)各個(gè)字符的range位置踩官,當(dāng)我們有特殊邏輯要使用各種range的時(shí)候却桶,可能會(huì)有問(wèn)題,對(duì)于這種特殊的情況蔗牡,無(wú)法做到統(tǒng)一適配颖系,所以只能具體情況具體處理

總結(jié)

至此,大部分的情況都可以適配了辩越。整個(gè)適配過(guò)程嘁扼,盡量使用hook的方式,統(tǒng)一處理黔攒,避免代碼的侵入性趁啸。然而有很多地方只能處理最基本的情況,對(duì)很多特殊case是無(wú)法兼容的督惰,比如textAlignment的處理不傅,無(wú)法覆蓋到所有View。比如Unicode字符串的處理赏胚,某些特殊case下可能會(huì)有坑访娶。對(duì)于這些特殊case,我們?cè)倬唧w處理觉阅。
整體來(lái)說(shuō)崖疤,雖然系統(tǒng)在iOS9之后就支持RTL了,但是因?yàn)槭钦麄€(gè)布局方式都改變典勇,系統(tǒng)也無(wú)法做到盡善盡美劫哼,這個(gè)適配過(guò)程還是有很多坑需要去填。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末割笙,一起剝皮案震驚了整個(gè)濱河市沦偎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖豪嚎,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異谈火,居然都是意外死亡侈询,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門糯耍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)扔字,“玉大人,你說(shuō)我怎么就攤上這事温技「镂” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵舵鳞,是天一觀的道長(zhǎng)震檩。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蜓堕,這世上最難降的妖魔是什么抛虏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮套才,結(jié)果婚禮上迂猴,老公的妹妹穿的比我還像新娘。我一直安慰自己背伴,他們只是感情好沸毁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著傻寂,像睡著了一般息尺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上崎逃,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天掷倔,我揣著相機(jī)與錄音,去河邊找鬼个绍。 笑死勒葱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的巴柿。 我是一名探鬼主播凛虽,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼广恢!你這毒婦竟也來(lái)了凯旋?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎至非,沒(méi)想到半個(gè)月后钠署,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡荒椭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年谐鼎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趣惠。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡狸棍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出味悄,到底是詐尸還是另有隱情草戈,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布侍瑟,位于F島的核電站唐片,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏丢习。R本人自食惡果不足惜牵触,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望咐低。 院中可真熱鬧揽思,春花似錦、人聲如沸见擦。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鲤屡。三九已至损痰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間酒来,已是汗流浹背卢未。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留堰汉,地道東北人辽社。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像翘鸭,于是被迫代替她去往敵國(guó)和親滴铅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容