iOS UITableView、自定义视图Touch事件,手势冲突

1.Hit-Test机制
2.Demo--在cell上,添加手势画图的demo

并精确手势识别区域为图中扇形区域;


企业微信截图_2ce5e5fc-2ed9-40d7-80cf-e95b639effc0.png
1.Hit-Test机制

当用户触摸(Touch)屏幕进行交互时,系统会首先找到响应者(Responder)。系统检测到触摸操作时,将Touch以UIEvent的方式加入到UIApplication队列中。UIApplication从事件队列中取出事件传递到UIWindow进行处理,UIWindow会通过hitTest:withEvent:方法寻找触碰点所在的试图,这个过程叫做Hit-Test

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView * view = [super hitTest:point withEvent:event];
    return view;
}

hit-test的传递顺序如下
UIApplication -> UIWindow -> Root View -> ··· -> subview
在根视图(rootView)上调用pointInside:withEvent:判断触摸点是否在当前视图内;如果返回NO,那么hitTest:withEvent:返回nil;
如果返回YES,那么它会向当前视图的所有子视图发送hitTest:withEvent:消息,所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕。

- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event {
    BOOL isInside = [super pointInside:point withEvent:event];
    return isInside;
}

在这过程中会判断alpha(0-0.01)、userInteractionEnabled = NO、hidden = Yes时则会忽略此次hit-test。
系统就是通过Hit-Test找到触碰到的视图进行响应

2.Demo--在cell上,添加手势画图的demo

(1)首先自定义一个继承自UITableView的自定义表格视图:WTMotionDistanceTableView重写hitTest方法-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event

#import "WTMotionDistanceTableView.h"
#import "WTMotionDistanceView.h"

@implementation WTMotionDistanceTableView

-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    
    // 1.判断当前控件能否接收事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
    
    // 2. 判断点在不在当前控件
    if ([self pointInside:point withEvent:event] == NO) return nil;
    
    // 3.从后往前遍历自己的子控件
    NSInteger count = self.subviews.count;
    
    UIView *fitView = nil;
    for (NSInteger i = count - 1; i >= 0; i--) {
        
        UIView *childView = self.subviews[i];
        // 把当前控件上的坐标系转换成子控件上的坐标系
        CGPoint childPoint = [self convertPoint:point toView:childView];
        
        fitView = [childView hitTest:childPoint withEvent:event];
        if (fitView && [fitView isKindOfClass:[WTMotionDistanceView class]]) {
            // 寻找到最合适的view
            self.scrollEnabled = NO;
            break;
        }
    }
    
    if (!fitView || ![fitView isKindOfClass:[WTMotionDistanceView class]]) {
        self.scrollEnabled = YES;
    }
    if (!fitView){
        fitView = self;
    }
    // 循环结束,表示没有比自己更合适的view
    return fitView;
}
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    return [super pointInside:point withEvent:event];
}

@end

(2)自定义手势绘图视图
.h文件

#import <UIKit/UIKit.h>

static CGFloat kMinMotionDistanceValue = 1.0;
static CGFloat kMaxMotionDistanceValue = 100.0;

static CGFloat kMaxMotionDistanceRedius = 150.0;
static CGFloat kMinMotionDistanceRedius = 37.5;  // kMaxMotionDistanceRedius/4

@protocol WTMotionDistanceViewDelegate <NSObject>
-(void)updateFLLightPirMotionDistanceValue:(CGFloat)distance;

@end

@interface WTMotionDistanceView : UIView

+(CGFloat)viewRadian;
+(CGFloat)viewHeight;
+(CGFloat)viewWidth;

@property(nonatomic, assign) CGFloat redius;
@property (nonatomic, weak) id<BCFLMotionDistanceViewDelegate> delegate;
+(CGFloat)changeDeviceMotionValueToAppDistance:(CGFloat)value;
+(CGFloat)changeAppDistanceToDeviceMotionValue:(CGFloat)distance;

@end

.m文件

#import "WTMotionDistanceView.h"
#import "TJLineModel.h"

