OC常用視圖、動畫方法

繪圖

系統(tǒng)將內(nèi)存中的對象渲染成屏幕上的圖片
IOS中實(shí)現(xiàn)繪圖功能的底層框架結(jié)構(gòu)

  1. 最基礎(chǔ)的繪圖引擎:Core Graphics(QuartZ2D)
  2. 基于Core Graphics封裝出來的第二個層次:Core Animation
  3. 基于Core Animation再封裝出來就是UIKit

預(yù)備:

新建一個類,繼承自UIView,實(shí)現(xiàn)drawRect方法,并且實(shí)現(xiàn)繪制的代碼目前只能寫在這個方法中,因?yàn)槔L制過程是一個非常復(fù)雜的過程,系統(tǒng)只允許在特定的區(qū)域內(nèi)拿到繪圖上下文對象CGContextRef,該方法會由系統(tǒng)在創(chuàng)建視圖實(shí)例時自動調(diào)用一次

繪制圖形——UIBezier Path

step1:創(chuàng)建路徑對象,勾勒路徑

step2:設(shè)置填充或描邊的顏色

step3:繪制[path stroke];

直線

UIBezierPath * path = [UIBezierPath bezierPath];
//勾勒路徑
[path moveToPoint:CGPointMake(40, 40)];
[path addLineToPoint:CGPointMake(40, 140)];
//封閉圖形
[path closePath];

弧線

UIBezierPath * path = [UIBezierPath bezierPath];
//圓弧的中心
CGPoint center = CGPointMake(self.bounds.size.width*0.5, self.bounds.size.height*0.5);
//圓弧的半徑
CGFloat radius = MIN(self.bounds.size.width, self.bounds.size.height)*0.5-10;
//勾勒路徑
//起點(diǎn)是二分之三π,終點(diǎn)是起點(diǎn)數(shù)值基礎(chǔ)上累加一個弧度,累加的弧度要由下載數(shù)據(jù)決定,數(shù)據(jù)的百分之百時,弧度時2π,所以下載了0.3時,就用0.3*2π就可以了
[path addArcWithCenter:center radius:radius startAngle:M_PI_2*3 endAngle:M_PI_2*3+self.downloadValue*2*M_PI clockwise:YES];

曲線

UIBezierPath * path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(180, 40)];
//添加曲線
[path addCurveToPoint:CGPointMake(40, 180) controlPoint1:CGPointMake(40, 40) controlPoint2:CGPointMake(180, 180)];

矩形

UIBezierPath * path2 = [UIBezierPath bezierPathWithRect:CGRectMake(50, 50, 200, 100)];

圓角矩形

UIBezierPath * path2 = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 200, 100) cornerRadius:50];
[[UIColor blueColor]setStroke]; 

橢圓(正圓)

UIBezierPath * path3 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 200, 200, 100)];

總結(jié):

前三種圖形(直線、曲線、圓?。?,先創(chuàng)建UIBezierPath然后使用addXXX方法實(shí)現(xiàn)路徑設(shè)計(jì)

后兩種圖形(矩形、橢圓),在創(chuàng)建UIBezierPath時直接就設(shè)置了路徑,使用bezierPathWithXXX方法

繪制字符串——NSString

只能畫一行

NSString *str = @"這是一段用于測試多文本能夠自動換行的測試文字,大概能夠?qū)憘€兩三行看到結(jié)束就可以,恩,差不多了,就這樣了吧!";    
NSDictionary *attributes = @{
    NSFontAttributeName:[UIFont systemFontOfSize:20],
    NSForegroundColorAttributeName:[UIColor redColor]};
[str drawAtPoint:CGPointMake(30, 30) withAttributes:attributes];

在一個空間內(nèi)繪制,自動換行

[str drawInRect:CGRectMake(30, 30, 150, 200) withAttributes:attributes];

根據(jù)文本內(nèi)容自動設(shè)置空間

//自動計(jì)算出不超出指定的寬度和高度的前提下
//能夠完整的顯示字符串的最合適的高度和寬度
CGRect  textFrame =  [str boundingRectWithSize:CGSizeMake(200, 999) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];

[str drawInRect:CGRectMake(50, 50, textFrame.size.width, textFrame.size.height) withAttributes:attributes]; 

繪制圖片——UIImage

UIImage * image = [UIImage imageNamed:@"icon120"];
[image drawAtPoint:CGPointMake(50, 40)];
[image drawInRect:CGRectMake(50, 160, 120, 120)];
使用UIBezierPath的addClip方法,將路徑以外的部分設(shè)置為繪圖無效區(qū),再來繪制圖片時就能生成異形圖片
[path addClip];

重繪

當(dāng)數(shù)據(jù)發(fā)生改變時,希望能夠根據(jù)新的數(shù)據(jù)重新繪內(nèi)容時,調(diào)用視圖的setNeedsDisplay方法,通知系統(tǒng)重繪。

[self setNeedsDisplay];

使用UIGraphiceBeginImageContextWithOptions和UIGraphiceEndImageContext開辟臨時畫布,在這個區(qū)間內(nèi)可以編寫繪圖代碼,最后將這塊畫布保存成一張新的圖片

//申請一塊臨時畫布
//opaque YES表示不透明 NO表示透明
//scale 畫得比例
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(self.imageView.bounds.size.width, self.imageView.bounds.size.height), NO, 1);

