View編程指南2—Views

系統化學習,知其然,知其所以然

一、創建和配置View對象(Creating and Configuring View Objects)

有兩種方式可以創建View對象:編程方式Interface Builder

1.1 創建

方式1:Interface Builder

創建VIew最簡單的方式是使用 Interface Builder,可以達到所見即所得效果。您在設計時看到的是運行時獲得的內容。將活動對象保存在一個nib文件中,這是一個資源文件,用于保存對象的狀態和配置。

在視圖控制器中使用nib文件時,只需使用nib文件信息初始化視圖控制器即可。視圖控制器在適當的時候處理視圖的加載和卸載。但是,如果您的nib文件未與視圖控制器相關聯,則可以使用NSBundle或UINib對象手動加載nib文件內容,該對象使用nib文件中的數據重新構建視圖對象。

方式2:Programmatically

如果以編程方式創建視圖,則可以使用標準 allocation/initialization 模式來執行此操作。 視圖的默認初始化方法是 initWithFrame:方法,該方法設置視圖相對于(即將建立的)父視圖的初始大小和位置。 例如,要創建一個新的泛型UIView對象,可以使用類似于以下的代碼:

CGRect viewRect = CGRectMake(0,0,100,100);
UIView * myView = [[UIView alloc] initWithFrame:viewRect];
注意:雖然所有的視圖都支持initWithFrame:方法,但是每個View也可能有一個建議首選的初始化方法。 有關任何自定義初始化方法的信息,請參閱該類的參考文檔。

1.2 設置屬性 Setting the Properties of a View

UIView有幾個聲明的屬性來控制視圖的外觀和行為。 這些屬性用于操縱視圖的大小和位置,視圖的透明度,背景顏色和渲染行為。 所有這些屬性都具有適當的默認值,您可以根據需要稍后進行更改。 可以通過 Programmatically 和 Interface Builder 兩張途徑修改。詳情請查看API文檔。

1.3 標記視圖 Tagging Views for Future Identification

UIView包含一個tag屬性,可以使用一個整數值來唯一標記一個View,并在運行時執行對這些視圖的搜索。 (基于標記的搜索比自己迭代視圖層次更快。)tag屬性的默認值為0。

使用UIView的 viewWithTag: 方法搜索視圖。 此方法執行View及其子視圖的深度優先搜索。不會向上或者橫行搜索,只向下搜索,所以從root view 開始可以得到更多內容。

二、創建和管理視圖層次結構 Creating and Managing a View Hierarchy

2.1 添加和刪除子視圖 Adding and Removing Subviews

Interface Builder 是構建視圖層次結構最方便的方式。可以用圖形方式組裝視圖,查看視圖之間的關系,并確切了解在運行時將如何顯示這些視圖。使用Interface Builder時,將結果視圖層次結構保存在一個nib文件中,在運行時加載,因為需要相應的視圖。

以編程方式創建視圖,請創建并初始化它們,然后使用以下方法將它們排列為層次結構:

//添加
- (void)addSubview:(UIView *)view; 

//插入
- (void)insertSubview:(UIView *)view 
              atIndex:(NSInteger)index;
- (void)insertSubview:(UIView *)view 
         aboveSubview:(UIView *)siblingSubview;
- (void)insertSubview:(UIView *)view 
         belowSubview:(UIView *)siblingSubview;
         
//跳轉順序
- (void)bringSubviewToFront:(UIView *)view;
- (void)sendSubviewToBack:(UIView *)view;
- (void)exchangeSubviewAtIndex:(NSInteger)index1 
            withSubviewAtIndex:(NSInteger)index2;
//移除
- (void)removeFromSuperview;

在視圖控制器的 loadView 或 viewDidLoad 方法可以添加子視圖到視圖層次結構。

  • 如果以編程方式構建視圖,則將視圖創建代碼放置在視圖控制器的 loadView 方法中。
  • 無論是以編程方式創建視圖還是從nib文件加載視圖,都可以在 viewDidLoad 方法中包含其他視圖配置代碼。

當將子視圖添加到另一個視圖時,UIKit通知更改的父視圖和子視圖。 如果實現自定義視圖,則可以通過覆蓋以下方法中的一個或者多個來截獲這些通知。

//父視圖即將變化
- (void)willMoveToSuperview:(UIView *)newSuperview;
//父視圖已經變化
- (void)didMoveToSuperview;