@interface WTMotionDistanceView ()
@property (nonatomic, assign) CGPoint beginPoint;
@end

@implementation WTMotionDistanceView

-(instancetype)init {
    if (self = [super init]) {
        self.backgroundColor = [UIColor whiteColor];
    }
    return self;
}

#pragma mark public methods
+(CGFloat)viewRadian {
    return 40.0;
}
+(CGFloat)viewHeight {
    return kMaxMotionDistanceRedius;
}
+(CGFloat)viewWidth {
    return cos([self viewRadian]*M_PI/180)*kMaxMotionDistanceRedius*2;
}
+(CGFloat)changeAppDistanceToDeviceMotionValue:(CGFloat)distance {
    
    CGFloat max_redius = dp2po(kMaxMotionDistanceRedius);
    CGFloat min_redius = dp2po(kMinMotionDistanceRedius);
    
    CGFloat radio = (kMaxMotionDistanceValue-kMinMotionDistanceValue)/(max_redius-min_redius);
    
    CGFloat motionValue = (distance-kMinMotionDistanceRedius)*radio;
    motionValue += kMinMotionDistanceValue;
    motionValue = motionValue<kMinMotionDistanceValue?kMinMotionDistanceValue:motionValue;
    motionValue = motionValue>kMaxMotionDistanceValue?kMaxMotionDistanceValue:motionValue;
    
    return motionValue;
}

+(CGFloat)changeDeviceMotionValueToAppDistance:(CGFloat)value {
    
    CGFloat max_redius = dp2po(kMaxMotionDistanceRedius);
    CGFloat min_redius = dp2po(kMinMotionDistanceRedius);
    
    CGFloat radio = (max_redius-min_redius)/(kMaxMotionDistanceValue-kMinMotionDistanceValue);
    
    CGFloat redius = (value-kMinMotionDistanceValue)*radio;
    redius += kMinMotionDistanceRedius;
    redius = redius<kMinMotionDistanceRedius?kMinMotionDistanceRedius:redius;
    redius = redius>kMaxMotionDistanceRedius?kMaxMotionDistanceRedius:redius;
    
    return redius;
}

#pragma mark private
-(void)drawEndedToUpdateDistance {
    
    if (self.delegate && [self.delegate respondsToSelector:@selector(updateFLLightPirMotionDistanceValue:)]) {
        [self.delegate updateFLLightPirMotionDistanceValue:_redius];
    }
}

#pragma mark set/get
-(void)setRedius:(CGFloat)redius {
    
    if (_redius != redius) {
        _redius = redius;
        [self setNeedsDisplay];
    }
}

#pragma mark 重写 画图
// Only override drawRect: if you perform custom drawing.
- (void)drawRect:(CGRect)rect {
    
    CGPoint startPoint = CGPointMake(self.bounds.size.width/2, 0);
    CGFloat max_redius = dp2po(kMaxMotionDistanceRedius);
    CGFloat min_redius = dp2po(kMinMotionDistanceRedius);
    CGFloat new_redius = _redius;
    if (new_redius<min_redius) {
        new_redius = min_redius;
    }
    if (new_redius>max_redius) {
        new_redius = max_redius;
    }
    _redius = new_redius;
    
    //上下文
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGFloat radian = [BCFLMotionDistanceView viewRadian];
    /*画扇形*/
    //iGlobalFocusColor=2b92f9
    UIColor *defaultColor = iColor(0xeb, 0xf0, 0xf5, 1.0);
    CGContextSetFillColorWithColor(context, defaultColor.CGColor);//填充颜色
    CGContextSetStrokeColorWithColor(context, defaultColor.CGColor);
    CGContextMoveToPoint(context, startPoint.x, startPoint.y);
    CGContextAddArc(context, startPoint.x, startPoint.y, max_redius,   radian* M_PI / 180, (180-radian) * M_PI / 180, 0);
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFillStroke); //绘制路径
    
    //ebf0f5
    UIColor *focusColor = iColor(0x2b, 0x92, 0xf9, 0.7);
    CGContextSetFillColorWithColor(context, focusColor.CGColor);
    CGContextSetStrokeColorWithColor(context, focusColor.CGColor);
    //以redius为半径围绕圆心画指定角度扇形
    CGContextMoveToPoint(context, startPoint.x, startPoint.y);
    CGContextAddArc(context, startPoint.x, startPoint.y, new_redius,  radian * M_PI / 180, (180-radian) * M_PI / 180, 0);
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFillStroke); //绘制路径
}

