六天完成一個簡單iOS App - 第六天

第六天任務

  1. 推薦標簽頁面的完成
  2. 圓形頭像的設置和封裝
  3. 評論界面的完成
  4. 新帖界面的完成
  5. 發布界面的完成

推薦標簽頁面的完成

點擊精華頁面左上角按鈕來到推薦標簽界面。

推薦標簽界面

推薦標簽的實現有了之前的經驗就非常簡單了,根據MVC原則創建文件,同樣在cell中添加模型屬性,根據模型為cell內控件賦值。
唯一有一個注意點:當點擊進入推薦標簽頁面,如果此時數據還沒有獲取到,點擊返回,SVP的提醒還在,block會對控制器產生強引用,如果block還沒有執行完,控制器是不會死的,block執行完畢之后,強引用才會被放開,控制器才會被銷毀,所以block中需要使用弱引用__weak typeof(self) weakSelf = self;,但是雖然使用弱引用,控制器在該被銷毀的時候就會被銷毀,但是block內的代碼還是會繼續執行的,只不過weakSelf會被置為nil,所以我們需要在一點擊返回的時候將請求取消,在-(void)viewWillDisappear:(BOOL)animated當控制器view即將消失的時候 隱藏SVP 并且取消請求,但是AFN中如果正在發送請求當請求還沒有返回的時候,取消請求會來到failure方法中,所以需要在failure方法中進行判斷if (error.code == NSURLErrorCancelled),如果是需要請求的那么直接返回即可,如果是請求失敗,則提醒用戶。

但是如果是進入下一個界面,則不需要取消請求

圓形頭像的設置

圓形頭像使用Quartz2D來實現,實現思路:開啟圖形上下文,在圖形上下文上添加一個圓,裁剪,然后將圖片繪制到圓形區域,然后獲得圖片即是圓形圖片。
這里對圓形頭像進行了封裝,給image添加分類,傳入一張圖片,返回一張圓形圖片
UIImage+CLExtension.m

#import "UIImage+CLExtension.h"
@implementation UIImage (CLExtension)
/** 返回圓形圖片 */
-(instancetype)circleImage
{
    // 開啟圖形上下文
    UIGraphicsBeginImageContext(self.size);
    // 上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 添加一個圓
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    CGContextAddEllipseInRect(ctx, rect);
    // 裁剪
    CGContextClip(ctx);
    // 繪制圖片
    [self drawInRect:rect];
    // 獲得圖片
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    // 關閉圖形上下文
    UIGraphicsEndImageContext();   
    return image;
}
/** 直接根據image name設置圓角 */
+(instancetype)circleImageNamed:(NSString *)name
{
    return [[UIImage imageNamed:name] circleImage];
}
@end

傳入圖片或者直接傳入圖片name,返回一張圓形圖片。

因為一個項目中的頭像一般是統一的,如果是方形的則項目中所有頭像都是方形的,而如果要修改為圓形的則每一處頭像設置都需要更改,為了能夠統一控制項目中所有頭像的形狀,我們給imageView添加設置頭像的分類

#import "UIImageView+CLExtension.h"
#import <UIImageView+WebCache.h>

@implementation UIImageView (CLExtension)

/** 默認為圓形頭像 */
- (void)setHeader:(NSString *)url
{
    [self setCircleHeader:url];
}
/** 設置圓形頭像 */
- (void)setCircleHeader:(NSString *)url
{
       // 將占位圖片也轉化為圓形 其實占位圖片本來就是圓形
    UIImage *placeholder = [UIImage circleImageNamed:@"defaultUserIcon"];
    [self sd_setImageWithURL:[NSURL URLWithString:url] placeholderImage:placeholder completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
        // 如果image為空則返回占位圖片
        if (image == nil) return;
        self.image = [image circleImage];
    }];
}
/** 設置方形頭像 */
- (void)setRectHeader:(NSString *)url
{
    UIImage *placeholder = [UIImage imageNamed:@"defaultUserIcon"];
    [self sd_setImageWithURL:[NSURL URLWithString:url] placeholderImage:placeholder];
}
@end

而項目中設置頭像也變得非常簡單,直接[imageView setHeader:url]即可,這個時候全世界的頭像都變成圓的啦。


圓形頭像

而當需要將項目中所有頭像由方形轉變為圓形的時候,只需要在分類方法中將[self setCircleHeader:url];修改為[self setRectHeader:url];即可,這個時候全世界的頭像又都會變成方的。