UIBezierPath * path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, self.imageView.bounds.size.width, self.imageView.bounds.size.height)];
[path addClip];
UIImage * image = [UIImage imageNamed:@"icon120"];
[image drawInRect:CGRectMake(0, 0, self.imageView.bounds.size.width, self.imageView.bounds.size.height)];
//將畫布中的圖像另存為一張image對象
UIImage * newHeaderImage = UIGraphicsGetImageFromCurrentImageContext();
self.imageView.image = newHeaderImage;

    UIGraphicsEndImageContext();

變形Transform

視圖發(fā)生了位移(Translation)或者縮放(Scale)或者旋轉(zhuǎn)(Rotation)這樣的外觀或位置的改變

實(shí)現(xiàn)變形:修改視圖的transform屬性即可

transform屬性屬于CGAffineTransform結(jié)構(gòu)體類型(仿射變換)

包含有6個數(shù)值的3x3矩陣

可以借助函數(shù)幫我們計(jì)算出為了改變視圖的樣式而對應(yīng)的矩陣值

實(shí)現(xiàn)幫我們計(jì)算矩陣的函數(shù):

計(jì)算新的矩陣數(shù)值時,永遠(yuǎn)是基于視圖沒有任何變形的那個狀態(tài)來計(jì)算

CGAffineTransformMakeTranslation()
CGAffineTransformMakeScale()
CGAffineTransformMakeRotation()

計(jì)算新的矩陣數(shù)值時,基于傳入的矩陣狀態(tài)來計(jì)算新的矩陣,會疊加變形效果

CGAffineTransformTranslate()
CGAffineTransformScale()
CGAffineTransformRotate()

使用

self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, 20, 20);

手勢

將用戶物理性的觸屏操作轉(zhuǎn)變成了對象存儲起來,所有手勢的父類UIGestureRecognizer

系統(tǒng)將一下有特點(diǎn)的觸屏操作封裝成了不同的手勢類型包括:

UITapGestureRecognizer->點(diǎn)擊(一次性手勢)

UISwipeGestureRecognizer->輕掃(解鎖)(一次性手勢)

UILongPressGestureRecognizer->長按

UIPanGestureRecognizer->拖拽

UIPinchGestureRecognizer->捏合手勢

UIRotationGestureRecognizer->轉(zhuǎn)轉(zhuǎn)

使用手勢

step1:創(chuàng)建指定手勢的實(shí)例,在創(chuàng)建時設(shè)定好當(dāng)該類型手勢發(fā)生時,系統(tǒng)自動發(fā)什么消息(自動調(diào)用那個方法來響應(yīng))

step2:設(shè)置或讀取手勢對象的核心屬性

step3:將手勢添加到某個視圖中,即代表,當(dāng)用戶在該視圖上做了這樣的手勢時,系統(tǒng)才會捕獲并調(diào)用方法來執(zhí)行響應(yīng)

Tap點(diǎn)擊手勢

核心屬性:numberOfTapsRequired

方法:locationInView獲取觸點(diǎn)在某個視圖坐標(biāo)系下的坐標(biāo)值

//1 創(chuàng)建Tap手勢
UITapGestureRecognizer * tapGR = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)];
//2 設(shè)置手勢的屬性
//設(shè)置需要觸發(fā)手勢的點(diǎn)擊數(shù),默認(rèn)1
tapGR.numberOfTapsRequired = 1;
//設(shè)置需要觸發(fā)手勢的觸點(diǎn)的個數(shù),默認(rèn)1
tapGR.numberOfTouchesRequired = 1;
//3 將手勢與具體的視圖綁定在一起
[self.view addGestureRecognizer:tapGR];
//手勢的響應(yīng)方法
-(void)tap:(UITapGestureRecognizer *)gr{
    //返回觸摸點(diǎn)在哪個坐標(biāo)系中的位置
    CGPoint point = [gr locationInView:self.view];
    NSLog(@"%@",NSStringFromCGPoint(point));
}

Swipe輕掃手勢

屬性:direction方向

可以將左和右,或者上和下進(jìn)行組合,使用按位或運(yùn)算符

注意:Tap和Swipe手勢歸為一次性手勢,即手勢發(fā)生過程中,響應(yīng)方法只執(zhí)行一次

UISwipeGestureRecognizer * swipeGR = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];
//掃動方向
swipeGR.direction = UISwipeGestureRecognizerDirectionRight|UISwipeGestureRecognizerDirectionLeft;
//綁定手勢到視圖上
[self.view addGestureRecognizer:swipeGR];

LongPress長按手勢

設(shè)置:minimumPressDuration長按需要的最少時間

長按手勢有不同的狀態(tài),按下時,起始狀態(tài),移動中,改變狀態(tài),抬起時,結(jié)算狀態(tài)。從一個狀態(tài)到另一個狀態(tài)發(fā)生變化時都會連續(xù)地調(diào)用響應(yīng)方法

