iOS事件傳遞以及響應(yīng)綜合分析

  1. 響應(yīng)者對(duì)象UIResponder
  2. 事件傳遞
    • 事件傳遞過(guò)程
    • 關(guān)于hitTest:withEvent:方法解析
  3. 事件響應(yīng)者鏈條
    • 應(yīng)用舉例:
  4. 手勢(shì)的共存和互斥
    • 綜合案例
  5. 手勢(shì)和View的點(diǎn)擊事件關(guān)系

一. 響應(yīng)者對(duì)象UIResponder

在用戶使用APP的過(guò)程中,會(huì)產(chǎn)生各種各樣的事件 膳叨,iOS中的事件可以分為3大類型 :


在iOS中不是任何對(duì)象都能處理事件的洽洁,只有繼承了UIResponder的對(duì)象才能接收并處理事件,我們稱之為響應(yīng)者對(duì)象菲嘴。

那么為什么繼承自UIResponder的類就能夠接收并處理事件呢饿自?因?yàn)樵擃愔刑峁┝艘韵?個(gè)對(duì)象方法來(lái)處理觸摸事件:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

二. 事件傳遞

下面我們通過(guò)一張圖來(lái)看看iOS中事件的產(chǎn)生和傳遞過(guò)程:


  1. 當(dāng)發(fā)生觸摸事件后汰翠,系統(tǒng)會(huì)將該事件加入到一個(gè)由UIApplication管理的隊(duì)列事件中
  2. 然后UIApplication對(duì)象會(huì)從事件隊(duì)列中取出最前面的事件,并將事件分發(fā)下去以便處理昭雌,通常會(huì)先將該事件發(fā)送給應(yīng)用程序的主窗口(keyWindow)
  3. 主窗口會(huì)在視圖層次結(jié)構(gòu)中找到一個(gè)最合適的視圖來(lái)處理該觸摸事件
  4. 找到合適的視圖控件后复唤,就會(huì)調(diào)用視圖控件的touches方法來(lái)作事件的具體處理:touchesBegin... touchesMoved...touchesEnded等
  5. 這些touches方法默認(rèn)的做法是將事件順著響應(yīng)者鏈條向上傳遞,將事件交給上一個(gè)相應(yīng)者進(jìn)行處理

下面我們舉個(gè)例子來(lái)演示下具體的傳遞過(guò)程城豁,如圖:


一般事件的傳遞是從父控件傳遞到子控件的苟穆,如果父控件接受不到觸摸事件抄课,那么子控件就不可能接收到觸摸事件

例如:點(diǎn)擊了綠色的View唱星,傳遞過(guò)程如下:UIApplication->Window->白色View->綠色View
點(diǎn)擊藍(lán)色的View,傳遞過(guò)程如下:UIApplication->Window->白色View->橙色View->藍(lán)色View

關(guān)于hitTest:withEvent:方法
  1. iOS系統(tǒng)檢測(cè)到手指觸摸操作時(shí)會(huì)將其放入當(dāng)前活動(dòng)Application的事件隊(duì)列跟磨,UIApplication會(huì)從事件隊(duì)列中取出觸摸事件并傳遞給key window處理间聊,window對(duì)象首先會(huì)調(diào)用 hitTest:withEvent:方法, 而該方法內(nèi)部會(huì)調(diào)用pointInside:withEvent:方法抵拘,該方法內(nèi)部通過(guò)倒敘便利的方式也就是最先便利最后加入的子視圖哎榴,從而來(lái)判斷觸摸點(diǎn)是否在該View區(qū)域內(nèi),如果pointInside返回YES僵蛛,則表明觸摸事件發(fā)生在該View內(nèi)部尚蝌,此時(shí)系統(tǒng)會(huì)遍歷該View的所有Subview 尋找最小單位的UIView

  2. 如果當(dāng)前View.userInteractionEnabled = NOenabled=NO(UIControl) 或者alpha<=0.01充尉,hidden等情況的時(shí)候飘言,hitTest就不會(huì)調(diào)用自己的pointInside,直接返回nil驼侠,然后系統(tǒng)就會(huì)去遍歷兄弟節(jié)點(diǎn)姿鸿。
    注意:UIImageViewuserInteractionEnabled默認(rèn)就是NO,因此UIImageView以及它的子控件默認(rèn)是不能接收到觸摸事件的倒源。

  3. 如果一個(gè)子視圖的區(qū)域超過(guò)父視圖的區(qū)域,比如下圖,tabBar 中間的item


正常情況下對(duì)超出tabBar區(qū)域的觸摸操作不會(huì)被識(shí)別岗宣,因?yàn)閠abBar的pointInside:withEvent:方法會(huì)返回NO菩鲜,這樣就不會(huì)繼續(xù)向下遍歷子視圖了。當(dāng)然胳螟,我們可以重寫(xiě)pointInside:withEvent:方法來(lái)處理這種情況昔馋,下文會(huì)詳細(xì)描述。

