ZSSRichTextEditor換WKWebView

前言

Apple will no longer accept submissions of new apps that use UIWebView as of April 30, 2020 and app updates that use UIWebView as of December 2020. Instead, use WKWebView for improved security and reliability.

1.首先稍微封裝了一下WKWebView

MTWKWebView.h

#import <WebKit/WebKit.h>

typedef void(^MTWKJSCallBack)(WKScriptMessage *message);

@protocol MTWKWebViewDelegate;

@interface MTWKWebView : WKWebView<WKNavigationDelegate,WKScriptMessageHandler>
@property (nonatomic, weak)id<MTWKWebViewDelegate>wkDelegate;

//運行JS
//- (id)evaluatingJavaScriptFromString:(NSString *)script;

//添加監(jiān)聽方法
- (void)addMessageHandlerName:(NSString *)messageHandlerName  callBack:(MTWKJSCallBack)callBack;

//添加js方法 內(nèi)部注冊監(jiān)聽 JS只需要調(diào)用方法即可
- (void)addScriptFuncName:(NSString *)name  callBack:(MTWKJSCallBack)callBack;

//注入js
- (void)addScriptSource:(NSString *)scriptSource;

//添加所有監(jiān)聽
- (void)registScriptMessage;

//移除所有監(jiān)聽
- (void)removeScriptMessage;
@end


@protocol MTWKWebViewDelegate <NSObject>
@optional
//網(wǎng)頁內(nèi)容開始加載到web view的時候調(diào)用
- (BOOL)wkWebView:(MTWKWebView *)wkWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(WKNavigationType)navigationType;
//根據(jù)導航的返回信息來判斷是否加載網(wǎng)頁
- (BOOL)wkWebView:(MTWKWebView *)wkWebView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse;
- (void)wkWebViewDidStartLoad:(MTWKWebView *)wkWebView;
- (void)wkWebViewDidFinishLoad:(MTWKWebView *)wkWebView;
- (void)wkWebView:(MTWKWebView *)wkWebView didFailLoadWithError:(NSError *)error ;

@end

MTWKWebView.m

#import "MTWKWebView.h"

@interface MTWKWebView ()
@property (nonatomic, strong) NSMutableDictionary <NSString *,MTWKJSCallBack> *jsHook;
@end

@implementation MTWKWebView
- (void)dealloc
{
    NSLog(@"MTWKWebView dealloc");
}

#pragma mark - Public
//添加監(jiān)聽方法
- (void)addMessageHandlerName:(NSString *)messageHandlerName  callBack:(MTWKJSCallBack)callBack {
    [self _addMessageHandlerName:messageHandlerName callBack:callBack];
}

//添加js方法 內(nèi)部注冊監(jiān)聽 JS只需要調(diào)用方法即可
- (void)addScriptFuncName:(NSString *)name  callBack:(MTWKJSCallBack)callBack {
    [self _addScriptFuncName:name callBack:callBack];
}

//注入js
- (void)addScriptSource:(NSString *)scriptSource{
    [self _addScriptSource:scriptSource];
}

//添加所有監(jiān)聽
- (void)registScriptMessage {
    [self removeScriptMessage];
    __weak typeof(self)weakSelf = self;
    [self.jsHook enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, MTWKJSCallBack  _Nonnull obj, BOOL * _Nonnull stop) {
        [weakSelf.configuration.userContentController addScriptMessageHandler:self name:key];
    }];
}

//移除所有監(jiān)聽
- (void)removeScriptMessage {
    [self.jsHook enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, MTWKJSCallBack  _Nonnull obj, BOOL * _Nonnull stop) {
        [self.configuration.userContentController removeScriptMessageHandlerForName:key];
    }];
}

#pragma mark - Private
//添加監(jiān)聽方法
- (void)_addMessageHandlerName:(NSString *)messageHandlerName  callBack:(MTWKJSCallBack)callBack {
    [self.configuration.userContentController addScriptMessageHandler:self name:messageHandlerName];
    [self.jsHook setObject:callBack forKey:messageHandlerName];
}