UILongPressGestureRecognizer * longPressGR = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)];
//設(shè)置長按動作的間隔秒數(shù)
longPressGR.minimumPressDuration = 2;

[self.view addGestureRecognizer:longPressGR];
-(void)longPress:(UILongPressGestureRecognizer *)gr{
    NSLog(@"%@",NSStringFromCGPoint([gr locationInView:self.view]));
}

Pinch捏合手勢

UIPinchGestureRecognizer * pinchGR = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinch:)];
[self.view addGestureRecognizer:pinchGR];
-(void)pinch:(UIPinchGestureRecognizer *)gr{
    //代表動作快慢的速率
    //正數(shù)代表外擴(kuò),負(fù)數(shù)代表往內(nèi)捏合,絕對值越大,代表動作越快,越小代表動作越慢
    CGFloat veloctity = gr.velocity;
    //代表動作外擴(kuò)或向內(nèi)捏合的比率倍數(shù)
    //外擴(kuò):大于1的數(shù),捏合:小于1的數(shù)
    CGFloat scale = gr.scale;
    NSLog(@"velocity = %.2f,scale=%.2f",veloctity,scale);
    //每次計(jì)算完之后置1,讓每次比較以上一次得到的數(shù)為基準(zhǔn)
    gr.scale = 1;
}

Rotation旋轉(zhuǎn)手勢

UIRotationGestureRecognizer * rotationGR = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotation:)];
[self.view addGestureRecognizer:rotationGR];
-(void)rotation:(UIRotationGestureRecognizer *)gr{
    //旋轉(zhuǎn)的弧度
    //順時針正數(shù),逆時針負(fù)數(shù)
    CGFloat rotation = gr.rotation;
    
    NSLog(@"%.2f",rotation);
}

Pan拖拽手勢

UIPanGestureRecognizer * panGR = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:panGR];
-(void)pan:(UIPanGestureRecognizer *)gr{
    //手勢觸點(diǎn),在self.view坐標(biāo)系下的點(diǎn)的坐標(biāo)值
    CGPoint location = [gr locationInView:self.view];
    //手勢移動到的新點(diǎn)相對于手勢起始點(diǎn)的橫向縱向距離
    //手勢移動了多遠(yuǎn)
    CGPoint translation = [gr translationInView:self.view];
    
    NSLog(@"%@ %@",NSStringFromCGPoint(location),NSStringFromCGPoint(translation));
    
}

多手勢并存:

step1:設(shè)置需要并處的手勢的代理為當(dāng)前控制器

step2:控制器遵守協(xié)議UIGestureRecognizerDelegate

step3:實(shí)現(xiàn)

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;方法,返回YES
    //拖拽功能
    UIPanGestureRecognizer * panGR = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
    [self.view addGestureRecognizer:panGR];
    //縮放功能
    UIPinchGestureRecognizer *pinchGR = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinch:)];
    [self.view addGestureRecognizer:pinchGR];
    //設(shè)置自己為代理
    pinchGR.delegate = self;
    //旋轉(zhuǎn)功能
    UIRotationGestureRecognizer * rotationGR = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotation:)];
    [self.view addGestureRecognizer:rotationGR];
    //設(shè)置自己為代理
    rotationGR.delegate = self; 
    //雙擊還原
    UITapGestureRecognizer * tapGR = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)];
    tapGR.numberOfTapsRequired = 2;
    [self.view addGestureRecognizer:tapGR];

//實(shí)現(xiàn)圖片跟隨手勢移動
-(void)pan:(UIPanGestureRecognizer *)gr{
    
    //相對于起始點(diǎn)走了多遠(yuǎn)
    //當(dāng)視圖發(fā)生放大縮小時,相應(yīng)的視圖的坐標(biāo)系的比例也會放大或縮小
    //要修改的transform屬性,數(shù)據(jù)是使用imageView坐標(biāo)系中的刻度值
    //所以在讀取手勢移動了的距離是多少時,也要讀取在imageView這個坐標(biāo)系下的距離
    CGPoint translation = [gr translationInView:self.imageView];
    self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, translation.x, translation.y);
    //將本次走了的距離歸零
    [gr setTranslation:CGPointZero inView:self.imageView];
    
    //-------修改中心點(diǎn)實(shí)現(xiàn)位移------
//    CGPoint translation2 = [gr translationInView:self.view];
//    CGPoint center = self.imageView.center;
//    center.x += translation2.x;
//    center.y += translation2.y;
//    self.imageView.center = center;
//    [gr setTranslation:CGPointZero inView:self.view];
}
跟隨手勢捏合實(shí)現(xiàn)放大縮小
-(void)pinch:(UIPinchGestureRecognizer *)gr{
    self.imageView.transform = CGAffineTransformScale(self.imageView.transform, gr.scale, gr.scale);
    //去掉本次比率,歸1
    gr.scale = 1;
}
//縮放
-(void)rotation:(UIRotationGestureRecognizer *)gr{
    self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, gr.rotation);
    gr.rotation = 0;
}
//實(shí)現(xiàn)多點(diǎn)觸控并發(fā)方法
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}
//雙擊還原
-(void)tap:(UITapGestureRecognizer *)gr{
    //Identity系統(tǒng)提供的一個常量,該常量中記錄的就是沒有發(fā)生任何變形時的那個矩陣
    self.imageView.transform = CGAffineTransformIdentity;
}