//調用view的窗口即將變化
- (void)willMoveToWindow:(UIWindow *)newWindow;
//調用view的窗口變化
- (void)didMoveToWindow;

//一個子視圖即將移除
- (void)willRemoveSubview:(UIView *)subview;
//一個子視圖已添加
- (void)didAddSubview:(UIView *)subview;

2.2 隱藏視圖 (Hiding Views)

隱藏視圖有兩種方式

  • 設置 hidden = YES ;
  • 設置 alpha = 0 ;

但是有以下地方需要注意

  • 隱藏視圖仍然參與自動布局,如果還需要顯示話,隱藏比移除效果更好。
  • 隱藏視圖不會自動退出 first responder ,需要手動結束;
  • hidden不是可以做動畫屬性,使用alpha 替代 ;

2.3 訪問視圖層次結構中的視圖(Locating Views in a View Hierarchy)

有兩種方式可以訪問

  • 保持指針
  • 使用tag屬性

2.4 Translating, Scaling, and Rotating Views

可以使用 UIView transform 屬性進行translate、 scale、 rotate 操作。transform 屬性包含了一個 CGAffineTransform 結構體,默認為 identity transform ,不會更改視圖的外觀。

例如

// M_PI/4.0 is one quarter of a half circle, or 45 degrees.
CGAffineTransform xform = CGAffineTransformMakeRotation(M_PI/4.0);
self.view.transform = xform;

[圖片上傳失敗...(image-db10b0-1511255073202)]

注意點

  • 仿射變換添加順序很重要,會導致不同結果
  • 仿射變換的中心點是視圖的 Center 。
  • 更多詳情

2.5 坐標轉換 (Converting Coordinates in the View Hierarchy)

  • UIView通過以下方法轉換坐標
- (CGPoint)convertPoint:(CGPoint)point 
               fromView:(UIView *)view;
               
- (CGPoint)convertPoint:(CGPoint)point 
                 toView:(UIView *)view;
                 
- (CGRect)convertRect:(CGRect)rect 
             fromView:(UIView *)view;
             

- (CGRect)convertRect:(CGRect)rect 
               toView:(UIView *)view;
  • UIWindow通過以下方法轉換坐標
- (CGPoint)convertPoint:(CGPoint)point 
             fromWindow:(UIWindow *)window;
             
- (CGRect)convertRect:(CGRect)rect 
           fromWindow:(UIWindow *)window;
           
- (CGPoint)convertPoint:(CGPoint)point 
               toWindow:(UIWindow *)window;
               
- (CGRect)convertRect:(CGRect)rect 
             toWindow:(UIWindow *)window;

三、在運行時調整視圖的大小和位置(Adjusting the Size and Position of Views at Runtime)

每當視圖的大小發生變化時,其子視圖的大小和位置都必須相應地改變。 UIView支持自動布局和手動布局。

  • 通過自動布局,您可以設置每個視圖在其父視圖調整大小時應遵循的規則,然后完全忽略調整大小的操作。
  • 通過手動布局,您可以根據需要手動調整視圖的大小和位置。

3.1 觸發布局變化條件(Being Prepared for Layout Changes)

在視圖中發生以下任何事件時,可能會發生布局更改:

  • 視圖的bounds屬性發生變化

  • 設備方向更改例如橫屏,通常會觸發rootview的bounds發生變化

  • view的layer發生更改,并且需要布局。

  • 調用視圖的setNeedsLayout或layoutIfNeeded方法來強制執行布局

  • 通過調用視圖底層對象的setNeedsLayout方法來強制執行布局

3.2 使用自動調整規則自動處理布局更改(Handling Layout Changes Automatically Using Autoresizing Rules)

當您更改視圖的大小時,通常需要更改嵌入子視圖的位置和大小,以適應其父視圖的新大小。

  1. superview 的 autoresizesSubviews 屬性確定子視圖是否調整大小。

  2. 如果此屬性設置為YES,則視圖使用每個子視圖的 autoresizingMask 屬性來確定如何調整和定位該子視圖。

  3. 對任何子視圖的大小更改會觸發嵌入式子視圖的類似布局調整。

對于視圖層次結構中的每個視圖,將該視圖的autoresizingMask屬性設置為適當的值是處理自動布局更改的重要部分。表3-2列出了可應用于給定視圖的自動調整選項,并描述了在布局操作過程中的效果。可以使用 OR 運算符組合或者相加。如果使用Interface Builder來組裝視圖,則可以使用“自動調整大小”檢查器來設置這些屬性。