評論界面的完成。

先來看一下評論界面的內容


評論界面

點擊cell會進入到評論界面,評論界面使用xib進行描述,分為上面tableView和底部工具條。

評論界面xib

需要注意的還是約束的添加,因為這里需要底部工具條隨著鍵盤的彈出上移,所以底部工具條的底部與SuperView的底部間距為零,如圖

底部工具條最底端約束

然后我們拿到這個約束,監控鍵盤的彈出,當鍵盤彈出的時候,將約束間距修改為鍵盤的高度,同時也可以拿到鍵盤彈出的時間,使底部工具條在相同時間內上移即可。

// 添加監聽
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
- (void)keyboardWillChangeFrame:(NSNotification *)note
{    
    // 修改約束  = 屏幕的高度 - 鍵盤的y值
    CGFloat keyboardY = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].origin.y;
    CGFloat screenH = [UIScreen mainScreen].bounds.size.height;
    self.bottomMargin.constant = screenH - keyboardY;
    // 執行動畫
    // 獲取執行動畫的時間
    CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    [UIView animateWithDuration:duration animations:^{
        // 更新約束
        [self.view layoutIfNeeded];
    }];
}

注意:控制器銷毀的時候一定要記得移除監聽

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

評論界面上方cell的顯示有兩種做法。

  1. 總共分為三組cell 第一組cell 用來顯示內容 第二組cell用來顯示 最熱評論 第三組cell用來顯示最新評論

  2. cell分為兩組,將cell的內容轉化為heardView。

如果tableView的style設置為 plain 而不是group,同時設置tableView的頭標題 heardView , tableView往上面滑動的時候 heardView就會停留在屏幕最上方。

heardTitle的設置可以在代理方法中直接返回內容

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section

但是為了能夠使heardView更加豐富,可以直接返回UIview

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section

如果heardView特別多 可以使用 UITableViewHeaderFooterView
UITableViewHeaderFooterView和cell一樣有重用機制,需要注冊,并從緩存池中取

也可以繼承UITableViewHeaderFooterView進行自定義
通過重寫- (instancetype)initWithReuseIdentifier:(NSString *)方法對其內部進行一些修改- (void)layoutSubviews對其內部的控件frame進行一些修改

一般如果想要修改控件內子控件的frame,等但是發現怎么改都會被改回去,那么這個時候可以嘗試在layoutSubViews中進行修改,先讓super設置完畢之后,我們在進行設置進行覆蓋,用來覆蓋對子控件的一些設置。

cell的高度計算
評論界面的cell使用的是UITableViewAutomaticDimension自動計算高度,這樣cell在添加約束的時候需要額外小心,先來看一下評論cell的xib

評論界面cell的xib

值得注意的評論的內容可能是音頻button也可能是label,幾個需要額外注意的約束是,內容label與cell的contentView底部間距固定為10,保證cell的高度隨著label的高度變化而變化,而無論label有沒有內容,label的高度應該大于等于音頻button的高度,保證當是音頻評論label沒有內容的時候,cell的高度同樣等于音頻button + 10的高度,label的行數設置為0,保證label可以自動換行顯示全部文字。音頻button與label左邊與上邊對齊。來看一下label的約束。


label的約束

同時在代碼中需要設置cell的高度自動計算,并且給cell一個大致的估算高度

    // 設置cell行高自動計算 自動計算尺寸
    self.commentTableView.rowHeight = UITableViewAutomaticDimension;
    // 需要先給一個大約的估算高度
    self.commentTableView.estimatedRowHeight = 44;

cell的內容顯示
cell的內容顯示就非常簡單了,無非需要對評論的內容進行判斷,如果是文字內容則隱藏音頻button,如果是音頻則表示肯定沒有文字,設置button的title即可。

另外因為評論分為最熱評論和最新評論,分為幾種情況,最熱評論和最新評論都有,有最新評論但是沒有最熱評論,和沒有評論。設置heardtitle,返回行數,和賦值的時候進行一些判斷即可。

// 如果是第0組,并且最熱評論有值則返回最評論行數
if (section == 0 && self.hotestComments.count) {
    return self.hotestComments.count;
}
// 否則都返回最新評論行數
return self.latestComments.count;