//注入js
- (void)_addScriptSource:(NSString *)scriptSource{
    WKUserScript *script = [[WKUserScript alloc] initWithSource:scriptSource injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:true];
    [self.configuration.userContentController addUserScript:script];
}

//添加js方法 內(nèi)部注冊監(jiān)聽 JS只需要調(diào)用方法即可
- (void)_addScriptFuncName:(NSString *)name  callBack:(MTWKJSCallBack)callBack{
    NSString *messageHandlerName = [NSString stringWithFormat:@"MTScritpFunc_%@",name];
    NSString *userScriptSource = [NSString stringWithFormat:@"function %@(s) {window.webkit.messageHandlers.%@.postMessage(s);}",name,messageHandlerName];
    
    //注入JS
    [self _addScriptSource:userScriptSource];
    //注冊監(jiān)聽
    [self _addMessageHandlerName:messageHandlerName callBack:callBack];
}

#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    if ([self.wkDelegate respondsToSelector:@selector(wkWebView:shouldStartLoadWithRequest:navigationType:)]) {
        BOOL shouldStart = [self.wkDelegate wkWebView:self shouldStartLoadWithRequest:navigationAction.request navigationType:navigationAction.navigationType];
        
        if (shouldStart == false) {
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
    if ([self.wkDelegate respondsToSelector:@selector(wkWebView:decidePolicyForNavigationResponse:)]){
        BOOL shouldLoad = [self.wkDelegate wkWebView:self decidePolicyForNavigationResponse:navigationResponse];
        
        if (shouldLoad == false) {
            decisionHandler(WKNavigationResponsePolicyCancel);
            return;
        }
    }
    decisionHandler(WKNavigationResponsePolicyAllow);
}

//開始加載
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
    if ([self.wkDelegate respondsToSelector:@selector(wkWebViewDidStartLoad:)]) {
        [self.wkDelegate wkWebViewDidStartLoad:self];
    }
}

//跳轉到其他的服務器
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
    
}

//網(wǎng)頁由于某些原因加載失敗
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
    if ([self.wkDelegate respondsToSelector:@selector(wkWebView:didFailLoadWithError:)]) {
        [self.wkDelegate wkWebView:self didFailLoadWithError:error];
    }
}

//網(wǎng)頁開始接收網(wǎng)頁內(nèi)容
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {
    
}

//網(wǎng)頁導航加載完畢
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
    if ([self.wkDelegate respondsToSelector:@selector(wkWebViewDidFinishLoad:)]) {
        [self.wkDelegate wkWebViewDidFinishLoad:self];
    }
}

//網(wǎng)頁導航加載失敗
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
    if ([self.wkDelegate respondsToSelector:@selector(wkWebView:didFailLoadWithError:)]) {
        [self.wkDelegate wkWebView:self didFailLoadWithError:error];
    }
}

//網(wǎng)頁加載內(nèi)容進程終止
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {
}

#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if ([message.name isKindOfClass:[NSString class]] == false) {
        return ;
    }
    
    if ([self.jsHook objectForKey:message.name]) {
        MTWKJSCallBack obj = [self.jsHook objectForKey:message.name];
        if (obj) {
            obj(message);
        }
    }
}

#pragma mark - Getter
- (NSMutableDictionary<NSString *,MTWKJSCallBack> *)jsHook {
    if (!_jsHook) {
        _jsHook = [NSMutableDictionary dictionary];
    }
    return _jsHook;
}

@end

addScriptFuncName: callBack:主要是針對JS直接調(diào)用方法,比如

onclick="getImg(0)"

或者

window.getImg(0)

2.ZSSRichTextEditor

雖然ZSSRichTextEditor已經(jīng)適配了WKWebView,仍然有不少問題踩寇。而且項目加了一些業(yè)務代碼退腥,所以不能簡單的移植激况,記錄一下修改的地方

去掉鍵盤自帶的工具條

/**
 
 WKWebView modifications for hiding the inputAccessoryView
 
 **/
@interface WKWebView (HackishAccessoryHiding)
@property (nonatomic, assign) BOOL hidesInputAccessoryView;
@end

@implementation WKWebView (HackishAccessoryHiding)

static const char * const hackishFixClassName = "WKWebBrowserViewMinusAccessoryView";
static Class hackishFixClass = Nil;

