iOS開發之Masonry框架-使用方法須知

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

[TOC]

常用的屬性與常量

  1. MASViewAttribute 以對應的系統類型

    MASViewAttribute NSLayoutAttribute
    view.mas_left NSLayoutAttributeLeft
    view.mas_right NSLayoutAttributeRight
    view.mas_top NSLayoutAttributeTop
    view.mas_bottom NSLayoutAttributeBottom
    view.mas_leading NSLayoutAttributeLeading
    view.mas_trailing NSLayoutAttributeTrailing
    view.mas_width NSLayoutAttributeWidth
    view.mas_height NSLayoutAttributeHeight
    view.mas_centerX NSLayoutAttributeCenterX
    view.mas_centerY NSLayoutAttributeCenterY
    view.mas_baseline NSLayoutAttributeBaseline
  1. UIView

    先來一波最為常用的使用方法,大家可以看一下大致語法,下面會細講使用

    //分別設置各個相對邊距(superview為view的父類視圖,下同)
    make.left.mas_equalTo(superView.mas_left).mas_offset(10);
    make.right.mas_equalTo(superView.mas_right).mas_offset(-10);
    make.top.mas_equalTo(superView.mas_top).mas_offset(10);
    make.bottom.mas_equalTo(superView.mas_bottom).offset(-10);
            
    //直接連接使用left大于等于某個值
    make.left.mas_greaterThanOrEqualTo(10);
        
    //設置寬和高
    make.width.mas_equalTo(60);
    make.height.mas_equalTo(60);
        
    //.設置center和寬高比
    make.center.mas_equalTo(superView);
    make.width.mas_equalTo(superView).multipliedBy(1.00/3);
    make.height.mas_equalTo(superView).multipliedBy(0.25);
        
    //.關于約束優先級,此處要注意約束沖突的問題,統一約束優先級大的生效
    make.left.mas_equalTo(100);
    make.left.mas_equalTo(view.superview.mas_left).offset(10);
    make.left.mas_equalTo(20).priority(700);
    make.left.mas_equalTo(40).priorityHigh();
    make.left.mas_equalTo(60).priorityMedium();
    make.left.mas_equalTo(80).priorityLow();
        
    //如果你想讓view的(x坐標)左邊大于等于label的左邊,以下兩個約束的寫法效果一樣
    make.left.greaterThanOrEqualTo(label);
    make.left.greaterThanOrEqualTo(label.mas_left);
    

    注:約束的鏈式寫法中,不包含其他相對的view時,默認為其superview,即make.left.mas_equalTo(100);等價于make.left.mas_equalTo(view.superview.mas_left).offset(10);make.left.mas_equalTo(view.superview).offset(10);

  2. 更加便利的約束方法

    Masonry提供了一些便利的方法供我們同時創建多個不同的約束,他們被稱為MASCompositeConstraints,如:

    edges:

    // 使一個view的top, left, bottom, right 等于view2的
    make.edges.equalTo(view2);
    
    //相對于superviewde上左下右邊距分別為5,10,15,20
    make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20))
    

    size:

    // 使得寬度和高度大于等于 titleLabel
    make.size.greaterThanOrEqualTo(titleLabel)
    
    // 相對于superview寬度大100,高度小50
    make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))
    

    center:

    //中心與button1對齊
    make.center.equalTo(button1)
    
    //水平方向中心相對向左偏移5,豎直方向中心向下偏移10
    make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))
    
    

    你可以在約束鏈里添加相應的view來增加代碼的可讀性:

    // 除了top,所有的邊界與superview對齊
    make.left.right.and.bottom.equalTo(superview);
    
    make.top.equalTo(otherView);
    
  1. NSNumber

    自動布局允許使用常量去設置寬或高,如果你想通過一個數字設置一個view的最小和最大的width,可以用equality blocks,如下:

    //width >= 200 && width <= 400
        
    make.width.greaterThanOrEqualTo(@200);
        
    make.width.lessThanOrEqualTo(@400)
    

    然而自動布局不允許對齊屬性的約束(如:left,right,centerY等)設置為常量值,你可以使用NSNumber來設置相對于父類view這些約束屬性,如:

    // creates view.left = view.superview.left + 10
    make.left.lessThanOrEqualTo(@10)
    

    如果你不想使用NSNumber來設置,也可以用如下結構來創建你的約束,如:

    make.top.mas_equalTo(42);
        
    make.height.mas_equalTo(20);
        
    make.size.mas_equalTo(CGSizeMake(50, 100));
        
    make.edges.mas_equalTo(UIEdgeInsetsMake(10, 0, 10, 0));
        
    make.left.mas_equalTo(view).mas_offset(UIEdgeInsetsMake(10, 0, 10, 0));
        
    
  1. NSArray
    用數組添加集中不同類的約束,如:

    make.height.equalTo(@[view1.mas_height, view2.mas_height]);
    
    make.height.equalTo(@[view1, view2]);
    
    make.left.equalTo(@[view1, @100, view3.right]);
    
    
  2. 常見約束的各種類型

    /**
     1.尺寸:width、height、size
     2.邊界:left、leading、right、trailing、top、bottom
     3.中心點:center、centerX、centerY
     4.邊界:edges
     5.偏移量:offset、insets、sizeOffset、centerOffset
     6.priority()約束優先級(0~1000),multipler乘因數, dividedBy除因數
     */
    

