iOS 點(diǎn)擊事件傳遞及響應(yīng)

一、前言

首先附上蘋果的官方文檔《Event Handling Guide for iOS》

二、目錄

  • 1.iOS中的事件介紹
  • 2.事件的產(chǎn)生和傳遞
  • 3.事件響應(yīng)
  • 4.實(shí)際項(xiàng)目中的應(yīng)用

1、iOS中的事件介紹

iOS中的事件介紹可以分為3大類型:

  • 觸屏事件(例如點(diǎn)擊按鈕固蛾、通過(guò)手勢(shì)縮放圖片肠缨、拖動(dòng)上下滾動(dòng)頁(yè)面等)
  • 傳感器事件(例如搖一搖紅包、通過(guò)旋轉(zhuǎn)設(shè)備控制賽車方向哮伟、指南針等)
  • 遠(yuǎn)程控制事件(例如耳機(jī)的線控、外接手柄妄帘、遙控器等)
    著重講解一下iOS處理觸屏事件楞黄,觸屏事件分為兩種方式:
  • 高級(jí)事件處理:利用UIKit提供的各種用戶控件或者手勢(shì)識(shí)別器來(lái)處理事件。
  • 低級(jí)事件處理:在UIView的子類中重寫觸屏回調(diào)方法抡驼,直接處理觸屏事件鬼廓。
    如果想要監(jiān)聽一個(gè)view上面的觸摸事件,之前的做法是:
    (1)自定義一個(gè)view
    (2)實(shí)現(xiàn)view的touches方法致盟,在方法內(nèi)部實(shí)現(xiàn)具體處理代碼碎税。
    通過(guò)touches方法監(jiān)聽view觸摸事件,有很明顯的幾個(gè)缺點(diǎn):
    (1)必須得自定義view馏锡。
    (2)由于是在view內(nèi)部的touches方法中監(jiān)聽觸摸事件雷蹂,因此默認(rèn)情況下,無(wú)法讓其他外界對(duì)象監(jiān)聽view的觸摸事件杯道。
    (3)不容易區(qū)分用戶的具體手勢(shì)行為匪煌。
    iOS 3.2之后,蘋果推出了手勢(shì)識(shí)別功能(Gesture Recognizer)党巾,在觸摸事件處理方法萎庭,大大簡(jiǎn)化了開發(fā)者的開發(fā)難度。
    UIKit中我們常用的是UIControl類實(shí)例的addTarget:action:forControlEvents:方法維護(hù)控制目標(biāo)行為表齿拂,除UIKit控件外擎椰,手勢(shì)識(shí)別器UIGestureRecongnizer類的實(shí)例也是處理觸屏事件的好幫手,其內(nèi)部也使用目標(biāo)行為表创肥。
    為什么手勢(shì)和單擊事件只會(huì)響應(yīng)手勢(shì)达舒?
    UIGestureRecognizer有個(gè)屬性cancelsTouchesInView,這個(gè)屬性默認(rèn)值為YES叹侄,即當(dāng)手勢(shì)識(shí)別成功后巩搏,會(huì)發(fā)送touchesCancelled消息為view來(lái)結(jié)束view的響應(yīng)。如果cancelsTouchesInView為NO趾代,那么gestureRecognizer和view都可以響應(yīng)贯底。
    UIKit內(nèi)置了6中手勢(shì)識(shí)別器:
    UITapGestureRecognizer:點(diǎn)擊(單擊、雙擊、三連擊等)手勢(shì)禽捆。
    UIPinchGestureRecognizer:縮放手勢(shì)笙什。
    UIPanGestureRecognizer:拖拽手勢(shì)。
    UISwipeGestureRecognizer:滑動(dòng)手勢(shì)胚想。
    UIRotationGestureRecognizer:旋轉(zhuǎn)手勢(shì)琐凭。
    UILongPressGestureRecognizer:長(zhǎng)按手勢(shì)。
    UIKit控件和手勢(shì)識(shí)別器屬于高級(jí)事件處理的范疇浊服,這些不再多說(shuō)统屈,以下文字都是介紹的低級(jí)事件處理過(guò)程。

