IOS開發(fā)系列——UIView專題之四:事件分發(fā)機(jī)制篇

4事件分發(fā)機(jī)制

iOS中的事件大概分為三種台猴,分別是Milti-Touch Events, Motion Events和Remote Control Events(events for controlling multimedia)。

4.1hitTest

iOS事件分發(fā)機(jī)制(一)hit-Testing

http://suenblog.duapp.com/blog/100031/iOS事件分發(fā)機(jī)制(一)%20hit-Testing

4.1.1事件檢測(cè)原理

每當(dāng)我們點(diǎn)擊了一下iOS設(shè)備的屏幕准验,UIKit就會(huì)生成一個(gè)事件對(duì)象UIEvent,然后會(huì)把這個(gè)Event分發(fā)給當(dāng)前active的app(官方原文說:Then it places the event object in the active app’s event queue.)

告知當(dāng)前活動(dòng)的app有事件之后廷没,UIApplication單例就會(huì)從事件隊(duì)列中去取最新的事件糊饱,然后分發(fā)給能夠處理該事件的對(duì)象。UIApplication獲取到Event之后颠黎,Application就糾結(jié)于到底要把這個(gè)事件傳遞給誰另锋,這時(shí)候就要依靠HitTest來決定了。

iOS中狭归,hit-Testing的作用就是找出這個(gè)觸摸點(diǎn)下面的View是什么夭坪,HitTest會(huì)檢測(cè)這個(gè)點(diǎn)擊的點(diǎn)是不是發(fā)生在這個(gè)View上,如果是的話过椎,就會(huì)去遍歷這個(gè)View的subviews室梅,直到找到最小的能夠處理事件的view,如果整了一圈沒找到能夠處理的view,則返回自身亡鼠。來一個(gè)簡(jiǎn)單的圖說明一下

假設(shè)我們現(xiàn)在點(diǎn)擊到了圖中的E赏殃,hit-testing將進(jìn)行如下步驟的檢測(cè)(不包含重寫hit-test并且返回非默認(rèn)View的情況)

1、觸摸點(diǎn)在ViewA內(nèi)间涵,所以檢查ViewA的Subview B仁热、C;

2勾哩、觸摸點(diǎn)不在ViewB內(nèi)股耽,觸摸點(diǎn)在ViewC內(nèi)部,所以檢查ViewC的Subview D钳幅、E物蝙;

3、觸摸點(diǎn)不在ViewD內(nèi)敢艰,觸摸點(diǎn)發(fā)生在ViewE內(nèi)部诬乞,并且ViewE沒有subview,所以ViewE屬于ViewA中包含這個(gè)點(diǎn)的最小單位钠导,所以ViewE變成了該次觸摸事件的hit-Test View震嫉;

4.1.2注意點(diǎn)

1、默認(rèn)的hit-testing順序是按照UIView中Subviews的逆順序牡属;

2票堵、如果View的同級(jí)別Subview中有重疊的部分,則優(yōu)先檢查頂部的Subview逮栅,如果頂部的Subview返回nil悴势,再檢查底部的Subview;

3措伐、Hit-Test也是比較聰明的特纤,檢測(cè)過程中有這么一點(diǎn),就是說如果點(diǎn)擊沒有發(fā)生在某View中侥加,那么該事件就不可能發(fā)生在View的Subview中捧存,所以檢測(cè)過程中發(fā)現(xiàn)該事件不在ViewB內(nèi),也直接就不會(huì)檢測(cè)在不在ViewF內(nèi)担败。也就是說昔穴,如果你的Subview設(shè)置了clipsToBounds=NO,實(shí)際顯示區(qū)域可能超出了superView的frame,你點(diǎn)擊超出的部分提前,是不會(huì)處理你的事件的吗货,就是這么任性!

4.1.3事件檢測(cè)實(shí)現(xiàn)

Hit-Test的檢查機(jī)制如上所示岖研,當(dāng)確定了Hit-TestView時(shí)卿操,如果當(dāng)前的application沒有忽略觸摸事件(UIApplication:isIgnoringInteractionEvents),則application就會(huì)去分發(fā)事件(sendEvent:->keywindow:sendEvent:)警检。

UIView中提供兩個(gè)方法用來確定hit-testing View,如下所示