//默認值,不會自動調整大小
UIViewAutoresizingNone

//父視圖的高度改變時改變高度
UIViewAutoresizingFlexibleHeight

//父視圖的寬度改變時改變寬度
UIViewAutoresizingFlexibleWidth

//視圖左邊緣和父視圖左邊緣之間的距離根據需要增長或縮小。如果不包含此常數,則視圖的左邊距離超視圖的左邊緣保持固定的距離。
UIViewAutoresizingFlexibleLeftMargin

//視圖右邊緣和父視圖右邊緣之間的距離根據需要增長或縮小。如果不包含此常數,則視圖的右邊距離超視圖的右邊緣保持固定的距離。
UIViewAutoresizingFlexibleRightMargin

//視圖下邊緣和父視圖下邊緣之間的距離根據需要增長或縮小。如果不包含此常數,則視圖的下邊距離超視圖的下邊緣保持固定的距離。
UIViewAutoresizingFlexibleBottomMargin

//視圖上邊緣和父視圖上邊緣之間的距離根據需要增長或縮小。如果不包含此常數,則視圖的上邊距離超視圖的上邊緣保持固定的距離。
UIViewAutoresizingFlexibleTopMargin

[圖片上傳失敗...(image-9dbd73-1511255073202)]

其中設置常量的地方會自動調整,否則為固定值。配置自動調整規則的最簡單方法是使用Interface Builder的“大小”檢查器中的“自動調整”控件。上圖中靈活的寬度和高度常數與“自動調整”控件圖中的寬度和大小指示器具有相同的行為。但是,指示效果是相反的。在界面構建器中,邊緣指示符的存在意味著邊距具有固定大小,并且缺少指示符意味著邊距具有靈活的大小。幸運的是,Interface Builder提供了一個動畫來展示自動修改行為對你的視圖的影響。

3.3 手動調整視圖的布局(Tweaking the Layout of Your Views Manually)

只要視圖的大小發生變化,UIKit就會應用該視圖的子視圖的自動調整行為,然后調用視圖的 layoutSubviews 方法以使其進行手動更改。 當自動調整沒有產生所需的結果時可以在自定義視圖中實現 layoutSubviews 方法。 此方法的實現可以執行以下任何操作:

  • 調整任何直接子視圖的大小和位置。

  • 添加或刪除子視圖或核心動畫層。

  • 通過調用setNeedsDisplay或setNeedsDisplayInRect:方法強制子視圖重繪。

經常手動布置子視圖的一個地方是在實現大的可滾動區域時。由于對其可滾動內容擁有單個大視圖是不切實際的,因此應用程序通常會實現一個根視圖,其中包含許多較小的視圖。每個圖塊代表可滾動內容的一部分。當滾動事件發生時,根視圖調用其setNeedsLayout方法來啟動布局更改。其layoutSubviews方法然后根據發生的滾動量重新定位平鋪視圖。當tile從視圖的可見區域滾出時,layoutSubviews方法將tile移動到傳入邊緣,替換進程中的內容。

編寫布局代碼時,請務必以下列方式測試代碼:

  • 更改視圖的方向以確保布局在所有支持的接口方向上正確。
  • 確保你的代碼正確響應狀態欄高度的變化。當打電話時,狀態欄高度會增加,當用戶結束通話時,狀態欄的大小會減小。