深入坐標(biāo)系

UIView——》frame bounds center transform

frame

a>什么是frame?

類型:CGRect結(jié)構(gòu)體類型

作用:記錄了試圖左頂點(diǎn)在俯視圖坐標(biāo)系中的位置,以及視圖在父視圖內(nèi)占的寬高

b>什么時候使用frame?

當(dāng)需要將一個視圖添加到另一個視圖中做子視圖時,一定要設(shè)置frame,保證在父視圖中的位置即占據(jù)的區(qū)域大小

c>當(dāng)修改frame時,其他三個屬性是否會改變?

bounds:YES

center:YES

transform:NO

2>bounds

a>什么是bounds?

類型:CGRect結(jié)構(gòu)體類型

作用:bounds中的x和y用于記錄該視圖自己坐標(biāo)系的基準(zhǔn)值,默認(rèn)都是0,0后兩個值記錄的就是該視圖自己的大小尺寸

b>什么時候使用bounds?

如果想要修改子視圖的位置,可以調(diào)整bounds中的x或y(無法直接修改,要先建立CGRect的數(shù)值接收,修改后重新賦值),需要讀取視圖的大小時,那么就使用bounds中的width和height。在沒有變形時,視圖有多大,在父視圖中就會占據(jù)多大空間,所以bounds中的w和h與frame中的w和h相等。

c>當(dāng)修改bounds時,其他三個屬性是否會改變?

frame:YES

center:NO

transform:NO

3>center

a>什么是center?

類型:CGPoint類型

作用:記錄視圖中心點(diǎn)在父視圖坐標(biāo)系下的位置

b>什么時候使用center?

如果想實(shí)現(xiàn)子視圖位置的變化,則可以修改center

添加子視圖時,如果不使用frame,那么可以使用bounds+center的組合設(shè)置來完成

c>當(dāng)修改center時,其他三個屬性是否會改變?

frame:YES

bounds:NO

transform:NO

4>transform

a>什么是transform?

類型:CGAffineTransform類型

作用:實(shí)現(xiàn)視圖在展現(xiàn)出外觀時,發(fā)生的位置的偏移、放大或縮小的效果、以及旋轉(zhuǎn)效果

b>什么時候使用transform?

在展現(xiàn)外觀時,有以上變化效果時,修改transform。

c>當(dāng)修改transform時,其他三個屬性是否會改變?

frame:YES

bounds:NO

center:NO

結(jié)論:

屏幕可以分為兩個空間,一個是看的見的用戶空間,一個是看不見的設(shè)備空間,transform屬性記錄的就是如何將設(shè)備空間中的視圖映射到用戶空間上,也就是說transform記錄的是映射規(guī)則

frame是用戶空間中的數(shù)據(jù),也就是記錄看見的結(jié)果的數(shù)據(jù),bounds+center時設(shè)備空間中的數(shù)據(jù),也就是合起來記錄看不見的那個空間中視圖的大小和位置,在不改變映射規(guī)則(transform)時,表里如一,但是,一旦改變了映射規(guī)則,那么就會表里不一,里面存儲的視圖展現(xiàn)出來時會根據(jù)transform發(fā)生改變

添加子視圖,找frame
讀取視圖大小,找bounds
改位置,找center
實(shí)在是想旋轉(zhuǎn),找transform

故事板中添加手勢

  1. 從資源庫中選擇合適的手勢圖標(biāo),拖拽到界面上
  2. 為了減少綁定過程,將手勢直接拖拽到綁定的目標(biāo)視圖上
  3. 可以在場景頂端的橫條中找到拖拽過的手勢,然后選中,在第四個檢查器中做常規(guī)設(shè)置
  4. 拆分視圖下,選中手勢對象,連線到代碼中,添加對該手勢動作的響應(yīng)

圖片視圖默認(rèn)不能與用戶交互,所以添加手勢到圖片視圖上以后,要開啟用戶交互功能,設(shè)置userInterActionEnabled為YES

UITouch觸控

將用戶的物理觸屏動作轉(zhuǎn)變成了數(shù)據(jù)存到了UITouch對象中

1>普通的視圖

touchesBegan
touchesMoved
touchesEnded
touchesCancel
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    //按下觸點(diǎn),就將此點(diǎn)記錄到屬性中
    self.beginPoint = [touch locationInView:self];
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    //創(chuàng)建路徑
    self.path = [UIBezierPath bezierPathWithRect:CGRectMake(self.beginPoint.x, self.beginPoint.y, point.x-self.beginPoint.x, point.y-self.beginPoint.y)];
    self.path.lineWidth = 3;
    
    [self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //觸點(diǎn)抬起時,將臨時的路徑確定保存到數(shù)組中
    [self.allPath addObject:self.path];
}
- (void)drawRect:(CGRect)rect
{
    [[UIColor redColor] setStroke];
    //當(dāng)前正在描繪,但還不確定結(jié)果的那個矩形
    [self.path stroke];
    //將所有存到數(shù)組中的已經(jīng)確定了的矩形都重新一遍
    for (UIBezierPath *path in self.allPath)
    {
        [path stroke];
    }   
}

