斯坦福大学iOS公开课笔记(7)--绘制视图和手势


title: 斯坦福大学iOS公开课笔记(7)--绘制视图和手势
date: 2017-05-31 20:57:14
tags:


这节课主要讲了iOS中的输入(Input)和输出(Output)。

输入是指用户和应用之间进行交互的部分,这节课中主要是使用手势UIGestureRecognizer来对应用内的控件进行控制。

输出是指应用展示给用户的东西,这节课中主要使用UIView进行输出。

然后在后边通过一个绘制扑克牌的Demo来进一步展示了如何使用手势和绘制视图进行输入和输出。

视图(UIView)

视图(UIView)是iOS中至关重要的部分,所有的东西都是通过视图来绘制,按钮UIButton是一个视图,标签UILabel是一个视图。视图在屏幕上是一块矩形区域,你可以通过坐标来对他的大小范围进行设置。

视图是层层嵌套的,每个视图都有一个父视图,和一个或多个子视图。要注意视图之间的层级关系,因为这会影响到显示的问题。

UIWindow

UIWindow在iOS中并不是很重要,因为iOS中只有一个UIWindow,而在Mac上就会有多个UIWindow。

self.view

每一个控制器UIViewController都会有一个自己的UIView,我们在控制器中可以通过self.view来获取他。获取到之后我们就可以对他添加一些子视图来处理我们的应用。

我们可以通过方法将UIView添加到某个UIView中,也可以将某一个UIView移除。

- (void)addSubview:(UIView *)aView; //添加一个子视图
- (void)removeFromSuperview;        //将自身从父视图中移除

位置和大小

每一个UIView都有控制他位置和大小的坐标,设置合理的坐标才能让视图合理的显示在屏幕上。了解坐标我们就要先了解下边几个概念。

  • CGFloat

    浮点型,UIView坐标中所有的参数都是使用CGFloat浮点型进行设置的。

  • CGPoint

    一个C语言结构体,用来表示点坐标,含有两个参数x和y,使用x和y来确定一个坐标的横竖位置。

    CGPoint p = CGPointMake(10.0, 20.0);
        p.x += 20;      //向右移动20个点。
    

- CGSize

    一个C语言结构体,用来描述UIView的大小,含有两个参数width和height。

    ```
    CGSize s = CGSizeMake(100.0, 200.0);
    s.height += 100;    //变高100个点。
    ```

- CGRect

    一个C语言结构体,包含前面的`CGPoint`和`CGSize`。

    ```
    CGRect r = CGRectMake(10.0, 20.0, 100.0, 200.0);
    ```

- 原点

    iOS中的原点在整个视图的左上角,横轴为X轴,越向右值越大。竖轴为Y轴,越向下值越大。这一点与笛卡尔坐标系不同。

    <center>![](斯坦福大学iOS公开课笔记(7)-绘制视图和手势/Coordinates.png)</center>

- bounds

  这是你的坐标系中绘制区域的原点,以及高度和宽度。
  
- frame
   
  也是用来设置你的坐标系中绘制区域的位置和高度和宽度。只不过他是基于你的父视图的位置的。
  
- center

  表示你所在的父视图的坐标中的中心位置,注意!没有属性可以得到你自身的中心,你只能使用自己的bounds的宽和高除以二获取自身的中心位置。
  
- bounds和frame的区别

    bounds和frame虽然都是表示视图的位置和大小,但是他们之前还是有一些区别的,如图,当视图发生旋转之后,bounds还是((0,0) (200,250)),而frame则变为了((140,65) (320,320))。
    
    <center>![](斯坦福大学iOS公开课笔记(7)-绘制视图和手势/frameAndBounds.png)</center>
    
    
### 创建一个视图
 
 创建视图可以使用`storyboard`直接拖拽或者使用代码来`alloc init`生成一个视图
 
 

CGRect labelRect = CGRectMake(20,20,50,30);
UILabel *label = [[UILabel alloc] initWithFrame:labelRect];
label.text = @"Hello";
[self.view addSubView:label];


当我们要绘制一个自定义的视图的使用,只要重写实现UIView中的`drawRect`方法就可以了。系统会自动调用它。

********注意:不要调用****`drawRect`****方法,只是重写并实现他********如果想要重绘视图,调用`setNeedsDisplay`就可以了。

另外可以使用`UIBezierPath`来绘制你需要的图形,关于`UIBezierPath`的具体使用可以看[这里](http://zhangzr.cn/2017/04/05/iOS-UIBezierPath%E7%BB%98%E5%88%B6%E7%9A%84%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8/)。

关于自定义视图的具体使用,下面的Demo中会写的清清楚楚,所以这边就不多说了。

## UIGestureRecognizer

手势是iOS中的输入部分。添加手势只需要两个步骤

1.这一步一般由控制器完成,为自己添加一个手势识别器。
2.这一步一般由视图自己完成,处理手势完成后发生的事情。

### UIPanGestureRecognizer 拖动手势

  • (void)setPannableView:(UIView*)pannableView
    {
    _pannableView = pananbleView;
    UIPanGestureRecognizer *pangr = [UIPanGestureRecognizer alloc] initWithTarget:pannableView action: @selector(pan:)];
    [pannableView addGestureRecognnizer:panr];
    }

### UIPinchGestureReccognizer 捏合手势

@property CGFloat scale; 捏合手势距离
@property (readonly) CGFloat velocity; 每分钟变化的速度


### UIRotationGestureRecgnizer 旋转手势

@property CGFloat rotation; 弧度
@property (readonly) CGFloat velocity; 每秒变化的速度


### UISwipeGestureRecgnizer 滑动手势