四、運行時修改視圖(Modifying Views at Runtime

由于應用程序從用戶接收輸入,他們調整其用戶界面以響應該輸入。 應用程序可能會通過重新排列視圖,更改其大小或位置,隱藏或顯示視圖或加載全新的視圖來修改視圖。 在iOS應用程序中,有幾種地方需要和方法可以執行這些操作:

  • 在 view controller 中:

    • 1 視圖控制器必須在顯示之前創建其視圖。它可以從一個nib文件加載視圖或以編程方式創建它們。當這些視圖不再需要時,就把它們處理掉。
    • 2 當設備改變方向時,視圖控制器可能會調整視圖的大小和位置以匹配。作為調整新方向的一部分,可能會隱藏一些視圖,并顯示其他視圖。
    • 3 當視圖控制器管理可編輯的內容時,它可能會調整其視圖層次結構。例如,它可能會添加額外的按鈕和其他控件來方便編輯其內容的各個方面,這可能還需要調整任何現有的視圖以適應現有的頁面布局。
  • 在 animation blocks 中:

    • 1 當您想要在用戶界面的不同視圖之間切換時,可以隱藏一些視圖并在動畫塊中顯示其他視圖
    • 2 實現特殊效果時,可以使用動畫塊來修改視圖的各種屬性。例如,要動畫改變視圖的大小,你可以改變它的frame的大小
  • 其他方法:

    • 1 觸摸事件或手勢發生時,您的界面可能會通過加載一組新的視圖或更改當前的視圖來響應。
    • 2 當用戶與滾動視圖交互時,大的可滾動區域可能會隱藏并顯示切片子視圖。更多詳情
    • 3 當鍵盤出現時,您可以重新定位或調整視圖的大小,使其不會位于鍵盤下方。 更多詳情

五、與圖層進行交互(Interacting with Core Animation Layers)

每個視圖對象都有一個專用的圖層,用于管理屏幕上視圖內容的顯示和動畫。 雖然您可以使用視圖對象做很多事情,但您也可以根據需要直接使用相應的圖層對象。 視圖的圖層對象存儲在視圖的layer屬性中。

5.1 更改與視圖關聯的圖層類(Changing the Layer Class Associated with a View)

View的layer在其創建完成后無法更改。原因在于

  • view在初始化之前先調用layerClass方法來獲取layer對象;
  • 然后layer.delegate = view;
  • view持有layer
  • view不能作為其他layer的delegate
  • 更改視圖的所有權或委托代理關系會導致繪圖問題和應用程序崩潰

更改View默認layer的唯一方法是創建子類,重寫該方法并返回不同的值。例如,

+ (Class)layerClass
{
    return [CATiledLayer class];
}

更多詳情

5.2 Embedding Layer Objects in a View

如果喜歡使用layer,可以使用自定義layer添加到View中構建視層級結構。自定義圖層負責渲染內容和調整大小位置,不響應事件。
例如

- (void)viewDidLoad {
    [super viewDidLoad];
 
    // Create the layer.
    CALayer* myLayer = [[CALayer alloc] init];
 
    // Set the contents of the layer to a fixed image. And set
    // the size of the layer to match the image size.
    UIImage layerContents = [[UIImage imageNamed:@"myImage"] retain];
    CGSize imageSize = layerContents.size;
 
    myLayer.bounds = CGRectMake(0, 0, imageSize.width, imageSize.height);
    myLayer = layerContents.CGImage;
 
    // Add the layer to the view.
    CALayer*    viewLayer = self.view.layer;
    [viewLayer addSublayer:myLayer];
 
    // Center the layer in the view.
    CGRect        viewBounds = backingView.bounds;
    myLayer.position = CGPointMake(CGRectGetMidX(viewBounds), CGRectGetMidY(viewBounds));
 
}

六、自定義視圖(Defining a Custom View)

6.1 重點事項 Checklist for Implementing a Custom View

  • 1 定義合適的初始化方法

    • 1 代碼 重寫 - (instancetype)initWithFrame:(CGRect)frame; 方法或者定義新的方法

    • 1 nib文件加載的視圖,重寫 - (instancetype)initWithCoder:(NSCoder *)aDecoder;方法;

  • 2 實現 - (void)dealloc; 方法來清理自定義數據。

  • 3 要處理任何自定義繪圖,請覆蓋- (void)drawRect:(CGRect)rect;方法并在那里繪制繪圖

  • 4 設置視圖的autoresizingMask屬性以定義其自動布局行為。

  • 5 如果View管理一個或多個子視圖,請執行以下操作:

    • 1 在視圖的初始化序列中創建這些子視圖。
    • 1 在創建時設置每個子視圖的autoresizingMask屬性。
    • 1 如果子視圖需要自定義布局,請覆蓋layoutSubviews方法以實現您的布局代碼。
  • 6 要處理基于觸摸的事件,請執行以下操作:

    • 1 通過使用- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer;方法添加手勢到視圖
    • 1 自己處理觸摸事件,重新以下方法
- (void)touchesBegan:(NSSet<UITouch *> *)touches 
           withEvent:(UIEvent *)event;
           
- (void)touchesMoved:(NSSet<UITouch *> *)touches 
           withEvent:(UIEvent *)event;
           
- (void)touchesEnded:(NSSet<UITouch *> *)touches 
           withEvent:(UIEvent *)event;
           
- (void)touchesCancelled:(NSSet<UITouch *> *)touches 
               withEvent:(UIEvent *)event;           
  • 7 如果打印 view 版本信息,請實現- (void)drawRect:(CGRect)rect forViewPrintFormatter:(UIViewPrintFormatter *)formatter;方法。

除了可以重新的方法外,還有很多通過API直接設置的屬性來控制view顯示效果。

6.2 初始化自定義視圖(Initializing Your Custom View)

  • 代碼 重寫 - (instancetype)initWithFrame:(CGRect)frame; 方法或者定義新的方法。
    例如
- (id)initWithFrame:(CGRect)aRect {
    self = [super initWithFrame:aRect];
    if (self) {
          // setup the initial properties of the view
          ...
       }
    return self;
}
  • nib文件加載的視圖,重寫 - (instancetype)initWithCoder:(NSCoder *)aDecoder;方法;可以通過實現- (void)awakeFromNib;添加額外的初始化工作。

6.3 實現繪圖代碼(Implementing Your Drawing Code)

一般來說,首選系統提供的標準視圖或者組合它們來呈現您的內容,那么這是首選。然后選擇自定義繪圖,對于需要進行自定義繪圖的視圖,您需要重寫 - (void)drawRect:(CGRect)rect;方法并在那里進行繪制。

在調用視圖的drawRect:方法之前,UIKit為視圖配置基本的繪圖環境。具體來說,它創建一個圖形上下文,并調整坐標系、剪輯區域以匹配視圖的坐標系和可見邊界。在調用drawRect:方法時,可以使用原生繪圖技術(如UIKit和Core Graphics)開始繪制內容。可以使用UIGraphicsGetCurrentContext函數獲取指向當前圖形上下文的指針。

  • drawRect:方法的實現應該完成一件事情:繪制你的內容。

  • 此方法不是要更新數據或執行任何與繪圖無關的任務的地方。
    它應該配置繪圖環境,繪制您的內容,并盡快退出。

  • 如果drawRect:方法可能會被頻繁地調用,那么應該盡可能地優化繪圖代碼,并在每次調用方法時盡可能少地繪制。

重要提示:當前的圖形上下文僅在對視圖的drawRect:方法進行一次調用期間才有效。 UIKit可能為這個方法的每個后續調用創建一個不同的圖形上下文,所以你不應該嘗試緩存對象并在以后使用它。

示例代碼

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect    myFrame = self.bounds;
 
    // Set the line width to 10 and inset the rectangle by
    // 5 pixels on all sides to compensate for the wider line.
    CGContextSetLineWidth(context, 10);
    CGRectInset(myFrame, 5, 5);
 
    [[UIColor redColor] set];
    UIRectFrame(myFrame);
}

