iOS Animation: 玩轉貝塞爾曲線之KYAnimatePageControl

讀書筆記

先看效果

2017-08-17 18_31_25.gif

源碼鏈接:https://github.com/LeeLom/AnimationSample

這節(jié)內(nèi)容主要是利用貝塞爾曲線來模擬一個球產(chǎn)生形變的過程。利用四條曲線來拼接出完整的球體。


圖1.png

所以,我們畫出這個球體的步驟,實際上也就是確定多邊形Ac1c2Bc3c4Cc5c6Dc7c8的過程,也即是確定各個點坐標的過程。我們得到這個多邊形后,利用貝塞爾曲線去逼近,既可以得到我們所需要的形變的圖像。

書中給出的確定多邊形的方法如下:

  • 確定外接正方形的位置:得到(origin_x, origin_y)
  • 確定多邊形各個坐標的:
    • 確定多邊形的坐標的過程中,我們需要根據(jù)每次滑動的值,來確定offsetmovedDistance。這兩個的計算公式可以參考文中的表達式。
    CGFloat offset = _outsideRect.size.width / 3.6;
    CGFloat movedDistance = (_outsideRect.size.width * 1 / 6) * fabs(_progress - 0.5) * 2;
  • 確定各個點的坐標:
    • A點:橫坐標永遠與外接多邊形相同,縱坐標為正方形的縱坐標加上移動的距離
    • B點:縱坐標和外接多邊形相同,橫坐標通過判斷左右移動確定。像左邊移動,則為外接多邊形的橫坐標與正方形邊長一半,移動距離的兩倍求和的值;像右邊移動,則為外接多邊形的橫坐標與正方形邊長一半的和。

其余點類似,這里不做過多解釋。如果不太理解,其實可以講progress分別設置成為不同的值進行查看即可。

  • 接著我們首先通過 (origin_x, orgin_y)畫出外接正方形。 邊長設置為定值
  • 使用Ac1c2Bc3c4Cc5c6Dc7c8畫出外接多邊形,同時利用貝塞爾曲線去逼近這個多邊形
  • 書中為了方便我們便捷,還將這12個點分別也畫了出來。

以上,就是書中本節(jié)的內(nèi)容,最本質(zhì)的就是不斷的根據(jù)滑動條的值去更新正方形的位置,從而調(diào)整外接多邊形的形狀,進而得到模擬出的貝塞爾曲線。


補充知識點

關于這節(jié)內(nèi)容,在代碼實現(xiàn)的過程中,發(fā)現(xiàn)了不少知識盲區(qū),網(wǎng)上查找了不少資料進行了一個整理:

關于initWithFrameinitWithCoder的區(qū)別

  • CircleView.m 中,初始化方法使用的是initWithFrame:,那么initWithCoder與它有什么區(qū)別?
    initWithFrame適用于代碼創(chuàng)建頁面的時候使用;
    initWithCoder適用于xib、storyboard創(chuàng)建的時候使用。與initWithCoder類似的還有一個是awakeFromNib, 該方法在initWithCoder:方法后調(diào)用,順序是initWithCoder: --> awakeFromNib
    PS1. OC類的初始化方法
    • 普通類初始化:init,initialize
    • 控制器類的初始化方法:init, initialize,initWithCoder:
    • UI空間類的初始化方法:init,initialize, initWithFrame: ,initWithCoder:

PS2. Stack Overflow上看到一個比較有意思的問題,跟Runloop結合的。
ViewController.m

@implementation TestViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    View1 *view1 = [[View1 alloc] init];
    [self.view addSubview:view1];
}
@end

View1.m

@implementation View1

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        NSLog(@"initWithFrame");
    }
    return self;
}

- (id)init {
    self = [super init];
    if (self) {
        NSLog(@"init");
    }
    return self;
}

@end

控制臺的輸出結果如下:

2013-10-17 12:33:46.209 test1[8422:60b] initWithFrame
2013-10-17 12:33:46.211 test1[8422:60b] init

分析一下控制臺出現(xiàn)的原因:

  - [view1(View1) init] 

      - [view1(UIView) init] (called by [super init] inside View1)

        - [view1(View1) initWithFrame:CGRectZero] (called inside [view(UIView) init] )

           - [view1(UIView) initWithFrame:CGRectZero] (called by [super initWithFrame] inside View1) 
              - ...

           - NSLog(@"initWithFrame"); (prints "test1[8422:60b] initWithFrame")

      - NSLog(@"init"); (called inside [view1(View1) init] ; prints "test1[8422:60b] init")

關于UIViewCALayer的區(qū)別

  • UIView可以響應事件,CALayer不可以。UIKit使用UIResponder作為響應對象,來響應系統(tǒng)傳遞過來的事件進行處理,UIView作為UIResponder的響應對象,因此可以處理。
    處理事件的函數(shù)。
– touchesBegan:withEvent:
– touchesMoved:withEvent:
– touchesEnded:withEvent:
– touchesCancelled:withEvent:
  • 每個UIView內(nèi)部都有一個CALayer在背后提供內(nèi)容的繪制和顯示,并且UIView的尺寸樣式都是由內(nèi)部的Layer提供。兩者都有樹狀層級結構,layer內(nèi)部有subLayers, view內(nèi)部有subViews。
  • View在顯示的時候, UIView作為layer的CALayerDelegate,View的顯示內(nèi)容由內(nèi)部的CALayer的display
  • CALayer是默認修改屬性支持隱式動畫,在給UIView的Layer做動畫的時候,View作為Layer的代理,Layer通過actionForLayer:forKey:向View請求響應的action(動畫行為)
  • layer 內(nèi)部維護著三分 layer tree,分別是 presentLayer Tree(動畫樹),modeLayer Tree(模型樹), Render Tree (渲染樹),在做 iOS動畫的時候,我們修改動畫的屬性,在動畫的其實是 Layer 的 presentLayer的屬性值,而最終展示在界面上的其實是提供 View的modelLayer