- (UIView *)hackishlyFoundBrowserView {
    UIScrollView *scrollView = self.scrollView;
    
    UIView *browserView = nil;
    for (UIView *subview in scrollView.subviews) {
        if ([NSStringFromClass([subview class]) hasPrefix:@"WKWebBrowserView"]) {
            browserView = subview;
            break;
        }
    }
    return browserView;
}

- (id)methodReturningNil {
    return nil;
}

- (void)ensureHackishSubclassExistsOfBrowserViewClass:(Class)browserViewClass {
    if (!hackishFixClass) {
        Class newClass = objc_allocateClassPair(browserViewClass, hackishFixClassName, 0);
        newClass = objc_allocateClassPair(browserViewClass, hackishFixClassName, 0);
        IMP nilImp = [self methodForSelector:@selector(methodReturningNil)];
        class_addMethod(newClass, @selector(inputAccessoryView), nilImp, "@@:");
        objc_registerClassPair(newClass);
        
        hackishFixClass = newClass;
    }
}

- (BOOL) hidesInputAccessoryView {
    UIView *browserView = [self hackishlyFoundBrowserView];
    return [browserView class] == hackishFixClass;
}

- (void) setHidesInputAccessoryView:(BOOL)value {
    UIView *browserView = [self hackishlyFoundBrowserView];
    if (browserView == nil) {
        return;
    }
    [self ensureHackishSubclassExistsOfBrowserViewClass:[browserView class]];
    
    if (value) {
        object_setClass(browserView, hackishFixClass);
    }
    else {
        Class normalClass = objc_getClass("WKWebBrowserView");
        object_setClass(browserView, normalClass);
    }
    [browserView reloadInputViews];
}

@end

使用
wkWebView.hidesInputAccessoryView = YES;

webview顯示自動彈出鍵盤功能

#pragma mark - Convenience replacement for keyboardDisplayRequiresUserAction in WKWebview

+ (void)allowDisplayingKeyboardWithoutUserAction {
    Class class = NSClassFromString(@"WKContentView");
    NSOperatingSystemVersion iOS_11_3_0 = (NSOperatingSystemVersion){11, 3, 0};
    NSOperatingSystemVersion iOS_12_2_0 = (NSOperatingSystemVersion){12, 2, 0};
    NSOperatingSystemVersion iOS_13_0_0 = (NSOperatingSystemVersion){13, 0, 0};
    if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_13_0_0]) {
        SEL selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:");
        Method method = class_getInstanceMethod(class, selector);
        IMP original = method_getImplementation(method);
        IMP override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, BOOL arg3, id arg4) {
            ((void (*)(id, SEL, void*, BOOL, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3, arg4);
        });
        method_setImplementation(method, override);
    }
    else if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_12_2_0]) {
        SEL selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:");
        Method method = class_getInstanceMethod(class, selector);
        IMP original = method_getImplementation(method);
        IMP override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, BOOL arg3, id arg4) {
            ((void (*)(id, SEL, void*, BOOL, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3, arg4);
        });
        method_setImplementation(method, override);
    }
    else if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_11_3_0]) {
        SEL selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:");
        Method method = class_getInstanceMethod(class, selector);
        IMP original = method_getImplementation(method);
        IMP override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, BOOL arg3, id arg4) {
            ((void (*)(id, SEL, void*, BOOL, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3, arg4);
        });
        method_setImplementation(method, override);
    } else {
        SEL selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:");
        Method method = class_getInstanceMethod(class, selector);
        IMP original = method_getImplementation(method);
        IMP override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, id arg3) {
            ((void (*)(id, SEL, void*, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3);
        });
        method_setImplementation(method, override);
    }
}

//TODO: Is this behavior correct? Is it the right replacement?
//    self.editorView.keyboardDisplayRequiresUserAction = NO;
[ZSSRichTextEditor allowDisplayingKeyboardWithoutUserAction];

添加圖片空白

這里參考的RichTextEditor的做法

其實我也不太明白原因

修改js