性能優化的2條建議

  • view.opaque = YES;
  • view.clearsContextBeforeDrawing = NO;

6.4 事件響應(Responding to Events)

  • 1 通過添加手勢來處理事件。使用- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer;方法添加手勢到視圖
  • 2 自己處理觸摸事件,重新以下方法
- (void)touchesBegan:(NSSet<UITouch *> *)touches 
           withEvent:(UIEvent *)event;
           
- (void)touchesMoved:(NSSet<UITouch *> *)touches 
           withEvent:(UIEvent *)event;
           
- (void)touchesEnded:(NSSet<UITouch *> *)touches 
           withEvent:(UIEvent *)event;
           
- (void)touchesCancelled:(NSSet<UITouch *> *)touches 
               withEvent:(UIEvent *)event;           
  • 3 multipleTouchEnabled 屬性控制多點觸控
  • 4 響應事件開關有兩種方式
    • userInteractionEnabled 屬性控制是否響應事件
    • 通過一對方法也可以控制
//此二者方法通常在動畫開始結束時調用。動畫期間是不響應觸摸事件。

- (void)beginIgnoringInteractionEvents;

- (void)endIgnoringInteractionEvents;
  • 5 通過重寫以下方法來判斷觸摸事件是否發生在視圖內
- (BOOL)pointInside:(CGPoint)point 
          withEvent:(UIEvent *)event;
          
- (UIView *)hitTest:(CGPoint)point 
          withEvent:(UIEvent *)event;

6.5 清理(Cleaning Up After Your View)

重寫- (void)dealloc;方法來清理內容,例如

- (void)dealloc {
    // Release a retained UIColor object
    [color release];
 
    // Call the inherited implementation
    [super dealloc];
}

但是在ARC模式下幾乎不用關注這部分工作。

參考連接 Views

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

推薦閱讀更多精彩內容