2>控制器自帶的視圖

實(shí)現(xiàn)控制器的
touchesBegan
touchesMoved
touchesEnded

3>UITouch的用處

a>跟蹤觸點(diǎn)的軌跡,做類似于畫板這樣的應(yīng)用
b>視圖跟蹤觸點(diǎn)的移動,比pan手勢的跟蹤位置更準(zhǔn)確

布局(Layout)

子視圖在父視圖中的擺放位置,通過設(shè)置frame實(shí)現(xiàn)布局,但是由于父視圖可能因?yàn)槠聊坏姆N類、橫豎屏翻轉(zhuǎn)、鍵盤彈起、各種bar的顯示隱藏等因素發(fā)生改變,而此時如果不修改子視圖的frame,依然保持在原有尺寸下擺放,那么界面就會出現(xiàn)混亂。所以當(dāng)父視圖大小發(fā)生變化時,希望調(diào)整子視圖的frame,適應(yīng)新的尺寸,保證布局出來的界面依然美觀,于是使用布局技術(shù)實(shí)現(xiàn)該需求。

核心理念:改frame

哪些因素會影響父視圖大小發(fā)生變化?

1>屏幕尺寸(3.5、4、4.7、5.5)
2>橫豎屏旋轉(zhuǎn)
3>各種Bar
4>鍵盤彈起
5>特殊的bar 來電話的綠色條、開啟熱點(diǎn)的藍(lán)色條、錄音時的紅色條

純代碼布局

理念:只要檢測到父視圖的frame發(fā)生了變化,則將父視圖中的所有子視圖重新計(jì)算frame

關(guān)鍵點(diǎn):如何檢測到父視圖frame發(fā)生了變化

a>對控制器自帶的那個view的直接子視圖的布局

代碼寫在控制的viewDidLayoutSubViews方法中

-(void)viewDidLayoutSubviews{
    [super viewDidLayoutSubviews];
    //布局按鈕
    CGRect frame = self.button.frame;
    frame.origin.x = self.view.bounds.size.width - 20 - frame.size.width;
    //獲取頂部被bar占據(jù)的高度
    frame.origin.y = self.topLayoutGuide.length + 20;
    self.button.frame = frame;
    
    frame = self.label.frame;
    frame.origin.x = self.view.bounds.size.width - frame.size.width - 20;
    //獲取底部被bar占據(jù)的高度
    frame.origin.y = self.view.bounds.size.height - 20 - frame.size.height - self.bottomLayoutGuide.length;
    self.label.frame = frame;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self.navigationController setNavigationBarHidden:!self.navigationController.navigationBarHidden animated:YES];
    [self.navigationController setToolbarHidden:!self.navigationController.toolbarHidden animated:YES];
}

b>對某個view的內(nèi)部的子視圖的布局

代碼寫在視圖的layoutSubViews方法中(第一句一定要調(diào)用super中對應(yīng)方法)

如:UITableViewCell對它內(nèi)部子視圖的布局、自定義tabBar對內(nèi)部的子視圖的布局

如何獲取視圖的頂部和底部被系統(tǒng)的bar占據(jù)的高度

AutoResizing布局

理念:視圖的位置由視圖與父視圖之間的間距決定,此間距可以設(shè)置為木棍或彈簧;視圖的大小遵循等比變換原則——如父視圖原有寬度是100,變化后是200,則父視圖的寬是原來的二倍,于是父視圖中的所有子視圖的寬度也會變?yōu)樵瓉淼膶挾鹊亩?/p>

如何使用:

預(yù)備:一定要關(guān)閉Auto Layout,然后在視圖的第五個檢查器中會有6條紅線,

step1:如果確定子視圖與父視圖的邊緣距離固定,則點(diǎn)亮該方向的紅線,

step2:如果確定子視圖的寬高也需要變化,則點(diǎn)亮中間帶有箭頭的紅線。

Auto Layout

核心理念:通過為視圖添加多個約束來說明視圖的位置,當(dāng)父視圖發(fā)生變化時,系統(tǒng)就會在部位被任何一個約束的情況下,為我們自動計(jì)算出新的frame,從而達(dá)到自動布局的效果

約束:添加的對視圖位置或大小的限定條件

如:
文字描述:按鈕距離頂部20,距離左邊緣20,寬100,高40
約束1:豎直方向上的約束
約束2:水平方向上的約束
約束3:大小中寬的約束
約束4:大小中高的約束

使用約束的原則:

1>約束要準(zhǔn)確

2>約束不能沖突

如何為視圖添加約束:

方式一:不用寫代碼,在故事板中通過配置菜單

方式二:使用代碼創(chuàng)建約束

約束類型:NSLayoutConstraint

1>萬能公式法:

任何一個約束都可以使用下面的公式進(jìn)行描述:
view1.attr1 = view2.attr2 * multiplier + constant
按鈕的左邊緣距離父視圖的左邊緣為20個點(diǎn)
button.left = view.left * 1 + 20;
按鈕的寬度是另一個按鈕的寬度的兩倍
button.width = button2.width * 2 + 0;
按鈕的右邊緣距離父視圖的右邊緣為20
button.right = view.right * 1 - 20;
按鈕的寬度為80
button.width = nil.not * 0 + 80;

注意:

a>使用代碼創(chuàng)建的視圖實(shí)例,添加到父視圖中時,系統(tǒng)會偷偷地為該視圖添加兩個約束,將視圖的左和上的紅線變成約束,一般都需要關(guān)閉該功能

b>約束創(chuàng)建好之后,要將該約束添加到視圖所在的父視圖身上

    //1 關(guān)閉button1和button2的自動翻譯紅線為約束功能
    button1.translatesAutoresizingMaskIntoConstraints = NO;
    button2.translatesAutoresizingMaskIntoConstraints = NO;
    
    //2 創(chuàng)建約束
    //button1距離左邊緣20個點(diǎn) button1.left = view.left * 1 + 20
    NSLayoutConstraint * c1 = [NSLayoutConstraint constraintWithItem:button1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:20];
    [self.view addConstraint:c1];
    //button1.top = view.top * 1 + 20
    NSLayoutConstraint * c2 = [NSLayoutConstraint constraintWithItem:button1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:60];
    [self.view addConstraint:c2];
    //button1.right = button2.left * 1 - 10
    NSLayoutConstraint * c3 = [NSLayoutConstraint constraintWithItem:button1 attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:button2 attribute:NSLayoutAttributeLeft multiplier:1 constant:-10];
    [self.view addConstraint:c3];
    //button1.width = button2.width * 1 + 0
    NSLayoutConstraint * c4 = [NSLayoutConstraint constraintWithItem:button1 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:button2 attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
    [self.view addConstraint:c4];
    //button2.right = view.right * 1 - 20
    NSLayoutConstraint * c5 = [NSLayoutConstraint constraintWithItem:button2 attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1 constant:-20];
    [self.view addConstraint:c5];
    //button2.top = button1.top * 1 + 0
    NSLayoutConstraint * c6 = [NSLayoutConstraint constraintWithItem:button2 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:button1 attribute:NSLayoutAttributeTop multiplier:1 constant:0];
    [self.view addConstraint:c6];
    //button1.height = nil.notanattri * 0 + 40
    NSLayoutConstraint * c7 = [NSLayoutConstraint constraintWithItem:button1 attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:40];
    [self.view addConstraint:c7];
    //button2.height = button1.height * 1 + 0
    NSLayoutConstraint * c8 = [NSLayoutConstraint constraintWithItem:button2 attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:button1 attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
    [self.view addConstraint:c8];

2>VFL法:Visual Format Language

使用一個特殊的字符串來表達(dá)一組視圖之間的約束關(guān)系,系統(tǒng)會自動將字符串中表達(dá)的約束一次性創(chuàng)建成功,存到一個數(shù)組中

特殊的字符串中可用的特殊符號?

[ ]視圖
-默認(rèn)8個點(diǎn)間距
-x-間距為x 
( )視圖的寬度
|父視圖邊緣
V:豎直方向
    button1.translatesAutoresizingMaskIntoConstraints = NO;
    button2.translatesAutoresizingMaskIntoConstraints = NO;
    button3.translatesAutoresizingMaskIntoConstraints = NO;
    //創(chuàng)建一個尺寸對照表
    NSDictionary * metrics = @{@"left":@20,
                               @"right":@20,
                               @"spacing":@20};
    //創(chuàng)建視圖的對照表
    //此函數(shù)會自動將傳入的對象名字符串做key,將此對象做key的value,生成如下形式的字典{@"b1":b1,@"b2":b2,@"b3":b3}
    NSDictionary * dictionary = NSDictionaryOfVariableBindings(button1,button2,button3);
    
    //1 創(chuàng)建水平方向的約束描述
    NSString * hVFL = @"|-left-[button1]-spacing-[button2(==button1)]-spacing-[button3(==button1)]-right-|";
    //2 將VFL變成一組約束
    NSArray * cs1 = [NSLayoutConstraint constraintsWithVisualFormat:hVFL options:NSLayoutFormatAlignAllTop|NSLayoutFormatAlignAllBottom metrics:metrics views:dictionary];
    //3 添加約束到父視圖
    [self.view addConstraints:cs1];
    //創(chuàng)建豎直方向的VFL
    NSString * vVFL = @"V:|-spacing-[button1(40)]";
    NSArray * cs2 = [NSLayoutConstraint constraintsWithVisualFormat:vVFL options:0 metrics:metrics views:dictionary];
    [self.view addConstraints:cs2];

動畫(Animation)

動畫

一般指"幀動畫",一幀就是一張靜態(tài)圖片,一般1秒24幀以上,人眼就無法分辨圖片的切換間隙了

幀率:FPS(Frame Per Second)每秒多少幀

ios中的動畫

a>UIImage

[UIImage animatedImageNamed:@"ship-anim" duration:播放完一遍所有圖片的時間]

b>UIImageView

1>拖拽到Assests.xcassets中的圖片資源只能使用UIImage imageNamed方式來加載,不能使用絕對路徑的方式來加載,因?yàn)橘Y源庫中的圖片部署到沙盒中的xxx.app包中時都被轉(zhuǎn)化到Assets.car文件中,此文件中沒有路徑一說。

2>沒有拖拽到Assests.xcassets資源庫中的圖片,部署時會被添加到xxx.app包種的根路徑下,此時加載這些有路徑的圖片時,可以使用imageNamed方法,也可以使用[[NSBundle mainBundle]pathForResource:fileName ofType:nil]方法。NSBundle mainBundle代表的就是xxx.app的根路徑

c>UIView

類方法:animatedxxx方法

可以做的動畫效果:連續(xù)的改變center,實(shí)現(xiàn)位移動畫,改變alpha,實(shí)現(xiàn)淡入淡出,改變transform,實(shí)現(xiàn)連續(xù)的旋轉(zhuǎn)

基本步驟:

step1:在動畫方法外,設(shè)置視圖的開始狀態(tài)
step2:調(diào)用UIView的類方法animatedxxx,填寫完動畫種類、時常相關(guān)參數(shù)后,在block塊內(nèi),設(shè)置好視圖的動畫結(jié)束后的狀態(tài)即可

d>通過修改約束實(shí)現(xiàn)動畫

step1:先設(shè)置約束新的狀態(tài)值
step2:調(diào)用UIView的animateWithDurationXX時,在block塊內(nèi)調(diào)用修改約束的視圖的父視圖的layoutIfNeeded方法完成動畫過程的設(shè)置

Core Animation

UIView核心顯示功能就是依靠CALayer實(shí)現(xiàn)的

UIView和CALayer的關(guān)系:1)UIView的顯示能力是依靠底層的CALayer實(shí)現(xiàn)的,每一個UIView都包含了一個CALayer對象,修改了CALayer,會影響表現(xiàn)出來的UIView外觀。2)CALayer是不能夠響應(yīng)事件的,但UIView由于繼承自UIResponder,所以UIView還可以響應(yīng)用戶的觸摸事件。