Masonry使用注意

  1. 使用mas_makeConstraints方法的元素必須 事先 添加到父元素中,例如[self.view addSubView:view];

  2. mas_equalToequalTo的區別:

      • equalTo:僅支持基本類型;
      • mas_equalTo:支持類型轉換,支持復雜類型。是對equalTo的封裝。支持CGSize CGPoint NSNumber UIEdgeinsets。

      以下實現的是相同的效果:make.width.equalTo(@100);make.width.mas_equalTo(100);

      • mas_equalTo是一個Macro,比較 值;
      • equalTo比較View。

      以下實現的是相同的效果make.bottom.mas_equalTo(ws.view.mas_bottom);make.bottom.equalTo(ws.view);

      mas_equalToequalTo多了類型轉換操作,大多數時候兩個方法是 通用的。但是

      • 對于數值元素使用mas_equalTo
      • 對于對象或多個屬性的處理,使用equalTo;(特別的多個屬性時,必須使用equalTo,例如make.left.and.right.equalTo(self.view)
  3. 去掉mas_前綴,只用equalTo,只需要把下面代碼添加到.prefix文件:

    // 只要添加了這個宏,就不用帶mas_前綴(`equalTo`就等價于`mas_equalTo`)
    #define MAS_SHORTHAND
    
    // 對于默認的約束參數自動裝箱
    #define MAS_SHORTHAND_GLOBALS
    
    // 這個頭文件,一定要放在上面兩個宏的后面
    #import "Masonry.h"
    
  4. 注意點方法withand,這兩個方法其實沒有做任何操作,方法只是返回對象本身,這個方法的作用,完全是為了可讀性。

    make.left.and.right.equalTo(self.view);make.left.right.equalTo(self.view);是完全一樣的,但是加了and方法的語法,可讀性更好。

  5. multipliedBy的使用只能是設置同一個控件的,比如這里的bottomInnerViewmake.height.mas_equalTo(bottomInnerView.mas_width).multipliedBy(3);

  6. 簡化:

    [iconView makeConstraints:^(MASConstraintMaker *make){
    make.top.equalTo(self.view).with.offset(30);
    make.left.equalTo(self.view).with.offset(30);
    make.bottom.equalTo(self.view).with.offset(-30);
    make.right.equalTo(self.view).with.offset(-30);
    }]
    

    可以簡化為
    make.top.left.bottom.and.right.equalTo(self.view).with.insets(UIEdgeInsetsMake(10,10,10,10));

    make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(30.30.30.30));

  7. 其中leadingleft trailingright在正常情況下是等價的,但是在一些布局是從右至左時(比如阿拉伯文),則會對調,所以基本可以不理不用。用leftright就好。用leadingtrailing后就不要用leftright,如果混用會出現崩潰。

  8. label的約束比必須設置最大的約束寬度;
    self.titleLabel.preferredMaxLayoutWidth = w - 100;

  9. 因為iOS中原點在左上角所以注意使用offset時注意right和bottom用負數

  10. 使用Masonry不需要設置控件的translatesAutoresizingMaskIntoConstraints屬性為NO,(錯誤觀點:為防止block中的循環引用,使用弱引用),因為在這里block是局部的引用,block內部引用self不會造成循環應用的。(沒必要的寫法:__weak typeof (self) weakSelf = self;

  11. Masonry約束控件出現沖突的問題:當約束沖突發生的時候,我們可以設置viewkey來定位是哪個view。
    比如:

    redView.mas_key = @"redView";
    greenView.mas_key = @"greenView";
    blueView.mas_key = @"blueView";
    

    如果覺得這樣一個個設置比較繁瑣,Masonry提供了批量設置的宏:

    // 一句代碼即可全部設置
    MASAttachKeysMASAttachKeys(redView,greenView,blueView);
    

約束的優先級

  1. .priority允許你指定一個精確的優先級,數值越大優先級越高,最高1000
    • priorityHigh等價于UILayoutPriorityDefaultHigh,優先級值為750
    • priorityMedium介于高優先級和低優先級之間,優先級值在250~750之間。
    • priorityLow等價于UILayoutPriorityDefaultLow,優先級值為250

優先級可以在約束的尾部添加:

make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow();
make.top.equalTo(label.mas_top).with.priority(600);

Greater/Less一般與Priority一起使用,為一個Constraint設置了Greater/Less后,調整Priority。如果ConstraintPriority的值越大,程序優先設置它的Constraint效果。

Masonry添加約束的方法

  1. 這個方法只會添加新的約束:

    [view makeConstraints:^(MASConstraintMaker *make) {
    
    }];
    
  1. 這個方法將會覆蓋以前的某些特定的約束

    [view updateConstraints:^(MASConstraintMaker *make) {
    
    }];
    
  1. 這個方法會將以前的所有約束刪掉,添加新的約束

     ```
     [view remakeConstraints:^(MASConstraintMaker *make) {
     
     }];
     ```
    
    • 里面覺得最好用的是masonry_remakeConstraints,保證不會錯,
  • 要記得將約束寫在updateConstraints里面:

    - (void) updateConstraints{
        
    }
    

    然后調用下面這一串:

    [self setNeedsUpdateConstraints];
        
    [self updateConstraintsIfNeeded];
        
    [self layoutIfNeeded];
    

    否則不會更新。

    • setNeedsLayout:

      1. setNeedsLayout:告知頁面需要刷新,但是不會立刻開始更新。執行后會立刻調用layoutSubviews
      2. layoutIfNeed:告知頁面布局立刻更新。所以一般都會和setNeedsLayout一起使用。如果希望立刻生成新的frame需要調用此方法。利用這點:一般布局動畫可以在更新布局后直接使用這個方法讓動畫生效。
      3. layoutSubViews:系統重寫布局setNeedsUpdateConstraints:告訴需要更新約束,但是不會立刻開始。
      4. updateConstraintsIfNeeded:告知立刻更新約束。
      5. updateConstraints:系統更新約束。

修改約束

有時候,你為了實現動畫或者移除替換一些約束時,你需要去修改一些已經存在的約束,Masonry提供了一些不同的方法去更新約束,你也可以將多個約束存在數組里。

  1. References

    你可以持有某個特定的約束,讓其成為成員變量或者屬性

    //設置為公共或私接口

    @property (nonatomic, strong) MASConstraint *topConstraint;
        
    ...
        
    // 添加約束
        
    [view1 mas_makeConstraints:^(MASConstraintMaker *make) {
        
    self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);
        
    make.left.equalTo(superview.mas_left).with.offset(padding.left);
        
    }];
        
    ...
        
    // 然后可以調用
    //該約束移除
    [self.topConstraint uninstall];
    //重新設置value,最常用
    self.topConstraint.mas_equalTo(20);
    //該約束失效
    [self.topConstraint deactivate];
    //該約束生效
    [self.topConstraint activate];
    
    
  2. mas_updateConstraints

    如果你只是想更新一下view對應的約束,可以使用 mas_updateConstraints 方法代替 mas_makeConstraints方法

    //這是蘋果推薦的添加或者更新約束的地方

    // 在響應setNeedsUpdateConstraints方法時,這個方法會被調用多次

    // 此方法會被UIKit內部調用,或者在你觸發約束更新時調用

    - (void)updateConstraints {
    
    [self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) {
    
    make.center.equalTo(self);
    
    make.width.equalTo(@(self.buttonSize.width)).priorityLow();
    
    make.height.equalTo(@(self.buttonSize.height)).priorityLow();
    
    make.width.lessThanOrEqualTo(self);
    
    make.height.lessThanOrEqualTo(self);
    
    }];
    
    //調用super
    [super updateConstraints];
    
    }
    
    
  3. mas_remakeConstraints

    mas_updateConstraints只是去更新一些約束,然而有些時候修改一些約束值是沒用的,這時候mas_remakeConstraints就可以派上用場了

    mas_remakeConstraints某些程度相似于mas_updateConstraints,但不同于mas_updateConstraints去更新約束值,他會移除之前的view的所有約束,然后再去添加約束

    - (void)changeButtonPosition {
    
      [self.button mas_remakeConstraints:^(MASConstraintMaker *make) {
    
      make.size.equalTo(self.buttonSize);
    
     if (topLeft) {
    
          make.top.and.left.offset(10);
    
     } else {
    
     make.bottom.and.right.offset(-10); 
    
    }
    
    }];
    
    
  • 蘋果官方建議:添加/更新約束在updateConstraints這個方法內

    // this is Apple's recommended place for adding/updating constraints
    - (void)updateConstraints {
    //更新約束
    [self.btn updateConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self);
        
        make.width.equalTo(@(self.buttonSize.width)).priorityLow();
        make.height.equalTo(@(self.buttonSize.height)).priorityLow();
        
        make.width.lessThanOrEqualTo(self);
        make.height.lessThanOrEqualTo(self);
    }];
        
    //according to apple super should be called at end of method
    //最后必須調用父類的更新約束
    [super updateConstraints];
    }
    