#pragma mark 手势
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    
    // 1.判断当前控件能否接收事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
    
    // 2. 判断点在不在当前控件
    if ([self pointInside:point withEvent:event] == NO) return nil;
    
    return self;
}
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    //需要判断当前触摸点是否在扇形区域内;
    /*
       扇形区域ABC, 圆点A, 弧线BC, 直线AB, 直线AC;
       -0---------------A---------------
                        |
                        |
        B               |                C
                        |
     */
    //width,height
    //1.直线AB, A(viewWeight/2, 0), B(0, redius*sin(40*M_PI/180))
    //2.直线AC, A(viewWeight/2, 0), B(2*redius*cos(40*M_PI/180), redius*sin(40*M_PI/180))
    //3.弧线BC, 圆方程A(viewWeight/2, 0), R=redius
    //4.当前点坐标: (x, y)
    CGFloat width = [BCFLMotionDistanceView viewWidth];
    CGFloat height = [BCFLMotionDistanceView viewHeight];
    CGFloat radian = [BCFLMotionDistanceView viewRadian];
    CGFloat cur_x = point.x, cur_y = point.y;
    CGPoint pointA= CGPointMake(width/2, 0);
    CGPoint pointB = CGPointMake(0, sin(radian*M_PI/180)*height);
    CGPoint pointC = CGPointMake(width, sin(radian*M_PI/180)*height);
    
    //判断是否位于直线AB下方
    TJLineModel *lineAB = [TJLineModel newLineModelWithPoint1:pointA point2:pointB];
    BOOL inLineAB = [lineAB isAboveLineWithPoint:point];
    
    //判断是否位于直线AC下方
    TJLineModel *lineAC = [TJLineModel newLineModelWithPoint1:pointA point2:pointC];
    BOOL inLineAC = [lineAC isAboveLineWithPoint:point];
    
    //判断是否位于圆内
    BOOL inCircle = (powf(point.x-pointA.x, 2.0)+powf(point.y-pointA.y, 2.0)-powf(height, 2.0))<0;
    
    if (inLineAB && inLineAC && inCircle) {
        return YES;
    }
    
    return NO;//[super pointInside:point withEvent:event];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    UITouch *touch = [touches anyObject];
    CGPoint p = [touch locationInView:self];
    
    _beginPoint = p;
}

-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    UITouch *touch = [touches anyObject];
    CGPoint p = [touch locationInView:self];
    
    CGFloat offsetY = p.y - _beginPoint.y;
    //更新
    self.redius = _redius + offsetY;
    _beginPoint = p;
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    _beginPoint = CGPointZero;
    [self drawEndedToUpdateDistance];
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    _beginPoint = CGPointZero;
    [self drawEndedToUpdateDistance];
}
@end

(3)当中涉及的直线模型
.h文件

#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>

#define WuQiongDa 1.0/0.0

@interface TJLineModel : NSObject

//线段标示直线,线段中点
@property (nonatomic, assign) CGPoint centerPoint;

//直线斜率
@property (nonatomic, assign) CGFloat lineSlope;
//直线-斜截式,y=kx+b
@property (nonatomic, assign) CGFloat lineValueB;

//类方法
+(instancetype)newLineModelWithPoint1:(CGPoint)p1 point2:(CGPoint)p2;
//计算交叉点
-(CGPoint)computeIntersectLine1:(TJLineModel*)l1 line2:(TJLineModel*)l2;

//已知直线上两点,判断某点是否在线段上
-(BOOL)isTouchLineWithPoint:(CGPoint)point;
//已知直线方程,判断某点相对位置 上下(上==左, 下==右)
-(BOOL)isAboveLineWithPoint:(CGPoint)point;

@end