判斷下當(dāng)前這個(gè)點(diǎn)在不在方法調(diào)用者上旺隙,注意:這個(gè)點(diǎn)必須是方法調(diào)用者上的坐標(biāo)系绒极,才會(huì)判斷準(zhǔn)確。
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event; // default returns YES if point is in bounds

下面我們用代碼來(lái)模擬下這個(gè)過(guò)程:

// 作用:尋找最合適view
// point:表示方法調(diào)用者坐標(biāo)系上的點(diǎn)
// 什么時(shí)候調(diào)用:只要一個(gè)事件傳遞給一個(gè)控件,就會(huì)調(diào)用這個(gè)控件的hitTest方法蔬捷,該方法返回誰(shuí),誰(shuí)就是最合適view
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    // 1.判斷下自己能否接收事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
    
    // 2.判斷下點(diǎn)在不在當(dāng)前控件上
    if ([self pointInside:point withEvent:event] == NO) return  nil; // 點(diǎn)不在當(dāng)前控件
    
    
    // 3.從后往前遍歷自己的子控件
    int count = (int)self.subviews.count;
    for (int i = count - 1; i >= 0; i--) {
        // 獲取子控件
        UIView *childView = self.subviews[I];
        
        // 把當(dāng)前坐標(biāo)系上的點(diǎn)轉(zhuǎn)換成子控件上的點(diǎn)
        CGPoint childP =  [self convertPoint:point toView:childView];
        
        UIView *fitView = [childView hitTest:childP withEvent:event];
        
        if (fitView) {
            return fitView;
        }
    }
    
    // 4.如果沒(méi)有比自己合適的子控件,最合適的view就是自己
    return self;
}

三. 事件響應(yīng)者鏈條

所謂的事件響應(yīng)者鏈條就是由多個(gè)響應(yīng)者對(duì)象連接起來(lái)的鏈條垄提,大致如下:

事件的完整處理過(guò)程

  1. 當(dāng)用戶點(diǎn)擊屏幕后產(chǎn)生觸摸事件榔袋,系統(tǒng)先將事件對(duì)象由上往下傳遞,也就是由父控件傳遞給子控件铡俐,直到找到最合適的控件來(lái)處理這個(gè)事件凰兑。
  2. 找到最合適的視圖控件后,調(diào)用該控件的 touches... 系列方法來(lái)作具體的事件處理
    • 如果該視圖控件中調(diào)用了 [super touches...]审丘,則將事件順著響應(yīng)者鏈條往上傳遞吏够,傳遞給上一個(gè)響應(yīng)者對(duì)象,依次類推
    • 如果該控件沒(méi)有實(shí)現(xiàn)touches... 系列方法滩报,則將事件順著響應(yīng)者鏈條往上傳遞锅知,傳遞給上一個(gè)響應(yīng)者對(duì)象,依次類推

注意:
事件的傳遞是從上到下脓钾,由父控件到子控件售睹,而事件的響應(yīng)是從下到上,是順著響應(yīng)者鏈條向上傳遞可训,由子控件到父控件的昌妹。他們是相反的。

應(yīng)用舉例:

1握截、擴(kuò)大UIButton的響應(yīng)熱區(qū)
有時(shí)候因?yàn)榭丶》裳拢覀兿霐U(kuò)大他的點(diǎn)擊響應(yīng)區(qū)域,此時(shí)我們可以:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    CGRect frame = [self getScaleFrame];
    return CGRectContainsPoint(frame, point);
}

- (CGRect)getScaleFrame {
    
    CGRect rect = self.bounds;
    if (rect.size.width < 40.f) {
        rect.origin.x -= (40-rect.size.width)/2;
    }
    
    if (rect.size.height < 40.f) {
        rect.origin.y -= (40-rect.size.height)/2;
    }
    rect.size.width = 40.f;
    rect.size.height = 40.f;
    return rect;
}

2谨胞、子view超出了父view的bounds響應(yīng)事件
項(xiàng)目中常常遇到button已經(jīng)超出了父view的范圍但仍需可點(diǎn)擊的情況固歪,比如自定義Tabbar中間的大按鈕,點(diǎn)擊超出Tabbar bounds的區(qū)域也需要響應(yīng)

//重寫(xiě)UITabBar的pointInside方法
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    // 1. 轉(zhuǎn)換點(diǎn)擊在tabbar上的坐標(biāo)點(diǎn), 到中間按鈕上
    CGPoint pointInMiddleBtn = [self convertPoint:point toView:self.middleView];

    // 2. 確定中間按鈕的圓心
    CGPoint middleBtnCenter = CGPointMake(33, 33);

    // 3. 計(jì)算點(diǎn)擊的位置距離圓心的距離
    CGFloat distance = sqrt(pow(pointInMiddleBtn.x - middleBtnCenter.x, 2) + pow(pointInMiddleBtn.y - middleBtnCenter.y, 2));

    // 4. 判定中間按鈕區(qū)域之外
    if (distance > 33 && pointInMiddleBtn.y < 18) {
        return NO;
    }

    return YES;
}