UITouch

當(dāng)你用一根手指觸摸屏幕時(shí)牙躺,會(huì)創(chuàng)建一個(gè)與之關(guān)聯(lián)的UITouch對(duì)象愁憔,一個(gè)UITouch對(duì)象對(duì)應(yīng)一根手指,在事件中可以根據(jù)NSSet中UITouch對(duì)象的數(shù)量得出次數(shù)觸摸事件是單指觸摸還是雙指多指等等

觸摸產(chǎn)生時(shí)所處的窗口
@property(nonatomic,readonly,retain) UIWindow *window;
觸摸產(chǎn)生時(shí)所處的視圖
@property(nonatomic,readonly,retain) UIView   *view;
短時(shí)間內(nèi)點(diǎn)按屏幕的次數(shù)孽拷,可以根據(jù)tapCount判斷單擊吨掌、雙擊或更多的點(diǎn)擊
@property(nonatomic,readonly) NSUInteger      tapCount;
記錄了觸摸事件產(chǎn)生或變化時(shí)的時(shí)間,單位是秒
@property(nonatomic,readonly) NSTimeInterval  timestamp;
當(dāng)前觸摸事件所處的狀態(tài)
@property(nonatomic,readonly) UITouchPhase    phase;

UIEvent

每產(chǎn)生一個(gè)事件, 就對(duì)應(yīng)產(chǎn)生一個(gè)UIEvent. UIEvent記錄著該事件產(chǎn)生的時(shí)間, 事件的類型等等
UIEvent幾個(gè)重要的屬性 :

事件類型
@property(nonatomic,readonly) UIEventType     type;
@property(nonatomic,readonly) UIEventSubtype  subtype;
事件產(chǎn)生的時(shí)間
@property(nonatomic,readonly) NSTimeInterval  timestamp;

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

在iOS中不是任何對(duì)象都能處理事件脓恕,只有繼承了UIResponder的對(duì)象才能接收并處理事件思犁,我們稱為響應(yīng)者對(duì)象。
UIApplication进肯,UIViewController,UIView都繼承自UIResponder棉磨,因此他們都是響應(yīng)者對(duì)象江掩,都能夠接收并處理事件
繼承自UIResponder的類能處理事件是由于UIResponder內(nèi)部提供了以下方法

觸摸事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

傳感器事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;

遠(yuǎn)程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;

兩個(gè)UIView相關(guān)屬性:

  • multipleTouchEnabled:是否開啟多點(diǎn)觸控
  • exclusiveTouch:多個(gè)控件接受事件時(shí)的排他性

2.iOS中事件的產(chǎn)生和傳遞

1.發(fā)生觸摸事件后,系統(tǒng)會(huì)將該事件加入到一個(gè)由UIApplication管理的隊(duì)列事件中
2.UIApplication會(huì)從事件隊(duì)列中取出最前面的事件乘瓤,并將事件分發(fā)下去以便處理环形,通常會(huì)先發(fā)送事件給應(yīng)用程序的主窗口(keyWindow)
3.主窗口會(huì)在視圖層次機(jī)構(gòu)中找到一個(gè)最合適的視圖來(lái)處理觸摸事件
事件的具體傳遞過(guò)程, 如圖:


image.png

一般事件的傳遞是從父控件傳遞到子控件的
例如:
點(diǎn)擊了綠色的view衙傀,傳遞過(guò)程如下:UIApplication->window->白色view->綠色view
點(diǎn)擊了藍(lán)色的view抬吟,傳遞過(guò)程如下:UIApplication->window->白色view->橙色view->藍(lán)色view
如果父控件接收不到觸摸事件,那么子控件就不可能接收到觸摸事件

(重難點(diǎn))如何尋找最合適的view

應(yīng)用如何找到最合適的控件來(lái)處理事件统抬?有以下準(zhǔn)則
1.首先判斷主窗口(keyWindow)自己是否能接受觸摸事件
2.觸摸點(diǎn)是否在自己身上
3.從后往前遍歷子控件火本,重復(fù)前面的兩個(gè)步驟(首先查找數(shù)組中最后一個(gè)元素)
4.如果沒有符合條件的子控件,那么就認(rèn)為自己最合適處理