Core Animation中的動畫只能添加到CALayer類型的對象身上,與UIView動畫最大的區(qū)別就是CA動畫是假的,視圖看著好像位置發(fā)生了變化,但其實(shí)實(shí)際位置沒變,在UIView動畫中,由于明確的設(shè)定了動畫結(jié)束時的狀態(tài),所以視圖的數(shù)據(jù)會隨著動畫的結(jié)束而真的被改變

動畫類的父類是CAAnimation,一般使用它的子類CABasicAnimation和CAKeyFrameAnimation

獲取UIView底層的那個CALayer對象

通過視圖的.layer屬性就能拿到該視圖底層的層對象

使用CALayer可以做什么

1>可以修改系統(tǒng)已有的UIView的layer改變視圖的外觀

常用屬性:

masksToBounds->是否可裁剪(修改圓形圖片一定要選)

backgroundColor->背景色

shadowXXXX->陰影

borderColor->邊框的顏色

borderWidth->邊框的寬度

corneRadius->圓角邊框

transform(CATransform3D)->變形

與尺寸位置有關(guān)的三個重要屬性:

bounds->大小

position->位置

anchorPoint->錨點(diǎn)

2>創(chuàng)建新的CALayer來完成視圖的設(shè)計(jì)

CALayer->繪制圖片

CATextLayer->繪制字符串

CAShapeLayer->繪制圖形

3>CALayer的動畫

CADisplayLink

理念:類似于定時器NSTimer,自身已經(jīng)設(shè)定好,一秒鐘會調(diào)用指定的方法60次,只需要算出每一幀改變的數(shù)值是多少

CABasicAnimation

基礎(chǔ)動畫:只需要設(shè)置動畫的起始值和終點(diǎn)值即可,一定要設(shè)置keyPath屬性,以此說明動畫需要修改的屬性名是哪個

CAKeyFrameAnimation

關(guān)鍵幀動畫:可以定制動畫過程中的細(xì)節(jié),所以可以通過values屬性記錄中間每一個重要的變化點(diǎn)的數(shù)據(jù),可以認(rèn)為基礎(chǔ)動畫就是一個只有兩個關(guān)鍵幀的動畫,另外,關(guān)鍵幀最重要的效果就是可以自定義動畫路徑,通過path屬性記錄

NSNotification通知

什么是通知

一個對象發(fā)生了改變,希望讓其他更多的對象知道他的改變,從而實(shí)現(xiàn)其他對象隨之發(fā)生改變的一種信息傳遞手段
如何實(shí)現(xiàn)通知

使用系統(tǒng)發(fā)出的通知

//獲取通知中心
NSNotificationCenter * center = [NSNotificationCenter defaultCenter];
//給通知中心發(fā)通知
//name:通知的名稱
//object:誰發(fā)出的通知
//userInfo:通知的內(nèi)容
[center postNotificationName:@"Update" object:self userInfo:@{@"title":@"TBBT",
                                                            @"episode":@"第12集"}];