3畜眨、方形按鈕的內(nèi)切圓點(diǎn)擊
如下圖 是一個(gè)正方形的UIButton昼牛,但是此時(shí)我們只想讓它的內(nèi)切圓接收點(diǎn)擊事件,而4個(gè)角落是不接受點(diǎn)擊事件的

@implementation CustomButton

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (!self.userInteractionEnabled ||[self isHidden] ||self.alpha <= 0.01) {
        return nil;
    }
    
    if ([self pointInside:point withEvent:event]) {
        //遍歷當(dāng)前對(duì)象的子視圖
        __block UIView *hit = nil;
        [self.subviews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            // 坐標(biāo)轉(zhuǎn)換
            CGPoint vonvertPoint = [self convertPoint:point toView:obj];
            //調(diào)用子視圖的hittest方法
            hit = [obj hitTest:vonvertPoint withEvent:event];
            // 如果找到了接受事件的對(duì)象康聂,則停止遍歷
            if (hit) {
                *stop = YES;
            }
        }];
        
        if (hit) {
            return hit;
        }
        else{
            return self;
        }
    }
    else{
        return nil;
    }
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    CGFloat x1 = point.x;
    CGFloat y1 = point.y;
    
    CGFloat x2 = self.frame.size.width / 2;
    CGFloat y2 = self.frame.size.height / 2;
    
    //圓的標(biāo)準(zhǔn)方程(x-a)2+(y-b)2=r2中贰健, ab為圓心,r為半徑
    double dis = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    // 67.923
    if (dis <= self.frame.size.width / 2) {
        return YES;
    }
    else{
        return NO;
    }
}

@end

手勢(shì)代理方法

 //  是否允許同時(shí)支持多個(gè)手勢(shì)恬汁,默認(rèn)只支持一個(gè)手勢(shì)伶椿,要調(diào)用此方法注意設(shè)置代理
  - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
  {
      return YES;
  }
  
  //  是否允許開(kāi)始觸發(fā)手勢(shì)
  - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
  {
      return NO;
  }
  
  //  是否允許接收手機(jī)的觸摸(可以控制觸摸的范圍)
  - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
  {
      //獲取當(dāng)前的觸摸點(diǎn)
      CGPoint currentP = [touch locationInView:self.imageView];
      //在圖片的左半?yún)^(qū)域可以接受觸摸
      if (currentP.x < self.imageView.bounds.size.width * 0.5) {
          return YES;
      }else {
          return NO;
      }
  }

四. 手勢(shì)的共存和互斥

首先我們來(lái)看看下面這段代碼:

- (void)viewDidLoad {
    [super viewDidLoad];
    GSViewOne *viewOne = [[GSViewOne alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
    viewOne.backgroundColor = [UIColor redColor];
    [self.view addSubview:viewOne];
    
    GSViewTwo *viewTwo = [[GSViewTwo alloc] initWithFrame:CGRectMake(20, 20, 160, 160)];
    viewTwo.backgroundColor = [UIColor yellowColor];
    [viewOne addSubview:viewTwo];
    
    //添加手勢(shì)
    GSGestureOne *gestureOne = [[GSGestureOne alloc] initWithTarget:self action:@selector(panOne)];
    [viewOne addGestureRecognizer:gestureOne];
    
    GSGestureTwo *gestureTwo = [[GSGestureTwo alloc] initWithTarget:self action:@selector(panTwo)];
    [viewTwo addGestureRecognizer:gestureTwo];
}

-(void)panOne{
    NSLog(@"panOne--redView");
}
-(void)panTwo{
    NSLog(@"panTwo--yellowView");
}

效果圖如下:


手勢(shì)共存
當(dāng)我們的手指在黃色View上拖拽的時(shí)候發(fā)現(xiàn)只識(shí)別了黃色區(qū)域的手勢(shì),那么現(xiàn)在有一個(gè)需求氓侧,當(dāng)手指在黃色區(qū)域拖拽的時(shí)候我要黃色和紅色區(qū)域的手勢(shì)都識(shí)別該如何實(shí)現(xiàn)脊另?

此時(shí)我們只需要實(shí)現(xiàn)UIGestureRecognizerDelegate協(xié)議,實(shí)現(xiàn)如下方法即可:

    //允許手勢(shì)共存,只要有一個(gè)手勢(shì)返回了YES约巷,那么就是共存
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}

手勢(shì)互斥
當(dāng)我們手指在黃色區(qū)域拖拽的時(shí)候我希望紅色區(qū)域手勢(shì)識(shí)別而黃色區(qū)域手勢(shì)不識(shí)別 偎痛,此時(shí)就用到了手勢(shì)互斥。

    //gestureTwo的響應(yīng)需要gestureOne響應(yīng)失敗
    [gestureTwo requireGestureRecognizerToFail:gestureOne];

或者是用代理方法也可以:

///otherGestureRecognizer它要識(shí)別独郎,需要gestureRecognizer被響應(yīng)失敗
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {

    return YES;

}

五. 手勢(shì)和View的點(diǎn)擊事件關(guān)系

現(xiàn)在有一個(gè)案例踩麦,BaseVC中添加了一個(gè)手勢(shì)

@implementation BaseVC
- (void)viewDidLoad {
    [super viewDidLoad];
    
    UITapGestureRecognizer *tapGes = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapClick)];
    [self.view addGestureRecognizer:tapGes];
}
-(void)tapClick{
    NSLog(@"%s",__func__);
}
@end