-(UIView*)hitTest:(CGPoint)pointwithEvent:(UIEvent*)event;

// recursively calls-pointInside:withEvent:. point is in the receiver's coordinate system

-(BOOL)pointInside:(CGPoint)pointwithEvent:(UIEvent *)event;

// default returns YES if point is inbounds

當(dāng)一個(gè)View收到hitTest消息時(shí)孙援,會(huì)調(diào)用自己的pointInside:withEvent:方法,如果pointInside返回YES害淤,則表明觸摸事件發(fā)生在我自己內(nèi)部,則會(huì)遍歷自己的所有Subview去尋找最小單位(沒有任何子view)的UIView拓售,如果當(dāng)前View.userInteractionEnabled = NO, enabled=NO(UIControl),或者alpha<=0.01, hidden等情況的時(shí)候窥摄,hitTest就不會(huì)調(diào)用自己的pointInside了,直接返回nil础淤,然后系統(tǒng)就回去遍歷兄弟節(jié)點(diǎn)崭放。簡(jiǎn)而言之,可以寫成這樣

[st_hitTest:withEvent:]

- (UIView*)hitTest:(CGPoint)pointwithEvent:(UIEvent*)event{

if(self.alpha<=0.01||!self.userInteractionEnabled||self.hidden) {

returnnil;

}

BOOLinside=[selfpointInside:pointwithEvent:event];

UIView*hitView=nil;

if(inside) {

NSEnumerator*enumerator=[self.subviewsreverseObjectEnumerator];

for(UIView*subviewinenumerator) {

hitView=[subviewhitTest:pointwithEvent:event];

if(hitView) {

break;

}

}

if(!hitView) {

hitView=self;

}

returnhitView;

}else{

returnnil;

}

}

hit-Test是事件分發(fā)的第一步鸽凶,就算你的app忽略了事件币砂,也會(huì)發(fā)生hit-Test。確定了hit-TestView之后玻侥,才會(huì)開始進(jìn)行下一步的事件分發(fā)决摧。

我們可以利用hit-Test做一些事情,比如我們點(diǎn)擊了ViewA,我們想讓ViewB響應(yīng)凑兰,這個(gè)時(shí)候掌桩,我們只需要重寫View's

hitTest方法,返回ViewB就可以了姑食,雖然可能用不到波岛,但是偶爾還是會(huì)用到的。大概代碼如下:

[STPView]

@interfaceSTPView: UIView

@end

@implementationSTPView

- (instancetype)initWithFrame:(CGRect)frame{

self=[superinitWithFrame:frame];

if(self) {

UIButton*button=[UIButtonbuttonWithType:UIButtonTypeCustom];

button.frame=CGRectMake(0,0,CGRectGetWidth(frame),CGRectGetHeight(frame)/2);

button.tag=10001;

button.backgroundColor=[UIColorgrayColor];

[buttonsetTitle:@"Button1"forState:UIControlStateNormal];

[selfaddSubview:button];

[buttonaddTarget:selfaction:@selector(_buttonActionFired:)forControlEvents:UIControlEventTouchDown];

UIButton*button2=[UIButtonbuttonWithType:UIButtonTypeCustom];

button2.frame=CGRectMake(0,CGRectGetHeight(frame)/2,CGRectGetWidth(frame),CGRectGetHeight(frame)/2);

button2.tag=10002;

button2.backgroundColor=[UIColordarkGrayColor];

[button2setTitle:@"Button2"forState:UIControlStateNormal];

[selfaddSubview:button2];

[button2addTarget:selfaction:@selector(_buttonActionFired:)forControlEvents:UIControlEventTouchDown];

}

returnself;

}