.m文件

#import "TJLineModel.h"

#define kMinTouchDXY 30/2

@interface TJLineModel ()
{
    CGPoint _point1;
    CGPoint _point2;
    CGPoint _centerPoint;
}
//已知直线上两点
@property (nonatomic, assign) CGPoint point1;
@property (nonatomic, assign) CGPoint point2;
@end


@implementation TJLineModel

+(instancetype)newLineModelWithPoint1:(CGPoint)p1 point2:(CGPoint)p2
{
    TJLineModel *newLM = [TJLineModel new];
    newLM.point1 = p1;
    newLM.point2 = p2;
    
    if (newLM.point1.x == newLM.point2.x) {
        newLM.lineValueB = 0;
    }else if (newLM.point1.y == newLM.point2.y)
    {
        newLM.lineValueB = WuQiongDa;
    }else
    {
        newLM.lineValueB = (newLM.point1.y-newLM.point1.x*(newLM.point1.y-newLM.point2.y)/(newLM.point1.x-newLM.point2.x));
    }
    
    return newLM;
}

//已知直线上两点
-(void)setPoint1:(CGPoint)point1
{
    _point1 = point1;
}
-(void)setPoint2:(CGPoint)point2
{
    _point2 = point2;
}

//两点之间中点
-(CGPoint)centerPoint
{
    _centerPoint = CGPointMake((self.point1.x+self.point2.x)/2, (self.point1.y+self.point2.y)/2);
    return _centerPoint;
}
/* 
   直线平移,斜率不变:y=kb+b
   中点:(Cx0,Cy0)-->(Cx=Cx0+dx, Cy=Cy0+dy)
   db=dy-dx*(ay-by)/(ax-bx)
 */
-(void)setCenterPoint:(CGPoint)centerPoint
{
    //原来的两点之间的中点
    CGPoint originCenter = self.centerPoint;
    //新的中点
    _centerPoint = centerPoint;
    
    
    //更新直线的参数
    /*
     分为几种情况:
     1.l1为垂直于x轴的直线
     2.l1为垂直于y轴的直线
     3.l1直线斜率均存在且不为0
     */
    //1.为垂直于x轴的直线
    if (_point1.x == _point2.x) {
        _point1 = CGPointMake(_centerPoint.x, _point1.y);
        _point2 = CGPointMake(_centerPoint.x, _point2.y);
    }
    //2.为垂直于y轴的直线
    if (_point1.y == _point2.y) {
        _point1 = CGPointMake(_point1.x, _centerPoint.y);
        _point2 = CGPointMake(_point2.x, _centerPoint.y);
    }
    //3.直线斜率均存在且不为0
    if (_point1.x!=_point2.x && _point1.y!=_point2.y) {
        //位移
        CGFloat dx = centerPoint.x-originCenter.x;
        CGFloat dy = centerPoint.y-originCenter.y;
        
        CGFloat dValueB = dy+dx*(self.point2.y-self.point1.y)/(self.point1.x-self.point2.x);
        self.lineValueB += dValueB;
    }
}

//斜率
-(CGFloat)lineSlope
{
    _lineSlope = (self.point1.y-self.point2.y)/(self.point1.x-self.point2.x);
    if (_lineSlope==0) {
        _lineSlope = 0.001;
    }
    if (_lineSlope==WuQiongDa) {
        _lineSlope = tan(M_PI/2-0.0001);
    }
    return _lineSlope;
}
//斜截式b--y=kx+b
-(CGFloat)lineValueB
{
    return _lineValueB;
}

