Masonry使用介紹

更新布局方法

設置好約束以后,布局是如何更新的呢?

Constraints

- (void)updateConstraintsIfNeeded    // 立即重新計算約束,如果在這之前addConstraints,就可以更新約束
- (void)setNeedsUpdateConstraints   // 立即返回,標記說需要改變約束值,在當前update cycle結束后更新之前所有標記過要改變的約束,調用updateConstraints方法
Layout

- (void)layoutIfNeeded     // 立即更新布局,重新計算約束,如果在這之前addConstraints就會立即反應在頁面上
- (void)setNeedsLayout    // 同Constraints,不過是更新布局
- (void)layoutSubviews    // 布局當前頁面的子頁面
Draw

- (void)setNeedsDisplay   // 同Constraints,不過是重新渲染

Masonry簡介

Masonry是一個輕量級的布局框架,它擁有自己的描述語法(采用更優雅的鏈式語法封裝)來自動布局,具有很好可讀性且同時支持iOS和Max OS X等。
總之,對于側重寫代碼的coder,請你慢慢忘記Frame,喜歡Masonry吧。

使用前的準備

若是你對于自動布局很熟練的話,再接觸這個第三方Masonry很容易上手的,對UI界面顯示的控件的約束本質都是相同的,現在呢,我一般都是喜歡在控制器里導入 #import "Masonry.h"之前再添加兩個宏,來提高App的開發效率。

//1. 對于約束參數可以省去"mas_"

define MAS_SHORTHAND

//2. 對于默認的約束參數自動裝箱

define MAS_SHORTHAND_GLOBALS

即:需要我們導入的框架與宏如下

//define this constant if you want to use Masonry without the 'mas_' prefix

define MAS_SHORTHAND

//define this constant if you want to enable auto-boxing for default syntax

define MAS_SHORTHAND_GLOBALS

因為之前開發時都是在xib文件中添加約束,或者代碼中計算frame并沒有接觸過Masonry,現在寫篇博客來歸納總結下Masonry的使用和注意點。這篇文章只是簡單介紹Masonry,以及Masonry的使用,并且會舉一些例子出來。但并不會涉及到Masonry的內部實現

Masonry中的坑:

在使用Masonry進行約束時,有一些是需要注意的。

在使用Masonry添加約束之前,需要在addSubview之后才能使用,否則會導致崩潰。
在添加約束時初學者經常會出現一些錯誤,約束出現問題的原因一般就是兩種:約束沖突和缺少約束。對于這兩種問題,可以通過調試和log排查。
之前使用Interface Builder添加約束,如果約束有錯誤直接就可以看出來,并且會以紅色或者黃色警告體現出來。而Masonry則不會直觀的體現出來,而是以運行過程中崩潰或者打印異常log體現,所以這也是手寫代碼進行AutoLayout的一個缺點。
這個問題只能通過多敲代碼,積攢純代碼進行AutoLayout的經驗,慢慢就用起來越來越得心應手了。

Masonry基礎使用

Masonry基礎API

mas_makeConstraints() 添加約束.
mas_remakeConstraints() 移除之前的約束,重新添加新的約束.
mas_updateConstraints() 更新約束


equalTo() 參數是對象類型,一般是視圖對象或者mas_width這樣的坐標系對象

mas_equalTo() 和上面功能相同,參數可以傳遞基礎數據類型對象,可以理解為比上面的API更強大

width() 用來表示寬度,例如代表view的寬度

mas_width() 用來獲取寬度的值。和上面的區別在于,一個代表某個坐標系對象,一個用來獲取坐標系對象的值
Auto Boxing

上面例如equalTo或者width這樣的,有時候需要涉及到使用mas_前綴,這在開發中需要注意作區分。

如果在當前類引入#import "Masonry.h"之前,用下面兩種宏定義聲明一下,就不需要區分mas_前綴。

// 定義這個常量,就可以不用在開發過程中使用"mas_"前綴。

#define MAS_SHORTHAND

// 定義這個常量,就可以讓Masonry幫我們自動把基礎數據類型的數據,自動裝箱為對象類型。

#define MAS_SHORTHAND_GLOBALS

修飾語句

Masonry為了讓代碼使用和閱讀更容易理解,所以直接通過點語法就可以調用,還添加了and和with兩個方法。這兩個方法內部實際上什么都沒干,只是在內部將self直接返回,功能就是為了更加方便閱讀,對代碼執行沒有實際作用。

例如下面的例子:

make.top.and.bottom.equalTo(self.containerView).with.offset(padding);
其內部代碼實現,實際上就是直接將self返回。

(MASConstraint *)with {
return self;
}
更新約束和布局
關于更新約束布局相關的API,主要用以下四個API:

(void)updateConstraintsIfNeeded 調用此方法,如果有標記為需要重新布局的約束,則立即進行重新布局,內部會調用updateConstraints方法
(void)updateConstraints 重寫此方法,內部實現自定義布局過程
(BOOL)needsUpdateConstraints 當前是否需要重新布局,內部會判斷當前有沒有被標記的約束
(void)setNeedsUpdateConstraints 標記需要進行重新布局
關于UIView重新布局相關的API,主要用以下三個API:
(void)setNeedsLayout 標記為需要重新布局
(void)layoutIfNeeded 查看當前視圖是否被標記需要重新布局,有則在內部調用layoutSubviews方法進行重新布局
(void)layoutSubviews 重寫當前方法,在內部完成重新布局操作
Masonry示例代碼
Masonry本質上就是對系統AutoLayout進行的封裝,包括里面很多的API,都是對系統API進行了一次二次包裝。

typedef NS_OPTIONS(NSInteger, MASAttribute) {

MASAttributeLeft = 1 << NSLayoutAttributeLeft,
MASAttributeRight = 1 << NSLayoutAttributeRight,
MASAttributeTop = 1 << NSLayoutAttributeTop,
MASAttributeBottom = 1 << NSLayoutAttributeBottom,
MASAttributeLeading = 1 << NSLayoutAttributeLeading,
MASAttributeTrailing = 1 << NSLayoutAttributeTrailing,
MASAttributeWidth = 1 << NSLayoutAttributeWidth,
MASAttributeHeight = 1 << NSLayoutAttributeHeight,
MASAttributeCenterX = 1 << NSLayoutAttributeCenterX,
MASAttributeCenterY = 1 << NSLayoutAttributeCenterY,
MASAttributeBaseline = 1 << NSLayoutAttributeBaseline,
};
常用方法

設置內邊距

/**
設置yellow視圖和self.view等大,并且有10的內邊距。
注意根據UIView的坐標系,下面right和bottom進行了取反。所以不能寫成下面這樣,否則right、bottom這兩個方向會出現問題。
make.edges.equalTo(self.view).with.offset(10);

除了下面例子中的offset()方法,還有針對不同坐標系的centerOffset()、sizeOffset()、valueOffset()之類的方法。
*/
[self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).with.offset(10);
make.top.equalTo(self.view).with.offset(10);
make.right.equalTo(self.view).with.offset(-10);
make.bottom.equalTo(self.view).with.offset(-10);
}];
通過insets簡化設置內邊距的方式

// 下面的方法和上面例子等價,區別在于使用insets()方法。
[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
// 下、右不需要寫負號,insets方法中已經為我們做了取反的操作了。
make.edges.equalTo(self.view).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
}];
更新約束

// 設置greenView的center和size,這樣就可以達到簡單進行約束的目的
[self.greenView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.view);
// 這里通過mas_equalTo給size設置了基礎數據類型的參數,參數為CGSize的結構體
make.size.mas_equalTo(CGSizeMake(300, 300));
}];

// 為了更清楚的看出約束變化的效果,在顯示兩秒后更新約束。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.greenView mas_updateConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(self.view).offset(100);
make.size.mas_equalTo(CGSizeMake(100, 100));
}];
});
大于等于和小于等于某個值的約束

[self.textLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.view);
// 設置寬度小于等于200
make.width.lessThanOrEqualTo(@200);
// 設置高度大于等于10
make.height.greaterThanOrEqualTo(@(10));
}];
self.textLabel.text = @"這是測試的字符串。能看到1、2、3個步驟,第一步當然是上傳照片了,要上傳正面近照哦。上傳后,網站會自動識別你的面部,如果覺得識別的不準,你還可以手動修改一下。左邊可以看到16項修改參數,最上面是整體修改,你也可以根據自己的意愿單獨修改某項,將鼠標放到選項上面,右邊的預覽圖會顯示相應的位置。";

textLabel只需要設置一個屬性即可

self.textLabel.numberOfLines = 0;
使用基礎數據類型當做參數

/**
如果想使用基礎數據類型當做參數,Masonry為我們提供了"mas_xx"格式的宏定義。
這些宏定義會將傳入的基礎數據類型轉換為NSNumber類型,這個過程叫做封箱(Auto Boxing)。

"mas_xx"開頭的宏定義,內部都是通過MASBoxValue()函數實現的。
這樣的宏定義主要有四個,分別是mas_equalTo()、mas_offset()和大于等于、小于等于四個。
*/
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.view);
make.width.mas_equalTo(100);
make.height.mas_equalTo(100);
}];
設置約束優先級

/**
Masonry為我們提供了三個默認的方法,priorityLow()、priorityMedium()、priorityHigh(),這三個方法內部對應著不同的默認優先級。
除了這三個方法,我們也可以自己設置優先級的值,可以通過priority()方法來設置。
*/
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.view);
make.width.equalTo(self.view).priorityLow();
make.width.mas_equalTo(20).priorityHigh();
make.height.equalTo(self.view).priority(200);
make.height.mas_equalTo(100).priority(1000);
}];