關于繪圖的一些函數(shù)

  • 繪制路徑的相關函數(shù)
    繪制路徑:制定起始點到另一個點,然后兩個點之間可以使用直線或者曲線來連接,就形成了一條路徑,例如使用下面的函來實現(xiàn)
    CGContextMoveToPoint()//指定起始點或者移動到新的點    
    CGContextAddLineToPoint()//從當前的點到制定的點之間畫一條線
    CGContextAddArc()//圓弧
    CGContextAddArcToPoint()//會在當前點和指定點與坐標系平行的直線做切線畫圓弧
    CGContextAddCurveToPoint()//畫曲線
    CGContextClosePath()//將起始點和結束點連接起來形成封閉的路徑
    CGContextAddEllipseInRect()//畫橢圓
    
  • 設置路徑的相關屬性
    設置需要繪制的路徑的屬性。設置顏色、透明度、寬度、繪制模式...例如用下面的函數(shù)來實現(xiàn)
    CGContextSetLineWidth()//線寬度
    CGContextSetLineJoin()//線連接點的樣式
    CGContextSetLineCap()//端點的樣式
    CGContextSetLineDash()//虛線
    CGContextSetStrokeColorWithColor()//stroke模式時的顏色
    CGContextSetFillColorWithColor()//fill模式時的顏色
    
  • 繪制內(nèi)容的兩種方式 fillStroke
    stroke: 描邊,只繪制路徑
    fill:繪制路徑包括的所有區(qū)域(所有路徑都會被當做closePath處理)
    fill的兩種模式:
    (1)non zero widing number(默認):如果兩個路徑有重疊的時候,繪制方向相同的話,那么重疊部分的繪制可能不是我們希望的
    (2)even-odd:不受繪制方向的影響
    (3)CGContextEOFillPath
    (4)CGContextFillPath
    (5)CGContextFillRect
    (6)CGContextFillRexts
    (7)CGContextFillEllipseInRect
    CGContextStrokePath(context);
    CGContextFillPath(context);
    
  • 兩個小例子
/**
 * 示例:stroke模式繪制線
 */
override func draw(rect: CGRext) {
      //獲取上下文
      let context = UIGraphicsGetCurrentContext();
      //設置stroke模式的顏色使用CGColor(這里涉及顏色和顏色空間的概念)
      let strokeColor = UIColor.blueColor();
      CGContextSetStrokeColorWIthColor(context, strokeColor.CGColor);
      //設置線的寬度
      CGContextSetLineWidth(context, 5.0);
      //設置連接處樣式
      CGContextSetLineJoin(context, CGLineJoin.Round);
      //起始點
      CGContextMoveToPoint(context, 10.0, 10.0);
      //在兩個點之間畫一條線,所以當前的結束點成為了下一個的起始點
      CGContextAddLineToPoint(context, 10.0, 80.0);
      //繼續(xù)在兩個點之間畫一條線
      CGContextAddLineToPoint(context, 80.0, 80.0);
      //設置為封閉路徑,將收尾連接起來
      CGContextClosePath(context);
      //繪制當前路徑
      CGContextStrokePath(context);
}
/**
 * 示例:fill模式繪制線
 */
override func draw(rect: CGRext) {
      //獲取上下文
      let context = UIGraphicsGetCurrentContext();
      //設置stroke模式的顏色使用CGColor(這里涉及顏色和顏色空間的概念)
      let fillColor = UIColor.blueColor();
      CGContextSetFillColorWIthColor(context, fillColor.CGColor);
      //設置線的寬度
      CGContextSetLineWidth(context, 5.0);
      //設置連接處樣式
      CGContextSetLineJoin(context, CGLineJoin.Round);
      //起始點
      CGContextMoveToPoint(context, 10.0, 10.0);
      //在兩個點之間畫一條線,所以當前的結束點成為了下一個的起始點
      CGContextAddLineToPoint(context, 10.0, 80.0);
      //繼續(xù)在兩個點之間畫一條線
      CGContextAddLineToPoint(context, 80.0, 80.0);
      // 設置為封閉路徑,將收尾連接起來
      // CGContextClosePath(context);
      //繪制當前路徑
      CGContextFillPath(context);
}
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

  • 轉載:http://www.itdecent.cn/p/32fcadd12108 每個UIView有一個伙伴稱為l...
    F麥子閱讀 6,567評論 0 13
  • 每個UIView有一個伙伴稱為layer,一個CALayer。UIView實際上并沒有把自己畫到屏幕上;它繪制本身...
    shenzhenboy閱讀 3,252評論 0 17
  • 在iOS中隨處都可以看到絢麗的動畫效果,實現(xiàn)這些動畫的過程并不復雜,今天將帶大家一窺ios動畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,688評論 6 30
  • 前言 本文只要描述了iOS中的Core Animation(核心動畫:隱式動畫、顯示動畫)、貝塞爾曲線、UIVie...
    GitHubPorter閱讀 3,736評論 7 11
  • 我聽說有個年輕人,他走過很遠的路,頭上掛著露珠,衣袖帶著花香,鞋上粘著草葉 ,風塵又仆仆。夜色尚濃,星星還在眨...
    又見北極星閱讀 384評論 0 0

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