- (void)_buttonActionFired:(UIButton*)button{

NSLog(@"=====Button Titled %@ ActionFired

", [buttontitleForState:UIControlStateNormal]);

}

- (UIView*)hitTest:(CGPoint)pointwithEvent:(UIEvent*)event{

UIView*hitView=[superhitTest:pointwithEvent:event];

if(hitView==[selfviewWithTag:10001]) {

return[selfviewWithTag:10002];

}

returnhitView;

}

@end

4.1.4利用catalog實(shí)現(xiàn)hitTest

來自STKit音半,這個(gè)category的目的就是方便的編寫hitTest方法则拷,由于hitTest方法是override,而不是delegate曹鸠,所以使用默認(rèn)的實(shí)現(xiàn)方式就比較麻煩隔躲。Category如下

[UIView+HitTest.h]

/**

* @abstract hitTestBlock

*

* @param其余參數(shù)參考UIView hitTest:withEvent:

* @param returnSuper是否返回Super的值。

*如果*returnSuper=YES,則代表會(huì)返回super hitTest:withEvent:,否則則按照block的返回值(即使是nil)

*

* @discussion切記物延,千萬不要在這個(gè)block中調(diào)用self hitTest:withPoint,否則則會(huì)造成遞歸調(diào)用宣旱。

*這個(gè)方法就是hitTest:withEvent的一個(gè)代替。

*/

typedefUIView*(^STHitTestViewBlock)(CGPointpoint, UIEvent*event,BOOL*returnSuper);

typedefBOOL(^STPointInsideBlock)(CGPointpoint, UIEvent*event,BOOL*returnSuper);

@interfaceUIView(STHitTest)

/// althought this is strong ,but i deal it with copy

@property(nonatomic,strong)STHitTestViewBlockhitTestBlock;

@property(nonatomic,strong)STPointInsideBlockpointInsideBlock;

@end

[UIView+HitTest.m]

@implementationUIView(STHitTest)

conststaticNSString*STHitTestViewBlockKey=@"STHitTestViewBlockKey";

conststaticNSString*STPointInsideBlockKey=@"STPointInsideBlockKey";

+ (void)load{

method_exchangeImplementations(class_getInstanceMethod(self,@selector(hitTest:withEvent:)),

class_getInstanceMethod(self,@selector(st_hitTest:withEvent:)));

method_exchangeImplementations(class_getInstanceMethod(self,@selector(pointInside:withEvent:)),

class_getInstanceMethod(self,@selector(st_pointInside:withEvent:)));

}

- (UIView*)st_hitTest:(CGPoint)pointwithEvent:(UIEvent*)event{

NSMutableString*spaces=[NSMutableStringstringWithCapacity:20];

UIView*superView=self.superview;

while(superView) {

[spacesappendString:@"----"];

superView=superView.superview;

}

NSLog(@"%@%@:[hitTest:withEvent:]",spaces,NSStringFromClass(self.class));

UIView*deliveredView=nil;

//如果有hitTestBlock的實(shí)現(xiàn)叛薯,則調(diào)用block

if(self.hitTestBlock) {

BOOLreturnSuper=NO;

deliveredView=self.hitTestBlock(point,event,&returnSuper);

if(returnSuper) {

deliveredView=[selfst_hitTest:pointwithEvent:event];

}

}else{

deliveredView=[selfst_hitTest:pointwithEvent:event];

}

//NSLog(@"%@%@:[hitTest:withEvent:] Result:%@", spaces,? NSStringFromClass(self.class), NSStringFromClass(deliveredView.class));

returndeliveredView;

}

- (BOOL)st_pointInside:(CGPoint)pointwithEvent:(UIEvent*)event{

NSMutableString*spaces=[NSMutableStringstringWithCapacity:20];

UIView*superView=self.superview;

while(superView) {

[spacesappendString:@"----"];

superView=superView.superview;

}

NSLog(@"%@%@:[pointInside:withEvent:]",spaces,NSStringFromClass(self.class));

BOOLpointInside=NO;

if(self.pointInsideBlock) {

BOOLreturnSuper=NO;

pointInside=self.pointInsideBlock(point,event,&returnSuper);

if(returnSuper) {

pointInside=[selfst_pointInside:pointwithEvent:event];

}

}else{

pointInside=[selfst_pointInside:pointwithEvent:event];

}

returnpointInside;

}

- (void)setHitTestBlock:(STHitTestViewBlock)hitTestBlock{

objc_setAssociatedObject(self, (__bridgeconstvoid*)(STHitTestViewBlockKey),

hitTestBlock,OBJC_ASSOCIATION_COPY);

}

- (STHitTestViewBlock)hitTestBlock{

returnobjc_getAssociatedObject(self, (__bridgeconstvoid*)(STHitTestViewBlockKey));

}

- (void)setPointInsideBlock:(STPointInsideBlock)pointInsideBlock{

objc_setAssociatedObject(self, (__bridgeconstvoid*)(STPointInsideBlockKey),

pointInsideBlock,OBJC_ASSOCIATION_COPY);

}

- (STPointInsideBlock)pointInsideBlock{

returnobjc_getAssociatedObject(self, (__bridgeconstvoid*)(STPointInsideBlockKey));

}

@end

代碼很簡(jiǎn)單浑吟,就是利用iOS的runtime能力,在hitTest執(zhí)行之前耗溜,插入了一個(gè)方法组力。如果有看不懂的,可以參考我以前的博客iOS面向切面編程

4.2Responder Chain

參考文檔:

iOS事件分發(fā)機(jī)制(二)The ResponderChain

http://suenblog.duapp.com/blog/100032/iOS事件分發(fā)機(jī)制(二)The%20Responder%20Chain

4.2.1事件傳遞原理

響應(yīng)鏈簡(jiǎn)單來說抖拴,就是一系列的相互關(guān)聯(lián)的對(duì)象燎字,從firstResponder開始腥椒,到application對(duì)象結(jié)束,如果firstResponder無法響應(yīng)事件候衍,則交給nextResponder來處理笼蛛,直到結(jié)束為止。iOS中很多類型的事件分發(fā)蛉鹿,都依賴于響應(yīng)鏈滨砍;在響應(yīng)鏈中,所有對(duì)象的基類都是UIResponder妖异,也就是說所有能響應(yīng)事件的類都是UIResponder的子類惋戏,UIApplication/ UIView/ UIViewController都是UIResponder的子類,這說明所有的Views他膳,絕大部分Controllers(不用來管理View的Controller除外)都可以響應(yīng)事件响逢。

PS:CALayer不是UIResponder的子類,這說明CALayer無法響應(yīng)事件棕孙,這也是UIView和CALayer的重要區(qū)別之一舔亭。

如果找到的hitTestView無法處理這個(gè)事件,事件就通過響應(yīng)鏈往上傳遞(hitTestView算是最早的Responder),直到找到一個(gè)可以處理的Responder為止散罕。

舉個(gè)例子分歇,如果觸摸通過hitTest確定的是一個(gè)View,而這個(gè)View沒有處理事件欧漱,則事件會(huì)發(fā)送給nextResponder去處理职抡,通常是superView,有關(guān)nextResponder的事件傳遞過程误甚,官方給出了一張很形象的圖,如下所示:


PS:View處理事件的方式有手勢(shì)或者重寫touchesEvent方法或者利用系統(tǒng)封裝好的組件(UIControls)缚甩。

圖中所表示的正是nextResponder的查找過程,兩種方式分別對(duì)應(yīng)兩種app的架構(gòu)窑邦,左邊的那種app架構(gòu)比較簡(jiǎn)單擅威,只有一個(gè)VC,右邊的稍微復(fù)雜一些冈钦,但是尋找路線的原則是一樣的郊丛,先解釋一下,UIResponder本身是不會(huì)去存儲(chǔ)或者設(shè)置nextResponder的瞧筛,所謂的nextResponder都是子類去實(shí)現(xiàn)的(這里說的是UIView厉熟,UIViewController,UIApplication)较幌,關(guān)于nextResponder的值總結(jié)如下:

1揍瑟、UIView的nextResponder是直接管理它的UIViewController(也就是VC.view.nextResponder=VC),如果當(dāng)前View不是ViewController直接管理的View,則nextResponder是它的superView(view.nextResponder= view.superView)

2乍炉、UIViewController的nextResponder是它直接管理的View的superView (VC. nextResponder = VC.view.superView)

3绢片、UIWindow的nextResponder是UIApplication

4滤馍、UIApplication的nextResponder是UIApplicationDelegate(官方文檔說是nil)

我寫了一段代碼,打印當(dāng)前UIResponder的所有nextResponder底循,大家可以拿去試一下巢株,代碼很簡(jiǎn)單,如下:

[STLogResponderChain]

voidSTLogResponderChain(UIResponder*responder) {

NSLog(@"------------------The

Responder Chain------------------");

NSMutableString*spaces=[NSMutableStringstringWithCapacity:4];

while(responder) {

NSLog(@"%@%@",spaces,responder.class);

responder=responder.nextResponder;

[spacesappendString:@"----"];

}

}

然后我測(cè)試了一下此叠,打印的日志如下圖所示:

[Log]

UIButton

----STPView

--------UIView

------------STPFeedViewController

----------------UIView

--------------------UIView

------------------------_STWrapperViewController

----------------------------UIView

--------------------------------UIView

------------------------------------STNavigationController

----------------------------------------STPWindow

--------------------------------------------UIApplication

------------------------------------------------STPAppDelegate

這樣比較清晰纯续,大家也會(huì)直觀的看到nextResponder的查找過程随珠。

4.2.2使用示例

接下來我們說正事了灭袁,假定我們現(xiàn)在有一個(gè)View是hitTestView,命名為STImageView,現(xiàn)在我們想讓這個(gè)image處理一些事情窗看,比如所有的圖片點(diǎn)下之后加一個(gè)灰色的效果茸歧,我們就把事件分發(fā)給它。

在UIResponder中显沈,提供以下幾個(gè)方法软瞎,幾個(gè)方法分別表示點(diǎn)擊的不同狀態(tài),大家看名字就能明白差不多:

-

(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;- (void)touchesMoved:(NSSet *)touches

withEvent:(UIEvent *)event;- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;- (void)touchesCancelled:(NSSet *)touches

withEvent:(UIEvent *)event;

如果我們想讓我們當(dāng)前的Responder處理事件,我們則需要重寫如下的幾個(gè)方法拉讯。我們的需求是手指按下圖片的時(shí)候加一個(gè)灰色的效果涤浇,松開的時(shí)候灰色消失。關(guān)于灰色的實(shí)現(xiàn)魔慷,我們暫定用一個(gè)View貼在ImageView上named

maskView只锭,然后用hidden來控制是否顯示(上一篇文章有說過,所有hidden的View默認(rèn)不接受任何事件)院尔。

我們需要在touchesBegan方法里面self.maskView.hidden = NO;然后在touchesEnded/ Cancelled里面self.maskView.hidden = YES;就可以實(shí)現(xiàn)我們的效果了蜻展,原理很簡(jiǎn)單,我們的hitTestView在事件分發(fā)的時(shí)候去處理事件邀摆,僅此而已纵顾。這里注意一下:UIImageView的默認(rèn)是不接受點(diǎn)擊事件的,如果想要實(shí)現(xiàn)如上所示效果栋盹,需要設(shè)置userInteractionEnabled=YES;

說到這里施逾,就有人產(chǎn)生了疑問,如果這么實(shí)現(xiàn)的話例获,那如果本身UIImageView還想讓下面的View處理事件該怎么辦汉额?會(huì)不會(huì)把所有的事件攔截下來?這里就說到了另一個(gè)問題躏敢,UIResponder在知道需要處理事件的時(shí)候闷愤,還是有決定權(quán)的,比如我可以決定讓整個(gè)響應(yīng)鏈繼續(xù)走下去件余,或者直接中斷掉整個(gè)響應(yīng)鏈讥脐。如果中斷了響應(yīng)鏈遭居,那么所有在鏈上的nextResponder都不會(huì)得知有事件發(fā)生,iOS也提供了這個(gè)方法旬渠,其實(shí)很簡(jiǎn)單:

我們?cè)谥貙慣ouchesEvents的時(shí)候俱萍,如果不想讓響應(yīng)鏈繼續(xù)傳遞,就不調(diào)用super對(duì)應(yīng)的實(shí)現(xiàn)就可以了告丢,相反枪蘑,有些時(shí)候你只需要做一個(gè)小改變,如上所示岖免,但是你不想中斷響應(yīng)鏈岳颇,你就需要調(diào)用父類對(duì)應(yīng)的實(shí)現(xiàn)。

這里有一點(diǎn)需要注意颅湘,一般來說话侧,我們?nèi)绻胍约禾幚硪恍┦录覀冃枰貙懭缟纤镜姆椒ù巢危绻覀兿胱约禾幚碚芭簦筒恍枰{(diào)用super。調(diào)用super的目的就是為了把事件傳遞給nextResponder鹿寨,并且如果我們?cè)趖ouchesBegan中沒有調(diào)用super新博,則super不會(huì)響應(yīng)其他的回掉(touchesMoved/touchesEnded),但是我們需要重寫所有如上所示的方法來確保我們的一切正常。touchesBegan和touchesEnded/touchesCancelled一定是成對(duì)出現(xiàn)的脚草,這點(diǎn)大家可以放心赫悄。

有關(guān)觸摸事件在響應(yīng)鏈上的分發(fā),就差不多這么多東西玩讳,最重要的是大家可以看那幾個(gè)touches方法涩蜘,多做實(shí)驗(yàn),就可以了解的更加深入熏纯。

4.2.3其他要點(diǎn)

這里有一些補(bǔ)充同诫,響應(yīng)鏈能夠處理很多東西,不僅僅是觸摸事件樟澜。一般來說误窖,如果我們需要一個(gè)對(duì)象去處理一個(gè)非觸摸事件(搖一搖,RemoteControlEvents秩贰,調(diào)用系統(tǒng)的復(fù)制霹俺、粘貼框等),我們要確保該對(duì)象是UIResponder子類毒费,如果我們要接收到事件的話丙唧,我們需要做兩件事情

1、重寫canBecomeFirstResponder觅玻,并且返回YES

2想际、在需要的時(shí)候像該對(duì)象發(fā)送becomeFirstResponder消息培漏。

我們有時(shí)候會(huì)遇到一些問題,比如我們重寫了motionEvents胡本,但是我們不能收到搖一搖的回調(diào)牌柄,或者我們的UIMenuController老是不彈出,我們就需要檢查一下侧甫,我們是否滿足了如上所示的條件珊佣,而且要確保becomeFirstResponder的發(fā)送時(shí)機(jī)正確。

當(dāng)然披粟,這個(gè)補(bǔ)充對(duì)于觸摸事件無效咒锻,觸摸事件的第一響應(yīng)者是根據(jù)hitTest確定而來的,有點(diǎn)繞僻爽,需要仔細(xì)捋捋虫碉。

需要注意的是:

如果你自己想自定義一個(gè)非TouchEvent的事件贾惦,當(dāng)需要繼續(xù)傳遞事件的話胸梆,切記不要在實(shí)現(xiàn)內(nèi)直接顯示的調(diào)用nextResponder的對(duì)應(yīng)方法, 而是直接調(diào)用super對(duì)應(yīng)的方法來讓這個(gè)事件繼續(xù)分發(fā)到響應(yīng)鏈须板。

到目前為止碰镜,事件的分發(fā)還沒有結(jié)束,之后會(huì)有一篇文章介紹一個(gè)很重要的角色习瑰,手勢(shì)绪颖。

最后,附上官方的文檔

Event Handling

Guide for iOS

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末甜奄,一起剝皮案震驚了整個(gè)濱河市柠横,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌课兄,老刑警劉巖牍氛,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異烟阐,居然都是意外死亡搬俊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門蜒茄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來唉擂,“玉大人,你說我怎么就攤上這事檀葛⊥嫠睿” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵屿聋,是天一觀的道長空扎。 經(jīng)常有香客問我庆聘,道長,這世上最難降的妖魔是什么勺卢? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任伙判,我火速辦了婚禮,結(jié)果婚禮上黑忱,老公的妹妹穿的比我還像新娘宴抚。我一直安慰自己,他們只是感情好甫煞,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布菇曲。 她就那樣靜靜地躺著,像睡著了一般抚吠。 火紅的嫁衣襯著肌膚如雪常潮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天楷力,我揣著相機(jī)與錄音喊式,去河邊找鬼。 笑死萧朝,一個(gè)胖子當(dāng)著我的面吹牛岔留,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播检柬,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼献联,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了何址?” 一聲冷哼從身側(cè)響起里逆,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎用爪,沒想到半個(gè)月后原押,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡项钮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年班眯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烁巫。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡署隘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出亚隙,到底是詐尸還是另有隱情磁餐,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站诊霹,受9級(jí)特大地震影響羞延,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜脾还,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一伴箩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鄙漏,春花似錦嗤谚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至桦踊,卻和暖如春椅野,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背籍胯。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來泰國打工竟闪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芒炼。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓瘫怜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親本刽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

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