//已知直线上两点,判断某点是否在线段上
-(BOOL)isTouchLineWithPoint:(CGPoint)point {
    
    if (_point1.x == _point2.x) {
        
        CGFloat y_max = _point1.y>_point2.y?_point1.y:_point2.y;
        CGFloat y_min = _point1.y<_point2.y?_point1.y:_point2.y;
        if (fabs(point.x-_point1.x)<kMinTouchDXY && (point.y>=y_min && point.y<=y_max)) return YES;
        
    } else if (_point1.y == _point2.y) {
        
        CGFloat x_max = _point1.x>_point2.x?_point1.x:_point2.x;
        CGFloat x_min = _point1.x<_point2.x?_point1.x:_point2.x;
        if (fabs(point.y-_point1.y)<kMinTouchDXY && (point.x>=x_min && point.x<=x_max)) return YES;
        
    } else {
        
        _lineValueB = (_point1.y-_point1.x*(_point1.y-_point2.y)/(_point1.x-_point2.x));
        CGFloat value = point.y - _lineSlope*point.x;
        if (value>=-kMinTouchDXY/2 && value<=kMinTouchDXY/2) {
            CGFloat x_max = _point1.x>_point2.x?_point1.x:_point2.x;
            CGFloat x_min = _point1.x<_point2.x?_point1.x:_point2.x;
            if (point.x>=x_min && point.x<=x_max) return YES;
        }
    }
    return NO;
}
//已知直线方程,判断某点相对位置 上下(上==左, 下==右)
-(BOOL)isAboveLineWithPoint:(CGPoint)point {
    
    if (_point1.x == _point2.x) {
        
        //垂直于x轴
        if (point.x>_point1.x) {
            return YES;
        }
        
    } else if (_point1.y == _point2.y) {
        
        //垂直于y轴
        if (point.y>_point1.y) {
            return YES;
        }
        
    } else {
        
        _lineValueB = (_point1.y-_point1.x*(_point1.y-_point2.y)/(_point1.x-_point2.x));
        CGFloat value = point.y - self.lineSlope*point.x-_lineValueB;
        if (value>0) {
            return YES;
        }
    }
    return NO;
}

//计算交叉点 已知:直线斜截式公式,求两直线交点
-(CGPoint)computeIntersectLine1:(TJLineModel*)l1 line2:(TJLineModel*)l2 {
    
    /*
       分为几种情况:
       1.(1)l1为垂直于x轴的直线
         (2)l1为垂直于y轴的直线
       2.(1)l2为垂直于x轴的直线
         (2)l3为垂直于y轴的直线
       3. l1、l2直线斜率均存在且不为0
     */
    //1.(1)l1垂直于x轴的直线
    if (l1.point1.x == l1.point2.x) {
        
        //A
        if (l2.point1.x == l2.point2.x) {
            //l2垂直于x轴的直线 - 无交点
            return CGPointMake(-1, -1);
        }
        //B
        if (l2.point1.y == l2.point2.y) {
            //l2垂直于y轴的直线
            return CGPointMake(l1.point1.x, l2.point1.y);
        }
        return CGPointMake(l1.point1.x, l2.lineSlope*l1.point1.x+l2.lineValueB);
    }
    //1.(2)l1垂直于y轴的直线
    if (l1.point1.y == l1.point2.y) {
        
        //A
        if (l2.point1.x == l2.point2.x) {
            //l2垂直于x轴的直线
            return CGPointMake(l2.point1.x, l1.point1.y);
        }
        //B
        if (l2.point1.y == l2.point2.y) {
            //l2垂直于y轴的直线 - 无交点
            return CGPointMake(-1, -1);
        }
        return CGPointMake((l1.point1.y-l2.lineValueB)/l2.lineSlope, l1.point1.y);
    }
    //2.(1)//l2垂直于x轴的直线
    if (l2.point1.x == l2.point2.x) {
        return CGPointMake(l2.point1.x, l1.lineSlope*l2.point1.x+l1.lineValueB);
    }
    //2.(2)//l2垂直于y轴的直线
    if (l2.point1.y == l2.point2.y) {
        return CGPointMake((l2.point1.y-l1.lineValueB)/l1.lineSlope, l2.point1.y);
    }
    
    //3.l1、l2直线斜率均存在且不为0
    CGFloat x = (l1.lineValueB-l2.lineValueB)/(l2.lineSlope-l1.lineSlope);
    CGFloat y = l1.lineValueB+l1.lineSlope*(l1.lineValueB-l2.lineValueB)/(l2.lineSlope-l1.lineSlope);
    return CGPointMake(x, y);
}

@end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容