詳述:
1.主窗口接收到應(yīng)用程序傳遞過(guò)來(lái)的事件后聪建,首先判斷自己能否接手觸摸事件钙畔。如果能,那么在判斷觸摸點(diǎn)在不在窗口自己身上
2.如果觸摸點(diǎn)也在窗口身上金麸,那么窗口會(huì)從后往前遍歷自己的子控件(遍歷自己的子控件只是為了尋找出來(lái)最合適的view)
3.遍歷到每一個(gè)子控件后擎析,又會(huì)重復(fù)上面的兩個(gè)步驟(傳遞事件給子控件,1.判斷子控件能否接受事件挥下,2.點(diǎn)在不在子控件上)
4.如此循環(huán)遍歷子控件揍魂,直到找到最合適的view桨醋,如果沒有更合適的子控件,那么自己就成為最合適的view现斋。

注意:之所以會(huì)采取從后往前遍歷子控件的方式尋找最合適的view只是為了做一些循環(huán)優(yōu)化喜最。因?yàn)橄啾容^之下,后添加的view在上面步责,降低循環(huán)次數(shù)返顺。

UIView不能接收觸摸事件的三種情況:

  • 不接受用戶交互: userInteractionEnabled = NO;
  • 隱藏: hidden = YES;
  • 透明: alpha = 0.0 ~0.01
    尋找最合適的view過(guò)程蔓肯,如圖:


    image.png

    說(shuō)明一下控件的添加順序:白1->綠2->橙2->藍(lán)3->紅3->黃4
    這里點(diǎn)擊了橙色的那塊區(qū)域遂鹊,事件傳遞判斷過(guò)程如下:
    1.UIApplication從事件隊(duì)列中取出事件分發(fā)給UIWindow
    2.UIWindow判斷自己是否能接受觸摸事件,可以
    3.UIWindow判斷觸摸點(diǎn)是否在自己身上蔗包,是的秉扑。
    4.UIWindow從后往前便利自己的子控件,取出白1
    5.白1都滿足最上面兩個(gè)條件调限,遍歷子控件橙2
    6.橙2都滿足最上面兩個(gè)條件舟陆,遍歷子控件,先取出紅3
    7.紅3不滿足條件2耻矮,取出藍(lán)3
    8.藍(lán)3也不滿足條件2秦躯,最后最合適的控件是橙2

在事件傳遞尋找最合適的view時(shí),底層到底干了哪些事裆装?

尋找合適的view用到兩個(gè)重要方法:

  • hitTest:withEvent:
  • pointInside
hitTest: withEvent: 方法

什么時(shí)候調(diào)用踱承?

  • 只要事件一傳遞給一個(gè)控件,這個(gè)控件就會(huì)調(diào)用他自己的hitTest:withEvent: 方法尋找合適的view
作用
尋找并返回最合適的view(能夠響應(yīng)事件的那個(gè)最合適的view)
注 意:不管這個(gè)控件能不能處理事件哨免,也不管觸摸點(diǎn)在不在這個(gè)控件上茎活,
事件都會(huì)先傳遞給這個(gè)控件,隨后再調(diào)用hitTest:withEvent:方法

hitTest:withEvent:底層調(diào)用流程:


image.png
底層具體實(shí)現(xiàn)如下 :
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    // 1.判斷當(dāng)前控件能否接收事件
    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;
    // 3.從后往前遍歷自己的子控件
    NSInteger count = self.subviews.count;
    for (NSInteger i = count - 1; i >= 0; i--) {
        UIView *childView = self.subviews[I];
        // 把當(dāng)前控件上的坐標(biāo)系轉(zhuǎn)換成子控件上的坐標(biāo)系
        CGPoint childP = [self convertPoint:point toView:childView];
        UIView *fitView = [childView hitTest:childP withEvent:event];
        if (fitView) { // 尋找到最合適的view
            return fitView;
        }
    }
    // 循環(huán)結(jié)束,表示沒有比自己更合適的view
    return self;
}