//先創(chuàng)建一個<span></span>標簽
//延遲0.3s等待動態(tài)增加的標簽<span>加入到DOM中,再向其中新增圖片
//為什么不直接創(chuàng)建<img> 標簽并指定src呢? 因為圖片顯示不出來,不知道什么原因
zss_editor.priInsertImage = function(){
    zss_editor.restorerange();
    var html = '<span id="imageSpan"></span>';
    zss_editor.insertHTML(html);
    zss_editor.enabledEditingItems();
}

//插入url圖片
zss_editor.insertImage = function(url, alt) {
    var img = document.createElement('img');//創(chuàng)建一個標簽
    img.setAttribute('src',url);//給標簽定義src鏈接
    img.setAttribute('style','width:100%;');//給標簽定義寬度
    img.setAttribute('alt',alt);//給標簽定義alt
    document.getElementById('imageSpan').appendChild(img);//放到指定的id里
    
    zss_editor.deletInsertImageSpan();//刪除插入url圖片時創(chuàng)建的<span></span>標簽
}

//刪除插入url圖片時創(chuàng)建的<span></span>標簽
zss_editor.deletInsertImageSpan = function(){
    var html = $('#imageSpan').html();
    $('#imageSpan').before(html);
    $('#imageSpan').remove();
}

修改insertImage: alt:方法

- (void)insertImage:(NSString *)url alt:(NSString *)alt {
    if (alt == nil) {
        alt = @"";
    }
    
//    NSString *trigger = [NSString stringWithFormat:@"zss_editor.insertImage(\"%@\", \"%@\");", url, alt];
//    [self.editorView evaluateJavaScript:trigger completionHandler:nil];
    
    
    
    //增加<span>標簽
    [self.editorView evaluateJavaScript:@"zss_editor.priInsertImage();" completionHandler:nil];
    
    //延遲1s是在等待動態(tài)增加的標簽<span>加入到DOM中,再向其中新增圖片
   //延遲1秒太久了  改成0.3
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSString *trigger = [NSString stringWithFormat:@"zss_editor.insertImage(\"%@\", \"%@\");", url, alt];
        [self.editorView evaluateJavaScript:trigger completionHandler:nil];
        
    });
}

圖片標簽寬度屬性需要設置

插入鏈接

修改showInsertLinkDialogWithLink: title:

[self focusTextEditor];
// Save the selection location
[self.editorView evaluateJavaScript:@"zss_editor.prepareInsert();" completionHandler:nil];
if (!self.selectedLinkURL) {
    [self insertLink:linkURL.text title:title.text];
} else {
    [self updateLink:linkURL.text title:title.text];
}

鍵盤彈出收回

CGFloat bottomSafeAreaInset = 0.0;
 if (self->_alwaysShowToolbar) {
    if (@available(iOS 11.0, *)) {
        bottomSafeAreaInset = self.view.safeAreaInsets.bottom;
    }
    frame.origin.y = self.view.frame.size.height - sizeOfToolbar - bottomSafeAreaInset;
    } else {
         frame.origin.y = self.view.frame.size.height + keyboardHeight;
}
self.toolbarHolder.frame = frame;

// Editor View
CGRect editorFrame = self.editorView.frame;
if (self->_alwaysShowToolbar) {
     editorFrame.size.height = ((self.view.frame.size.height - sizeOfToolbar - bottomSafeAreaInset - editorFrame.origin.y) - extraHeight);
    } else {
    editorFrame.size.height = self.view.frame.size.height;
}

輸入回調(diào)

//注冊監(jiān)聽
[wkWebView addMessageHandlerName:@"contentPasteCallback" callBack:^(WKScriptMessage *message) {
    weakSelf.editorPaste = YES;
}];

[wkWebView addMessageHandlerName:@"contentInputCallback" callBack:^(WKScriptMessage *message) {
    if (_receiveEditorDidChangeEvents) {
        [self updateEditor];
    }
        
    [self getText:^(NSString *text) {
        [self checkForMentionOrHashtagInText:text];
    }];
        
    if (self.editorPaste) {
        [self blurTextEditor];
        self.editorPaste = NO;
    }
 }];