評論內容刷新注意點
除了進行請求之前要取消之前的請求之外,評論界面的上拉刷新和下拉加載還有一些需要注意的地方

  1. 當沒有評論的時候服務器返回給我們的是一個空的數組,所以此時需要對返回數據類型進行判斷,如果是數組說明沒有評論,則直接結束刷新,返回即可。
// 如果沒有評論的話 服務器返回的是一個數組
if (![responseObject isKindOfClass:[NSDictionary class]]) {
    [self.commentTableView.mj_header endRefreshing];
    return ;
}
  1. 如果評論小于10條,一次就可以全部請求下來,此時已經不需要上拉加載更多評論了,所以除了關閉下拉刷新,還要判斷評論數組的count如果等于評論總數,則隱藏上拉加載更多
int total = [responseObject[@"total"]intValue];
if (weakSelf.latestComments.count == total) {// 說明加載完全了,隱藏上拉刷新
    // 沒有更多數據,隱藏上拉加載更多
    weakSelf.commentTableView.mj_footer.hidden = YES;
}
  1. 上拉加載更多同樣需要判斷,如果已經加載全部評論則隱藏上拉加載更多,如果沒有加載全部,則僅僅結束本次上拉加載即可
int total = [responseObject[@"total"]intValue];
        if (weakSelf.latestComments.count == total) {// 說明加載完全了,隱藏上拉刷新
            weakSelf.commentTableView.mj_footer.hidden = YES;
        }else{
            // 結束刷新
            [weakSelf.commentTableView.mj_footer endRefreshing];
        }
  1. 當沒有數據的時候MJRefresh提供了自動判斷的方法
/** 自動根據有無數據來顯示和隱藏(有數據就顯示,沒有數據隱藏。默認是NO) */
self.commentTableView.mj_footer.automaticallyHidden = YES;

tableView的heardView的顯示
評論界面的heardView和精華頁面的cell內容一致,我們可以直接通過cell的loadNibNamed方法來直接加載xib中的cell,但是內容還是需要自己設置。

// viewFromNib 是在分類中對loadNibNamed方法進行的封裝
CLTopicCell *cell = [CLTopicCell viewFromNib];
cell.topic = self.topic;
cell.cl_height = self.topic.cellHeight + 20;

// 設置heardView
self.commentTableView.tableHeaderView = cell;

需要注意的一點是,因為我們在之前設置cell之間的間距的時候重寫過cell的setFrame方法,在setFrame中將cell的高度減少了10,所以每次設置cell的frame都會來到這個方法,將cell的高度減少10,評論界面顯示的時候來到一次setFrame方法,設置cell高度的時候又來到一次,一共來到兩次setFrame方法,cell的高度被減少了20,所以設置cell高度的時候需要加上20。

另外因為這里setFrame方法中只對cell的高度做了修改,所以稍作修改就可以完整的顯示cell,但是如果在setFrame中對cell的位置和寬高同時做了修改,就會產生難以捉摸的錯誤,所以如果需要在setFrame中對cell的位置和寬高同時做修改時,建議使用一個UIView當做載體,heardView上添加UIView,UIView上在添加cell,此時cell的setFrame不會對UIView產生任何影響。

消除評論界面heardView中的最熱評論
如果是有最熱評論的cell,加載到評論界面時需要將最熱評論去掉,這里將CLTopic模型的top_cmt最熱評論屬性置為空,然后在給cell的topic賦值
但是這里存在兩個問題

  1. 此時最熱評論雖然沒有了,但是那部分會被空出來,這是因為我們之前對cell的高度進行了緩存,當設置cell高度時,發現cellHeight不為零,則直接返回高度,不會重新計算。因此我們這里將cellHeight設置為0,當設置cell的cellHeight時就會重新計算cellHeight。

  2. 此時我們返回精華界面,將cell滑出界面在滑回來,這時發現cell內的熱門評論也沒有了,這是因為我們之前將CLTopic模型的top_cmt最熱評論屬性置為空了,并且緩存了cell的高度,因此這里需要將top_cmt最熱評論屬性記錄保存起來,在評論控制器將要被銷毀的時候,也就是返回精華界面的時候,重新將top_cmt最熱評論屬性賦值回去,并將cellHeight高度重新設置為0,使其重新計算高度。

這里貼出設置heardView和dealloc方法

@property(nonatomic,strong)CLComment *saveTopCom;