Masonry也幫我們定義好了一些默認的優先級常量,分別對應著不同的數值,優先級最大數值是1000。
static const MASLayoutPriority MASLayoutPriorityRequired = UILayoutPriorityRequired;
static const MASLayoutPriority MASLayoutPriorityDefaultHigh = UILayoutPriorityDefaultHigh;
static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 500;
static const MASLayoutPriority MASLayoutPriorityDefaultLow = UILayoutPriorityDefaultLow;
static const MASLayoutPriority MASLayoutPriorityFittingSizeLevel = UILayoutPriorityFittingSizeLevel;
設置約束比例

// 設置當前約束值乘以多少,例如這個例子是redView的寬度是self.view寬度的0.2倍。
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.view);
make.height.mas_equalTo(30);
make.width.equalTo(self.view).multipliedBy(0.2);
}];
小練習

子視圖等高練習

/**
下面的例子是通過給equalTo()方法傳入一個數組,設置數組中子視圖及當前make對應的視圖之間等高。

需要注意的是,下面block中設置邊距的時候,應該用insets來設置,而不是用offset。
因為用offset設置right和bottom的邊距時,這兩個值應該是負數,所以如果通過offset來統一設置值會有問題。
*/
CGFloat padding = LXZViewPadding;
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.top.equalTo(self.view).insets(UIEdgeInsetsMake(padding, padding, 0, padding));
make.bottom.equalTo(self.blueView.mas_top).offset(-padding);
}];

[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view).insets(UIEdgeInsetsMake(0, padding, 0, padding));
make.bottom.equalTo(self.yellowView.mas_top).offset(-padding);
}];

/**
下面設置make.height的數組是關鍵,通過這個數組可以設置這三個視圖高度相等。其他例如寬度之類的,也是類似的方式。
*/
[self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.bottom.equalTo(self.view).insets(UIEdgeInsetsMake(0, padding, padding, padding));
make.height.equalTo(@[self.blueView, self.redView]);
}];
子視圖垂直居中練習

/**
要求:(這個例子是在其他人博客里看到的,然后按照要求自己寫了下面這段代碼)
兩個視圖相對于父視圖垂直居中,并且兩個視圖以及父視圖之間的邊距均為10,高度為150,兩個視圖寬度相等。
*/
CGFloat padding = 10.f;
[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.view);
make.left.equalTo(self.view).mas_offset(padding);
make.right.equalTo(self.redView.mas_left).mas_offset(-padding);
make.width.equalTo(self.redView);
make.height.mas_equalTo(150);
}];

[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.view);
make.right.equalTo(self.view).mas_offset(-padding);
make.width.equalTo(self.blueView);
make.height.mas_equalTo(150);
}];
UITableView動態Cell高度

在iOS UI開發過程中,UITableView的動態Cell高度一直都是個問題。實現這樣的需求,實現方式有很多種,只是實現起來復雜程度和性能的區別。

在不考慮性能的情況下,tableView動態Cell高度,可以采取估算高度的方式。如果通過估算高度的方式實現的話,無論是純代碼還是Interface Builder,都只需要兩行代碼就可以完成Cell自動高度適配。

實現方式:

需要設置tableView的rowHeight屬性,這里設置為自動高度,告訴系統Cell的高度是不固定的,需要系統幫我們進行計算。然后設置tableView的estimatedRowHeight屬性,設置一個估計的高度。(我這里用的代理方法,實際上都一樣)

原理:

這樣的話,在tableView被創建之后,系統會根據estimatedRowHeight屬性設置的值,為tableView設置一個估計的值。然后在Cell顯示的時候再獲取Cell的高度,并刷新tableView的contentSize。

(void)tableViewConstraints {
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
}

(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.dataList.count;
}

(MasonryTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MasonryTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:LXZTableViewCellIdentifier];
[cell reloadViewWithText:self.dataList[indexPath.row]];
return cell;
}

// 需要注意的是,這個代理方法和直接返回當前Cell高度的代理方法并不一樣。
// 這個代理方法會將當前所有Cell的高度都預估出來,而不是只計算顯示的Cell,所以這種方式對性能消耗還是很大的。
// 所以通過設置estimatedRowHeight屬性的方式,和這種代理方法的方式,最后性能消耗都是一樣的。

(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 50.f;
}

(UITableView *)tableView {
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
_tableView.delegate = self;
_tableView.dataSource = self;
// 設置tableView自動高度
_tableView.rowHeight = UITableViewAutomaticDimension;
[_tableView registerClass:[MasonryTableViewCell class] forCellReuseIdentifier:LXZTableViewCellIdentifier];
[self.view addSubview:_tableView];
}
return _tableView;
}
UIScrollView自動布局