事件傳遞給窗口或控件后琢唾,就調(diào)用hitTest:withEvent:方法尋找更合適的view载荔,所以是,先傳遞事件采桃,再根據(jù)事件在自己身上找更合適的view懒熙。不管子控件是不是最合適的view,系統(tǒng)默認(rèn)都要先把事件傳遞給子控件普办,經(jīng)過(guò)子控件調(diào)用自己的hitTest:withEvent:方法驗(yàn)證后才知道有沒有更合適的view煌珊,即便父控件是最合適的view了,子控件的hitTest:withEvent方法還是會(huì)調(diào)用泌豆,不然怎么知道有沒有更合適的定庵!即,如果確定最終父控件是最合適的view,那么該父控件的子控件的hitTest:withEvent:方法也是會(huì)被調(diào)用的蔬浙。
hitTest:withEvent:方法忽略隱藏(hidden=YES)的視圖,禁止用戶操作(userInteractionEnabled=YES)的視圖猪落,以及alpha級(jí)別小于0.01(alpha<0.01)的視圖。
如果一個(gè)子視圖的區(qū)域超過(guò)父視圖的bound區(qū)域(父視圖的clipsToBounds 屬性為NO,這樣超過(guò)父視圖bound區(qū)域的子視圖內(nèi)容也會(huì)顯示)畴博,那么正常情況下對(duì)子視圖在父視圖之外區(qū)域的觸摸操作不會(huì)被識(shí)別,因?yàn)楦敢晥D的pointInside:withEvent:方法會(huì)返回NO,這樣就不會(huì)繼續(xù)向下遍歷子視圖了笨忌。
** - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event **
該方法判斷觸摸點(diǎn)是否在控件身上, 是則返回YES, 否則返回NO
作用
可以使用以上兩個(gè)方法做到:
指鹿為馬(明明點(diǎn)擊的是B視圖, 卻由A視圖來(lái)響應(yīng)事件)
穿透某控件點(diǎn)擊被覆蓋的下一層控件
讓父控件frame之外的子控件響應(yīng)觸摸事件(下面實(shí)際應(yīng)用中有具體介紹)

3.時(shí)間響應(yīng)

上文介紹了事件的傳遞過(guò)程,找到合適的View之后就會(huì)調(diào)用該view的touches方法要進(jìn)行響應(yīng)處理具體的事件俱病,找不到最合適的view官疲,就不會(huì)調(diào)用touches方法進(jìn)行事件處理。
這里先介紹一下響應(yīng)者鏈條:響應(yīng)者鏈條其實(shí)就是很多響應(yīng)者對(duì)象(繼承自UIResponder的對(duì)象)一起組合起來(lái)的鏈條稱之為響應(yīng)者鏈條
一般默認(rèn)做法是控件將事件順著響應(yīng)者鏈條向上傳遞亮隙,將事件交給上一個(gè)響應(yīng)者進(jìn)行處理 (即調(diào)用super的touches方法)途凫。
那么如何判斷當(dāng)前響應(yīng)者的上一個(gè)響應(yīng)者是誰(shuí)呢?有以下兩個(gè)規(guī)則:
1.判斷當(dāng)前是否是控制器的View溢吻,如果是控制器的View维费,上一個(gè)響應(yīng)者就是控制器
2.如果不是控制器的View,上一個(gè)響應(yīng)者就是父控件
響應(yīng)過(guò)程如下圖:


image.png

touch響應(yīng):

  • 找到最合適的view會(huì)調(diào)用touches方法處理事件
  • touches默認(rèn)做法是把事件順著響應(yīng)者鏈條向上拋
//只要點(diǎn)擊控件,就會(huì)調(diào)用touchBegin,如果沒有重寫這個(gè)方法,自己處理不了觸摸事件
// 上一個(gè)響應(yīng)者可能是父控件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
// 默認(rèn)會(huì)把事件傳遞給上一個(gè)響應(yīng)者,上一個(gè)響應(yīng)者是父控件,交給父控件處理
[super touchesBegan:touches withEvent:event]; 
// 注意不是調(diào)用父控件的touches方法促王,而是調(diào)用父類的touches方法
// super是父類 superview是父控件 
}