//- (void)wkWebViewDidFinishLoad:(MTWKWebView *)wkWebView中添加
[self.editorView evaluateJavaScript:@"document.getElementById('zss_editor_content').addEventListener('paste', function(){window.webkit.messageHandlers.contentPasteCallback.postMessage(null);}, false);" completionHandler:nil];
[self.editorView evaluateJavaScript:@"document.getElementById('zss_editor_content').addEventListener('input', function(){window.webkit.messageHandlers.contentInputCallback.postMessage(null);}, false);" completionHandler:nil];

創(chuàng)建WKWebView

//創(chuàng)建web編輯器
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = userContentController;
//set data detection to none so it doesnt conflict
configuration.dataDetectorTypes = WKDataDetectorTypeNone;

MTWKWebView *wkWebView = [[MTWKWebView alloc] initWithFrame:frame configuration:configuration];
wkWebView.UIDelegate = self;
wkWebView.wkDelegate = self;
wkWebView.navigationDelegate = wkWebView;
wkWebView.hidesInputAccessoryView = YES;
wkWebView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
wkWebView.scrollView.delegate = self;
wkWebView.scrollView.bounces = NO;
self.editorView = wkWebView;
[self.view addSubview:wkWebView];

//注冊監(jiān)聽
__weak typeof(self)weakSelf = self;

NSString *scriptString = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
[wkWebView addScriptSource:scriptString];

[wkWebView addMessageHandlerName:@"contentPasteCallback" callBack:^(WKScriptMessage *message) {
    weakSelf.editorPaste = YES;
}];

[wkWebView addMessageHandlerName:@"contentInputCallback" callBack:^(WKScriptMessage *message) {
    if (_receiveEditorDidChangeEvents) {
        [self updateEditor];
    }
    
    [self getText:^(NSString *text) {
        [self checkForMentionOrHashtagInText:text];
    }];
    
    if (self.editorPaste) {
        [self blurTextEditor];
        self.editorPaste = NO;
    }
}];

//TODO: Is this behavior correct? Is it the right replacement?
//    self.editorView.keyboardDisplayRequiresUserAction = NO;
[ZSSRichTextEditor allowDisplayingKeyboardWithoutUserAction];

僅做參考

參考資料:

ZSSRichTextEditor

RichTextEditor

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末急但,一起剝皮案震驚了整個濱河市霎肯,隨后出現(xiàn)的幾起案子钾菊,更是在濱河造成了極大的恐慌辑畦,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件布朦,死亡現(xiàn)場離奇詭異囤萤,居然都是意外死亡,警方通過查閱死者的電腦和手機是趴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門涛舍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人唆途,你說我怎么就攤上這事富雅。” “怎么了肛搬?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵没佑,是天一觀的道長。 經(jīng)常有香客問我温赔,道長蛤奢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任陶贼,我火速辦了婚禮啤贩,結果婚禮上,老公的妹妹穿的比我還像新娘拜秧。我一直安慰自己痹屹,他們只是感情好,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布枉氮。 她就那樣靜靜地躺著痢掠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嘲恍。 梳的紋絲不亂的頭發(fā)上足画,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音佃牛,去河邊找鬼淹辞。 笑死,一個胖子當著我的面吹牛俘侠,可吹牛的內(nèi)容都是我干的象缀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼爷速,長吁一口氣:“原來是場噩夢啊……” “哼央星!你這毒婦竟也來了?” 一聲冷哼從身側響起惫东,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤莉给,失蹤者是張志新(化名)和其女友劉穎毙石,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颓遏,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡徐矩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了叁幢。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滤灯。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖曼玩,靈堂內(nèi)的尸體忽然破棺而出鳞骤,到底是詐尸還是另有隱情,我是刑警寧澤黍判,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布豫尽,位于F島的核電站,受9級特大地震影響样悟,放射性物質(zhì)發(fā)生泄漏拂募。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一窟她、第九天 我趴在偏房一處隱蔽的房頂上張望陈症。 院中可真熱鬧,春花似錦震糖、人聲如沸录肯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽论咏。三九已至,卻和暖如春颁井,著一層夾襖步出監(jiān)牢的瞬間厅贪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工雅宾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留养涮,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓眉抬,卻偏偏與公主長得像贯吓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蜀变,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355