之前聽很多人說過UIScrollView很麻煩,然而我并沒有感覺到有多麻煩(并非裝逼)。我感覺說麻煩的人可能根本就沒試過吧,只是覺得很麻煩而已。

我這里就講一下兩種進行UIScrollView自動布局的方案,并且會講一下自動布局的技巧,只要掌握技巧,布局其實很簡單。

布局小技巧:

給UIScrollView添加的約束是定義其frame,設置contentSize是定義其內部大小。UIScrollView進行addSubview操作,都是將其子視圖添加到contentView上。

所以,添加到UIScrollView上的子視圖,對UIScrollView添加的約束都是作用于contentView上的。只需要按照這樣的思路給UIScrollView設置約束,就可以掌握設置約束的技巧了。

提前設置contentSize

// 提前設置好UIScrollView的contentSize,并設置UIScrollView自身的約束
self.scrollView.contentSize = CGSizeMake(1000, 1000);
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];

// 雖然redView的get方法內部已經執行過addSubview操作,但是UIView始終以最后一次添加的父視圖為準,也就是redView始終是在最后一次添加的父視圖上。
[self.scrollView addSubview:self.redView];
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.equalTo(self.scrollView);
make.width.height.mas_equalTo(200);
}];

[self.scrollView addSubview:self.blueView];
[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.redView.mas_right);
make.top.equalTo(self.scrollView);
make.width.height.equalTo(self.redView);
}];

[self.scrollView addSubview:self.greenView];
[self.greenView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.scrollView);
make.top.equalTo(self.redView.mas_bottom);
make.width.height.equalTo(self.redView);
}];
自動contentSize

上面的例子是提前設置好UIScrollView的contentSize的內部size,然后直接向里面addSubview。但是這有個要求就是,需要提前知道contentSize的大小,不然沒法設置。

這個例子中將會展示動態改變contentSize的大小,內部視圖有多少contentSize就自動擴充到多大。

這種方式的實現,主要是依賴于創建一個containerView內容視圖,并添加到UIScrollView上作為子視圖。UIScrollView原來的子視圖都添加到containerView上,并且和這個視圖設置約束。

因為對UIScrollView進行addSubview操作的時候,本質上是往其contentView上添加。也就是containerView的父視圖是contentView,通過containerView撐起contentView視圖的大小,以此來實現動態改變contentSize。

// 在進行約束的時候,要對containerView的上下左右都添加和子視圖的約束,以便確認containerView的邊界區域。
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];

CGFloat padding = LXZViewPadding;
[self.containerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView).insets(UIEdgeInsetsMake(padding, padding, padding, padding));
}];

[self.containerView addSubview:self.greenView];
[self.greenView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.equalTo(self.containerView).offset(padding);
make.size.mas_equalTo(CGSizeMake(250, 250));
}];

[self.containerView addSubview:self.redView];
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.containerView).offset(padding);
make.left.equalTo(self.greenView.mas_right).offset(padding);
make.size.equalTo(self.greenView);
make.right.equalTo(self.containerView).offset(-padding);
}];

[self.containerView addSubview:self.yellowView];
[self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.containerView).offset(padding);
make.top.equalTo(self.greenView.mas_bottom).offset(padding);
make.size.equalTo(self.greenView);
make.bottom.equalTo(self.containerView).offset(-padding);
}];

多個(2個以上)控件的等間隔排序顯示
首先介紹2個函數
/**

  • axisType 軸線方向
  • fixedSpacing 間隔大小
  • fixedItemLength 每個控件的固定長度/寬度
  • leadSpacing 頭部間隔
  • tailSpacing 尾部間隔

*/
//1. 等間隔排列 - 多個控件間隔固定,控件長度/寬度變化

(void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing
tailSpacing:(CGFloat)tailSpacing;
//2. 等間隔排列 - 多個固定大小固定,間隔空隙變化

(void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
withFixedItemLength:(CGFloat)fixedItemLength
leadSpacing:(CGFloat)leadSpacing
tailSpacing:(CGFloat)tailSpacing;
//首先添加5個視圖
NSMutableArray *array = [NSMutableArray new];
for (int i = 0; i < 5; i ++) {
UIView *view = [UIView new];
view.backgroundColor = [UIColor greenColor];
[self addSubview:view];
[array addObject:view]; //保存添加的控件
}

//水平方向控件間隔固定等間隔
[array mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:15 leadSpacing:10 tailSpacing:10];
[array makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(50);
make.height.equalTo(70);
}];

//水平方向寬度固定等間隔
[array mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedItemLength:70 leadSpacing:10 tailSpacing:10];
[array makeConstraints:^(MASConstraintMaker *make) { //數組額你不必須都是view
make.top.equalTo(50);
make.height.equalTo(70);
}];

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

推薦閱讀更多精彩內容