@property UISwipeGestureRecognizerDirection direction 滑动方向
@property NSUInteger numberOfTouchesRequired; 几只手指来完成


### UITapGestureRecognizer 点击手势

@property NSUInteger numberOfTapsReqired;几次点击
@property NSUInteger numberOfTouchesRequired; 几只手指来完成


## 绘制纸牌 Demo

这个绘制纸牌的Demo中运用了之前所讲的绘制和手势的知识。我们会使用到文字和图片的绘制以及滑动(swip)手势和捏合(pinch)手势。

    <center>![](斯坦福大学iOS公开课笔记(7)-绘制视图和手势/showSuperCard.gif)</center>

### 创建 view

首先我们新建一个UIView,在共有方法里声明他的花色和大小以及是否正面向上的属性,并重写他们的setter方法,在他们的值发生改变的时候调用`setNeedDisplay`方法来更新样式。

@interface PlayingCardView : UIView

@property (nonatomic) NSUInteger rank;
@property (nonatomic ,copy) NSString *suit;
@property (nonatomic) BOOL faceUp;

@end


@implementation PlayingCardView

  • (void)setRank:(NSUInteger)rank
    {
    _rank = rank;
    [self setNeedsDisplay];
    }

  • (void)setSuit:(NSString *)suit
    {
    _suit = suit;
    [self setNeedsDisplay];
    }

  • (void)setFaceUp:(BOOL)faceUp
    {
    _faceUp = faceUp;
    [self setNeedsDisplay];
    }

@end


 ### 设置比例

在绘制卡片之前我们先通过几个方法和宏定义来做一些适配性的东西保证卡片在不同的大小下都可以完美的显示出来。

    ![](斯坦福大学iOS公开课笔记(7)-绘制视图和手势/card_big.png)
    
    ![](斯坦福大学iOS公开课笔记(7)-绘制视图和手势/card_small.png)

define CORNER_FONT_STANDARD_HEIGHT 180.0

define CORNER_RADIUS 12.0

  • (CGFloat)cornerScaleFactor
    {
    return self.bounds.size.height/CORNER_FONT_STANDARD_HEIGHT;
    }

  • (CGFloat)cornerRadius
    {
    return CORNER_RADIUS * [self cornerScaleFactor];
    }

  • (CGFloat)cornerOffSet
    {
    return [self cornerRadius]/3.0;
    }


### 绘制

绘制部分主要是通过重写`-(void)drawRect:(CGRect)rect`方法来实现

#### 绘制图片部分

  • (void)drawRect:(CGRect)rect
    {
    //使用UIBezierPath绘制一个圆角。

    UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:[self cornerRadius]];

    [roundedRect addClip];

    [[UIColor whiteColor] setFill];
    UIRectFill(self.bounds); //调用C

[[UIColor blackColor] setStroke];
[roundedRect stroke];


if(self.faceUp)
{
    //根据扑克牌的名字找到对应的图片
    UIImage *faceImage = [UIImage imageNamed:[NSString stringWithFormat:@"%@%@",self.suit,[self rankAsString]]];
    if(faceImage)
    {
        //将图像部分按比例缩小
        CGRect imagrRect = CGRectInset(self.bounds,
                                       self.bounds.size.width * (1.0 - self.faceCardScaleFcator),
                                       self.bounds.size.height * (1.0 - self.faceCardScaleFcator));
        //绘制图片
        [faceImage drawInRect:imagrRect];
    }
    else
    {
        [self drawPips];
    }
    
    //绘制文字
    [self drawCorners];
}
else
{
    [[UIImage imageNamed:@"cardBcak"] drawInRect:self.bounds];
}

}


#### 绘制文字部分

  • (void)drawCorners
    {
    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
    paragraphStyle.alignment = NSTextAlignmentCenter;
UIFont *cornerFont = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
cornerFont = [cornerFont fontWithSize:cornerFont.pointSize * [self  cornerScaleFactor]];

NSAttributedString *cornerText = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@\n%@",[self rankAsString],self.suit] attributes:@{NSFontAttributeName : cornerFont ,NSParagraphStyleAttributeName : paragraphStyle}];

//绘制文字
CGRect textBounds;
textBounds.origin = CGPointMake([self cornerOffSet], [self cornerOffSet]);
textBounds.size = [cornerText size];
[cornerText drawInRect:textBounds];


//旋转
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, self.bounds.size.width, self.bounds.size.height);
CGContextRotateCTM(context, M_PI);
[cornerText drawInRect:textBounds];

}


### 添加滑动手势

滑动手势是使用storyboard中脱线的方式来实现的,和添加一个`UIButton`的点击事件差不多,将添加的滑动手势拖入到控制器中并实现方法就可以了。

  • (IBAction)swipe:(id)sender {

    self.playingCardView.faceUp = !self.playingCardView.faceUp;

}


### 添加捏合手势

捏合手势是使用代码添加的,在view中添加手势的响应方法并把它放到公有文件中。

  • (void)pinch:(UIPinchGestureRecognizer *)gesture
    {
    if(gesture.state == UIGestureRecognizerStateChanged ||
    gesture.state == UIGestureRecognizerStateEnded)
    {
    self.faceCardScaleFcator *= gesture.scale;
    gesture.scale = 1.0;
    }
    }

  • (void)pinch:(UIPinchGestureRecognizer *)gesture;

然后在控制器中将它添加到view中

[self.playingCardView addGestureRecognizer:[[UIPinchGestureRecognizer alloc] initWithTarget:self.playingCardView action:@selector(pinch:)]];

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

推荐阅读更多精彩内容