在哪創建我的約束

貼一個官方說明的例子:

@implementation DIYCustomView
    
- (id)init {
    
    self = [super init];
        
    if (!self) return nil;
        
    // --- Create your views here ---
        
    self.button = [[UIButton alloc] init];
        
    return self;
    
}
    
// tell UIKit that you are using AutoLayout
    
+ (BOOL)requiresConstraintBasedLayout {
    
    return YES;
    
}
    
// this is Apple's recommended place for adding/updating constraints
    
- (void)updateConstraints {
    
    // --- remake/update constraints here
        
    [self.button remakeConstraints:^(MASConstraintMaker *make) {
        
        make.width.equalTo(@(self.buttonSize.width));
            
        make.height.equalTo(@(self.buttonSize.height));
        
    }];
        
    //according to apple super should be called at end of method
        
    [super updateConstraints];
    
}
    
- (void)didTapButton:(UIButton *)button {
    
    // --- Do your changes ie change variables that affect your layout etc ---
        
    self.buttonSize = CGSize(200, 200);
        
    // tell constraints they need updating
        
    [self setNeedsUpdateConstraints];
    
}

創建約束技巧:

  1. 多個(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;
    
    
  2. 多行label的約束問題

    //創建label
        self.label = [UILabel new];
        self.label.numberOfLines = 0;
        self.label.lineBreakMode = NSLineBreakByTruncatingTail;
        self.label.text = @"有的人,沒事時喜歡在朋友圈里到處點贊,東評論一句西評論一句,比誰都有存在感。等你有事找他了,他就立刻變得很忙,讓你再也找不著。真正的朋友,平常很少聯系。可一旦你遇上了難處,他會立刻回復你的消息,第一時間站出來幫你。所謂的存在感,不是你有沒有出現,而是你的出現有沒有價值。存在感,不是刷出來的,也不是說出來的。有存在感,未必是要個性鋒芒畢露、甚至鋒利扎人。翩翩君子,溫潤如玉,真正有存在感的人,反而不會刻意去強調他的存在感。他的出現,永遠都恰到好處。我所欣賞的存在感,不是長袖善舞巧言令色,而是對他人的真心關照;不是鋒芒畢露計較勝負,而是讓人相處得舒服;不是時時刻刻聒噪不休,而是關鍵時刻能挺身而出。別總急著出風頭,希望你能有恰到好處的存在感。";
        [self addSubview: self.label];
    
        [self.label makeConstraints:^(MASConstraintMaker *make) {
            make.left.top.equalTo(10);
            make.right.equalTo(-10);
        }];
    
    //添加約束
    - (void)layoutSubviews {
        //1. 執行 [super layoutSubviews];
        [super layoutSubviews];
    
        //2. 設置preferredMaxLayoutWidth: 多行label約束的完美解決
       self.label.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;
    
        //3. 設置preferredLayoutWidth后,需要再次執行 [super layoutSubviews]; 
        //其實在實際中這步不寫,也不會出錯,官方解釋是說設置preferredLayoutWidth后需要重新計算并布局界面,所以這步最好執行
        [super layoutSubviews];
    }
    
  3. UIScrollView的問題

    原理同自動布局一樣 UIScrollView上添加UIView
    UIView上添加需要顯示的控件 UIScrollView滾動高度取決于顯示控件的總高度
    對子控件做好約束,可達到控制UIView的大小

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

推薦閱讀更多精彩內容