//注冊對"Update"通知的監(jiān)聽
//observer:誰想接收通知
//selector:用哪個方法響應(yīng)
//name:監(jiān)聽的通知叫什么名
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resiveNewNotifiaciton:) name:@"Update" object:nil];
//該方法會在接收到通知時自動調(diào)用
//參數(shù)就是系統(tǒng)封裝好的通知對象,通過此參數(shù)可以拿到
//發(fā)出通知中的userInfo信息
-(void)resiveNewNotifiaciton:(NSNotification *)notification{
    NSDictionary * dict = notification.userInfo;
    NSLog(@"%@更新到了%@",dict[@"title"],dict[@"episode"]);
}

聊天氣泡界面及功能

鍵盤彈起時,

使用約束設(shè)計(jì)Cell

創(chuàng)建高度不定的動態(tài)單元格

點(diǎn)擊右下角不關(guān)鍵盤——設(shè)置不要給文本框連線而是給文本框設(shè)置delegate,在當(dāng)前控制器中遵守UITextFIeldDelegate,實(shí)現(xiàn)shouldClickReturn……方法中不要調(diào)用resignFirstResponder

使用下滑手勢關(guān)閉鍵盤——給tableView添加一個Swipe手勢,設(shè)置該手勢的delegate為當(dāng)前控制器,遵守UIGestureRecoganizerDelegate協(xié)議,實(shí)現(xiàn)一個返回值是BOOL,xxxSimulatn的方法,返回YES

- (void)viewDidLoad {
    [super viewDidLoad];
    //為了讓tableView自適應(yīng)高度,需要設(shè)置如下兩個屬性
    self.tableView.estimatedRowHeight = 70;
    self.tableView.rowHeight = UITableViewAutomaticDimension;
    //設(shè)置tableView的內(nèi)邊距,向下錯64個點(diǎn)
    self.tableView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0);
}
//在view即將顯示時添加對系統(tǒng)發(fā)出的鍵盤通知的監(jiān)聽
-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    //為當(dāng)前控制器注冊鍵盤彈起和關(guān)閉通知
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(openKeyboard:) name:UIKeyboardWillShowNotification object:nil];
    
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(closeKeyboard:) name:UIKeyboardWillHideNotification object:nil];
}
//在view即將消失時取消鍵盤通知的監(jiān)聽
-(void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
//收到彈起鍵盤的通知后執(zhí)行
-(void)openKeyboard:(NSNotification *)notification{
    //讀取彈起的鍵盤的高度
    CGFloat keyboardHeight = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
    //讀取動畫的時長
    CGFloat duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]; 
    //讀取動畫的種類
    NSInteger option = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] intValue]; 
    //修改底部輸入視圖的bottom約束
    self.bottomLayoutConstraint.constant = keyboardHeight;    
    [UIView animateWithDuration:duration delay:0 options:option animations:^{
        [self.view layoutIfNeeded];
        //鍵盤彈起后,表視圖移動到最底部
        [self scrollToTableViewLastRow];
    } completion:nil];
}

//收到收起鍵盤的通知后執(zhí)行
-(void)closeKeyboard:(NSNotification *)notification{
    //讀取動畫的時長
    CGFloat duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];    
    //讀取動畫的種類
    NSInteger option = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] intValue];  
    //修改底部輸入視圖的bottom約束
    self.bottomLayoutConstraint.constant = 0;    
    [UIView animateWithDuration:duration delay:0 options:option animations:^{
        [self.view layoutIfNeeded];
    } completion:nil];
}
//控制表視圖滾動到最底部
-(void)scrollToTableViewLastRow{
    NSIndexPath * lastIndexPath = [NSIndexPath indexPathForRow:self.allMessage.count-1 inSection:0];
    [self.tableView scrollToRowAtIndexPath:lastIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
//界面顯示后,表格已經(jīng)生成了,在滾動到底部
-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    [self scrollToTableViewLastRow];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在iOS中隨處都可以看到絢麗的動畫效果,實(shí)現(xiàn)這些動畫的過程并不復(fù)雜,今天將帶大家一窺ios動畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,699評論 6 30
  • 在iOS中隨處都可以看到絢麗的動畫效果,實(shí)現(xiàn)這些動畫的過程并不復(fù)雜,今天將帶大家一窺iOS動畫全貌。在這里你可以看...
    F麥子閱讀 5,275評論 5 13
  • Core Animation基礎(chǔ) Core Animation 利用了硬件加速和架構(gòu)上的優(yōu)化來實(shí)現(xiàn)快速渲染和實(shí)時動...
    獨(dú)木舟的木閱讀 1,716評論 0 3
  • Core Animation其實(shí)是一個令人誤解的命名。你可能認(rèn)為它只是用來做動畫的,但實(shí)際上它是從一個叫做Laye...
    小貓仔閱讀 3,981評論 1 4
  • 在iOS實(shí)際開發(fā)中常用的動畫無非是以下四種:UIView動畫,核心動畫,幀動畫,自定義轉(zhuǎn)場動畫。 1.UIView...
    請叫我周小帥閱讀 3,339評論 1 23

友情鏈接更多精彩內(nèi)容