如果控制器也不響應(yīng)touches方法犀盟,就交給UIWindow,如果UIWindow也不響應(yīng)蝇狼,交給UIApplication阅畴,如果都不響應(yīng)事件就作廢了。
事件的傳遞和響應(yīng)的區(qū)別:
事件的傳遞是從上到下(父控件到子控件)迅耘,事件的響應(yīng)是從下到上(順著響應(yīng)者鏈條向上傳遞:子控件到父控件)贱枣。

4.實(shí)際項(xiàng)目中的應(yīng)用

  • 情景1:點(diǎn)擊子控件,讓父控件響應(yīng)事件豹障;(點(diǎn)擊綠色view,紅色view響應(yīng))
    分析:可通過(guò)兩種方式實(shí)現(xiàn)
    (1)因?yàn)閔itTest:withEvent:方法 的作用就是控件接收到事件后焦匈,判斷自己是否能夠處理事件血公,判斷點(diǎn)在不在自己的坐標(biāo)上,然后返回最合適的view缓熟。所以我們可以在hitTest:withEvent:方法里面強(qiáng)制返回父控件為最合適的view累魔。
#import "GreenView2.h"

@implementation GreenView2

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    return [self superview]; // return nil; 
    // 此處返回nil也可以。返回nil就相當(dāng)于當(dāng)前的view不是最合適的view
}

@end

(2)讓誰(shuí)響應(yīng)够滑,就直接重寫誰(shuí)的touchesBegan:withEvent:方法

#import "RedView1.h"

@implementation RedView1

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
  
  NSLog(@"-- touchRed touchesBegan");
}

@end
  • 情景2:點(diǎn)擊子控件垦写,父控件和子控件都響應(yīng)事件
    分析:事件的響應(yīng)是順著響應(yīng)者鏈條向上傳遞的,即從子控件傳遞給父控件彰触,touch方法默認(rèn)不處理事件梯投,而是把事件順著響應(yīng)者鏈條傳遞給上一個(gè)響應(yīng)者,這樣我們就可以依托這個(gè)原理,讓一個(gè)事件多個(gè)控件響應(yīng)
#import "GreenView2.h"

@implementation GreenView2

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
  NSLog(@"-- touchGreen");
  [super touchesBegan:touches withEvent:event];
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末分蓖,一起剝皮案震驚了整個(gè)濱河市尔艇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌么鹤,老刑警劉巖终娃,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蒸甜,居然都是意外死亡棠耕,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門柠新,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)窍荧,“玉大人,你說(shuō)我怎么就攤上這事登颓〗淋瘢” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵框咙,是天一觀的道長(zhǎng)咕痛。 經(jīng)常有香客問(wèn)我,道長(zhǎng)喇嘱,這世上最難降的妖魔是什么茉贡? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮者铜,結(jié)果婚禮上腔丧,老公的妹妹穿的比我還像新娘。我一直安慰自己作烟,他們只是感情好愉粤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拿撩,像睡著了一般衣厘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上压恒,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天影暴,我揣著相機(jī)與錄音,去河邊找鬼探赫。 笑死型宙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伦吠。 我是一名探鬼主播妆兑,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼魂拦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了箭跳?” 一聲冷哼從身側(cè)響起晨另,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谱姓,沒想到半個(gè)月后借尿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡屉来,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年路翻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茄靠。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡茂契,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出慨绳,到底是詐尸還是另有隱情掉冶,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布脐雪,位于F島的核電站厌小,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏战秋。R本人自食惡果不足惜璧亚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脂信。 院中可真熱鬧癣蟋,春花似錦、人聲如沸狰闪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)埋泵。三九已至幔欧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間秋泄,已是汗流浹背琐馆。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工规阀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留恒序,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓谁撼,卻偏偏與公主長(zhǎng)得像歧胁,于是被迫代替她去往敵國(guó)和親滋饲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

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