ViewController繼承自BaseVC枚赡,在ViewController中添加了一個(gè)tableView,并且實(shí)現(xiàn)了didSelectRowAtIndexPath:方法

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    NSLog(@"%s",__func__);
}

運(yùn)行起來(lái)之后谓谦,點(diǎn)擊Cell贫橙,發(fā)現(xiàn)只執(zhí)行了基類的點(diǎn)擊手勢(shì),而沒(méi)有執(zhí)行Cell的點(diǎn)擊事件反粥,這是因?yàn)槭裁丛蚰兀?/p>

這是由于手勢(shì)是大哥卢肃,點(diǎn)擊事件是小弟,可以理解為手勢(shì)優(yōu)于點(diǎn)擊事件才顿。其實(shí)是因?yàn)槭謩?shì)有一個(gè)cancelsTouchesInView屬性莫湘,該屬性默認(rèn)值為YES,表示識(shí)別手勢(shì)之后娜膘,是否取消view的touch事件逊脯,我們只需設(shè)置該屬性為NO即可优质。

// default is YES. causes touchesCancelled:withEvent: or pressesCancelled:withEvent: to be sent to the  
//view for all touches or presses recognized as part of this gesture immediately before the action method is called.
@property(nonatomic) BOOL cancelsTouchesInView;       
tapGes.cancelsTouchesInView = NO;//識(shí)別手勢(shì)之后竣贪,是否取消view的touch事件,默認(rèn)值為YES

但是當(dāng)點(diǎn)擊Cell的時(shí)候我們只想執(zhí)行Cell的點(diǎn)擊事件而不想執(zhí)行父類的手勢(shì)事件巩螃,該如何操作呢演怎?

我們只需要實(shí)現(xiàn)手勢(shì)的代理方法即可:

// called before touchesBegan:withEvent: is called on the gesture recognizer for a new touch. 
// return NO to prevent the gesture recognizer from seeing this touch
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
    if ([touch.view isKindOfClass:[UITableView class]]) {
           return YES;
       }
       return  NO;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市避乏,隨后出現(xiàn)的幾起案子爷耀,更是在濱河造成了極大的恐慌,老刑警劉巖拍皮,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件歹叮,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡铆帽,警方通過(guò)查閱死者的電腦和手機(jī)咆耿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)爹橱,“玉大人萨螺,你說(shuō)我怎么就攤上這事±⑶” “怎么了慰技?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)组砚。 經(jīng)常有香客問(wèn)我吻商,道長(zhǎng),這世上最難降的妖魔是什么糟红? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任艾帐,我火速辦了婚禮蚯舱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘掩蛤。我一直安慰自己枉昏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布揍鸟。 她就那樣靜靜地躺著兄裂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪阳藻。 梳的紋絲不亂的頭發(fā)上晰奖,一...
    開(kāi)封第一講書(shū)人閱讀 52,246評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音腥泥,去河邊找鬼匾南。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蛔外,可吹牛的內(nèi)容都是我干的蛆楞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼夹厌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼豹爹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起矛纹,我...
    開(kāi)封第一講書(shū)人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤臂聋,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后或南,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體孩等,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年采够,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肄方。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吁恍,死狀恐怖扒秸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情冀瓦,我是刑警寧澤伴奥,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站翼闽,受9級(jí)特大地震影響拾徙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜感局,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一尼啡、第九天 我趴在偏房一處隱蔽的房頂上張望暂衡。 院中可真熱鬧,春花似錦崖瞭、人聲如沸狂巢。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)唧领。三九已至,卻和暖如春雌续,著一層夾襖步出監(jiān)牢的瞬間斩个,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工驯杜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留受啥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓鸽心,卻偏偏與公主長(zhǎng)得像滚局,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子再悼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容