title: 斯坦福大學(xué)iOS公開課筆記(7)--繪制視圖和手勢
date: 2017-05-31 20:57:14
tags:
這節(jié)課主要講了iOS中的輸入(Input)和輸出(Output)糯笙。
輸入是指用戶和應(yīng)用之間進(jìn)行交互的部分仓洼,這節(jié)課中主要是使用手勢UIGestureRecognizer
來對應(yīng)用內(nèi)的控件進(jìn)行控制。
輸出是指應(yīng)用展示給用戶的東西,這節(jié)課中主要使用UIView
進(jìn)行輸出。
然后在后邊通過一個繪制撲克牌的Demo來進(jìn)一步展示了如何使用手勢和繪制視圖進(jìn)行輸入和輸出株茶。
視圖(UIView)
視圖(UIView)是iOS中至關(guān)重要的部分,所有的東西都是通過視圖來繪制飘哨,按鈕UIButton
是一個視圖,標(biāo)簽UILabel
是一個視圖。視圖在屏幕上是一塊矩形區(qū)域设江,你可以通過坐標(biāo)來對他的大小范圍進(jìn)行設(shè)置。
視圖是層層嵌套的攘轩,每個視圖都有一個父視圖叉存,和一個或多個子視圖。要注意視圖之間的層級關(guān)系度帮,因為這會影響到顯示的問題歼捏。
UIWindow
UIWindow在iOS中并不是很重要,因為iOS中只有一個UIWindow够傍,而在Mac上就會有多個UIWindow甫菠。
self.view
每一個控制器UIViewController
都會有一個自己的UIView
挠铲,我們在控制器中可以通過self.view
來獲取他冕屯。獲取到之后我們就可以對他添加一些子視圖來處理我們的應(yīng)用。
我們可以通過方法將UIView添加到某個UIView中拂苹,也可以將某一個UIView移除安聘。
- (void)addSubview:(UIView *)aView; //添加一個子視圖
- (void)removeFromSuperview; //將自身從父視圖中移除
位置和大小
每一個UIView都有控制他位置和大小的坐標(biāo),設(shè)置合理的坐標(biāo)才能讓視圖合理的顯示在屏幕上瓢棒。了解坐標(biāo)我們就要先了解下邊幾個概念浴韭。
-
CGFloat
浮點(diǎn)型,UIView坐標(biāo)中所有的參數(shù)都是使用
CGFloat
浮點(diǎn)型進(jìn)行設(shè)置的脯宿。 -
CGPoint
一個C語言結(jié)構(gòu)體念颈,用來表示點(diǎn)坐標(biāo),含有兩個參數(shù)x和y连霉,使用x和y來確定一個坐標(biāo)的橫豎位置榴芳。
CGPoint p = CGPointMake(10.0, 20.0); p.x += 20; //向右移動20個點(diǎn)。
- CGSize
一個C語言結(jié)構(gòu)體跺撼,用來描述UIView的大小窟感,含有兩個參數(shù)width和height。
```
CGSize s = CGSizeMake(100.0, 200.0);
s.height += 100; //變高100個點(diǎn)歉井。
```
- CGRect
一個C語言結(jié)構(gòu)體柿祈,包含前面的`CGPoint`和`CGSize`。
```
CGRect r = CGRectMake(10.0, 20.0, 100.0, 200.0);
```
- 原點(diǎn)
iOS中的原點(diǎn)在整個視圖的左上角,橫軸為X軸躏嚎,越向右值越大蜜自。豎軸為Y軸,越向下值越大紧索。這一點(diǎn)與笛卡爾坐標(biāo)系不同袁辈。
<center>![](斯坦福大學(xué)iOS公開課筆記(7)-繪制視圖和手勢/Coordinates.png)</center>
- bounds
這是你的坐標(biāo)系中繪制區(qū)域的原點(diǎn),以及高度和寬度珠漂。
- frame
也是用來設(shè)置你的坐標(biāo)系中繪制區(qū)域的位置和高度和寬度晚缩。只不過他是基于你的父視圖的位置的。
- center
表示你所在的父視圖的坐標(biāo)中的中心位置媳危,注意荞彼!沒有屬性可以得到你自身的中心,你只能使用自己的bounds的寬和高除以二獲取自身的中心位置待笑。
- bounds和frame的區(qū)別
bounds和frame雖然都是表示視圖的位置和大小鸣皂,但是他們之前還是有一些區(qū)別的,如圖暮蹂,當(dāng)視圖發(fā)生旋轉(zhuǎn)之后寞缝,bounds還是((0,0) (200,250)),而frame則變?yōu)榱?(140,65) (320,320))。
<center>![](斯坦福大學(xué)iOS公開課筆記(7)-繪制視圖和手勢/frameAndBounds.png)</center>
### 創(chuàng)建一個視圖
創(chuàng)建視圖可以使用`storyboard`直接拖拽或者使用代碼來`alloc init`生成一個視圖
CGRect labelRect = CGRectMake(20,20,50,30);
UILabel *label = [[UILabel alloc] initWithFrame:labelRect];
label.text = @"Hello";
[self.view addSubView:label];
當(dāng)我們要繪制一個自定義的視圖的使用仰泻,只要重寫實現(xiàn)UIView中的`drawRect`方法就可以了荆陆。系統(tǒng)會自動調(diào)用它。
********注意:不要調(diào)用****`drawRect`****方法集侯,只是重寫并實現(xiàn)他********如果想要重繪視圖被啼,調(diào)用`setNeedsDisplay`就可以了。
另外可以使用`UIBezierPath`來繪制你需要的圖形棠枉,關(guān)于`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/)浓体。
關(guān)于自定義視圖的具體使用,下面的Demo中會寫的清清楚楚辈讶,所以這邊就不多說了命浴。
## UIGestureRecognizer
手勢是iOS中的輸入部分。添加手勢只需要兩個步驟
1.這一步一般由控制器完成贱除,為自己添加一個手勢識別器生闲。
2.這一步一般由視圖自己完成,處理手勢完成后發(fā)生的事情勘伺。
### 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 旋轉(zhuǎn)手勢
@property CGFloat rotation; 弧度
@property (readonly) CGFloat velocity; 每秒變化的速度
### UISwipeGestureRecgnizer 滑動手勢
@property UISwipeGestureRecognizerDirection direction 滑動方向
@property NSUInteger numberOfTouchesRequired; 幾只手指來完成
### UITapGestureRecognizer 點(diǎn)擊手勢
@property NSUInteger numberOfTapsReqired跪腹;幾次點(diǎn)擊
@property NSUInteger numberOfTouchesRequired; 幾只手指來完成
## 繪制紙牌 Demo
這個繪制紙牌的Demo中運(yùn)用了之前所講的繪制和手勢的知識。我們會使用到文字和圖片的繪制以及滑動(swip)手勢和捏合(pinch)手勢飞醉。
<center>![](斯坦福大學(xué)iOS公開課筆記(7)-繪制視圖和手勢/showSuperCard.gif)</center>
### 創(chuàng)建 view
首先我們新建一個UIView冲茸,在共有方法里聲明他的花色和大小以及是否正面向上的屬性屯阀,并重寫他們的setter方法,在他們的值發(fā)生改變的時候調(diào)用`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
### 設(shè)置比例
在繪制卡片之前我們先通過幾個方法和宏定義來做一些適配性的東西保證卡片在不同的大小下都可以完美的顯示出來难衰。
![](斯坦福大學(xué)iOS公開課筆記(7)-繪制視圖和手勢/card_big.png)
![](斯坦福大學(xué)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`方法來實現(xiàn)
#### 繪制圖片部分
-
(void)drawRect:(CGRect)rect
{
//使用UIBezierPath繪制一個圓角。UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:[self cornerRadius]];
[roundedRect addClip];
[[UIColor whiteColor] setFill];
UIRectFill(self.bounds); //調(diào)用C
[[UIColor blackColor] setStroke];
[roundedRect stroke];
if(self.faceUp)
{
//根據(jù)撲克牌的名字找到對應(yīng)的圖片
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];
//旋轉(zhuǎn)
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, self.bounds.size.width, self.bounds.size.height);
CGContextRotateCTM(context, M_PI);
[cornerText drawInRect:textBounds];
}
### 添加滑動手勢
滑動手勢是使用storyboard中脫線的方式來實現(xiàn)的逗栽,和添加一個`UIButton`的點(diǎn)擊事件差不多盖袭,將添加的滑動手勢拖入到控制器中并實現(xiàn)方法就可以了。
-
(IBAction)swipe:(id)sender {
self.playingCardView.faceUp = !self.playingCardView.faceUp;
}
### 添加捏合手勢
捏合手勢是使用代碼添加的彼宠,在view中添加手勢的響應(yīng)方法并把它放到公有文件中鳄虱。
- (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:)]];
-----
以上就是斯坦福大學(xué)iOS公開課第七課的筆記。