-(void)setupTableHeard
{
    // 如果有最熱評論,則設為空
    // 當控制器銷毀的時候,需要將值重新設置回來,并且將cellheight設置為0 讓其在重新計算一次。所以先將他保存起來
    self.saveTopCom = self.topic.top_cmt;
    self.topic.top_cmt = nil;
    self.topic.cellHeight = 0;

    // 從xib加載cell
    CLTopicCell *cell = [CLTopicCell viewFromNib];
    cell.topic = self.topic;
    cell.cl_height = self.topic.cellHeight + 20;

// 如果使用UIView當中間的載體,需要設置cell的frame。
//    cell.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, self.topic.cellHeight);
//    // 創建heardView
//    UIView *heardView =  [[UIView alloc]init];
//    [heardView addSubview:cell];
//    heardView.cl_height = self.topic.cellHeight;
//    heardView.backgroundColor = CLCommonColor(206);

    // 設置heardView
    self.commentTableView.tableHeaderView = cell;    
}
 - (void)dealloc
{
//     控制器銷毀的時候 將值重新設置回去,并將cellHeight設置為0,讓其重新計算高度
    self.topic.top_cmt = self.saveTopCom;
    self.topic.cellHeight = 0;
}

新帖模塊的完成

新帖模塊頁面和精華完全一樣,只是請求的數據不同,只需要讓新帖的控制器繼承自精華控制器,請求數據的時候對控制器類型進行判斷,根據不同的控制器設置不同的請求參數即可。

- (NSString *)aParam
{
    if (self.parentViewController.class == [CLNewViewController class]) {
        return @"newlist";
    }
    return @"list";
}

通過一張圖來看一下精華模塊和新帖模塊的結構

精華模塊和新帖模塊的結構

中間加號彈出界面完成

點擊中間加號,會彈出發表頁面。


發表頁面

考慮到發表頁面內部按鈕點擊事件較為復雜,發表頁面使用控制器,點擊加號按鈕moda出發表頁面控制器,至于發表頁面內容的布局和賦值不在贅述,6個button有一個飛出動畫,逐個從底部飛出到頁面上,其實現原理為:
布局button時,先將button放在現在的位置上,然后設置button的transform下移一個屏幕的高度

btn.transform = CGAffineTransformMakeTranslation(0, self.view.bounds.size.height);

然后當控制器view顯示完成的時候,設置每隔0.1s執行一次動畫,將一個button的transform恢復

self.time = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(upData) userInfo:nil repeats:YES];

恢復button的transform

btn.transform = CGAffineTransformIdentity;

當六個button全部恢復完成的時候將self.time取消

[self.time invalidate];

點擊狀態欄返回tableView頂部實現

當點擊狀態欄的時候,tableView會自動滾動到最上方,其實scrollView有scrollsToTop這個屬性,并且默認就是YES,但是有個局限性,只有在有一個屏幕滾動視圖的時候才會生效,當scrollView中有一個以上的滾動視圖時,將會失效。
而且只能設置狀態欄的狀態,卻沒有辦法拿到狀態欄做一些事情,使用控件遮擋狀態欄也會被狀態欄覆蓋。

那么如果想要遮住狀態欄,需要創建一個優先級大于statusBar的透明的Window用來遮擋狀態欄,并監聽點擊事件。
需要注意一點:iOS9之后,要求如果window在程序啟動完之后就顯示則必須有一個根控制器。因此需要設置將window延遲創建即可。
實現思路為:短暫延遲創建狀態欄大小的window,并設置window的層級大于StatusBar的層級,為window添加點擊事件,然后拿到keywindow的所有子控件找到scrollView,判斷scrollView有沒有顯示在keywindow上,如果顯示了則修改scrollView的offset.y等于頂端的偏移量即-contentInset.top即可。

window的層級分為三種,層級高的顯示在最外面,當層級相同時,越靠后調用的顯示在外面。

UIWindowLevelNormal; //默認,值為0
UIWindowLevelAlert; //值為2000 
UIWindowLevelStatusBar ; // 值為1000

判斷scrollView有沒有顯示在keywindow上,實質上是判斷scrollView和keywindow有沒有重疊的地方,而判斷他們有沒有重疊的前提是他們在同一個坐標系中,即在同一個父控件中。
UIView提供了轉換坐標系和判斷兩個空間是否有重疊的方法,

//    讓rect這個矩形框, 從view2坐標系轉換到view1坐標系, 得出一個新的矩形框newRect
CGRect newRect = [view1 convertRect:rect fromView:view2];
//    讓rect這個矩形框, 從view1坐標系轉換到view2坐標系, 得出一個新的矩形框newRect
CGRect newRect = [view1 convertRect:rect toView:view2];
是否包含
CGRectContainsRect(CGRect1,CGrect2)
是否交叉
CGRectIntersectsRect(CGrect1,CGRect2)

這里將判斷兩個空間知否交叉的判斷方法添加到UIView的分類中,自定義window,在application中延遲添加顯示。

判斷控件是否交叉方法

-(BOOL)intersectWithView:(UIView *)view
{
    // 這里使用keywindow是為了防止兩個控件在兩個不同的window中,這種情況一般不會出現,toView:nil 默認就是控件所在的window。
    UIWindow *window = [UIApplication sharedApplication].keyWindow;
    CGRect newRect = [self convertRect:self.bounds toView:window];
    CGRect newView = [view convertRect:view.bounds toView:window];    
    return CGRectIntersectsRect(newRect, newView);
}

window的創建與添加點擊事件

#import "CLTopWindow.h"
@implementation CLTopWindow
static UIWindow *window_;
+(void)show
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{    
        window_ = [[UIWindow alloc]init];
        window_.frame = [UIApplication sharedApplication].statusBarFrame;
        window_.backgroundColor = [UIColor clearColor];
        window_.windowLevel = UIWindowLevelAlert;
        window_.hidden = NO;
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(topWindowClick)];
        [window_ addGestureRecognizer:tap]; 
    });   
}
+(void)topWindowClick
{
    UIWindow *keiwindow = [UIApplication sharedApplication].keyWindow;
    [self findscrollViewsInView:keiwindow];
}
+(void)findscrollViewsInView:(UIView *)view
{
    for (UIView *subview in view.subviews) {
        [self findscrollViewsInView:subview];
    }
    if (![view isKindOfClass:[UIScrollView class]]) return;
    if(![view intersectWithView:[UIApplication sharedApplication].keyWindow])return;
    UIScrollView *scrollView = (UIScrollView *)view;
    // 修改offset
    CLLog(@"%@",scrollView);    
    CGPoint offset = scrollView.contentOffset;
    offset.y = - scrollView.contentInset.top;
    [scrollView setContentOffset:offset animated:YES];
    // 這是使scrollView顯示出某個區域
    //    [scrollView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
}
@end

重復點擊tabbarbutton和titleView中button后刷新數據實現

重復點擊tabbarButton或者titleView中的button之后刷新數據,首先需要記錄下來上次的點擊按鈕,與本次點擊比較,如果發現是重復點擊則通知界面刷新。
所以需要監聽按鈕的點擊,并發送通知,為了避免其他界面同時刷新,需要判斷控制器的view在不在window上和view跟window有沒有重疊,兩者缺一不可,判斷控制器的view在不在window上排除的是tabbar上的其他控制器view,判斷view跟window有沒有重疊排除的是精華模塊中其他子控制器。

監聽按鈕的點擊,分別可以在application中使用UITabBarControllerDelegate的代理方法監聽tabbarbutton的點擊,titlebutton的點擊在button點擊事件中。分別進行判斷并添加通知。

播放視頻和音樂

視頻的播放項目中暫時使用了MPMoviePlayerViewController,跳轉控制器進行播放,和音樂的播放,查看百思不得姐原項目,發現視頻和音頻都是在本界面播放的,自己嘗試了一下使用AVPlaylayer基本可以實現在本界面播放,但是還是存在很多問題,很多細節例如暫停播放,進度條等都沒有實現,并且覺得自己的實現并不正確,所以這里就不放上來了。

如果有朋友做過視頻,音頻播放這方面的實現,有時間并且愿意的話請多多指教

項目總體結構圖

項目總體結構圖

最后成果。

最后成果

至此,項目已經基本完成,內容非常有限,其中涉及到登陸的一些模塊無法獲得授權沒有完成,發布內容頁面,添加關注頁面,視頻音頻的播放等也不夠完善,其中也有許多欠缺的地方,一些細節處理不夠好,以后在慢慢完善。

昨天晚上rm-rf之后蒙掉了,還好有最近的代碼備份,今天又整理了一下。
代碼已經上傳到github,源碼下載

最后總結:如果不去做,就永遠不知道自己什么時候能準備好。


文中如果有不對的地方歡迎指出。我是xx_cc,一只長大很久但還沒有二夠的家伙。

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

推薦閱讀更多精彩內容