iOS自定義控件教程(三)UIView動畫入門

iOS自定義控件教程(一)UIKit入門,布局入門
iOS自定義控件教程(二)響應(yīng)鏈原理
iOS自定義控件教程(三)觸摸事件和簡單動畫
iOS自定義控件教程(四)Target-Action響應(yīng)模式

上一篇文章我們介紹了UIView的觸摸響應(yīng)鏈原理歼捏,順便學(xué)習(xí)UIView的基本屬性和方法。在iOS自定義控件教程(二)關(guān)于響應(yīng)鏈的那些事中我們講解了觸摸原理栅贴,但并未具體實現(xiàn)其功能,接下來我們具體講講點擊效果的實現(xiàn)和響應(yīng)的動畫效果的實現(xiàn)熏迹。
最終實現(xiàn)的效果:Github下載源碼

觸摸響應(yīng)鏈UIResponder

UIView繼承自UIResponder(響應(yīng)鏈類)筹误,繼承了相應(yīng)的響應(yīng)鏈方法:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(nullable NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEstimatedPropertiesUpdated:(NSSet * _Nonnull)touches NS_AVAILABLE_IOS(9_1);

// Generally, all responders which do custom press handling should override all four of these methods.
// Your responder will receive either pressesEnded:withEvent or pressesCancelled:withEvent: for each
// press it is handling (those presses it received in pressesBegan:withEvent:).
// pressesChanged:withEvent: will be invoked for presses that provide an analog value
// (like thumbsticks or analog push buttons)
// *** You must handle cancelled presses to ensure correct behavior in your application.  Failure to
// do so is very likely to lead to incorrect behavior or crashes.
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);

- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);

其中touches開頭的方法是觸摸事件相關(guān)的方法;presses開頭的方法癣缅,是iOS9加入的給iPhone6s等支持Deep Press功能的設(shè)備使用的相關(guān)方法厨剪;motion開頭的則是給設(shè)備的陀螺儀和加速傳感器使用的方法哄酝,用于獲取晃動等事件。

細(xì)心的朋友可能會發(fā)現(xiàn)祷膳,UIViewController(后面簡稱VC) 和 UIView 同樣繼承自UIResponder陶衅,這樣是為了方便UIViewController 處理他的view屬性的響應(yīng)事件,我們也就不用繼承UIView重寫他的響應(yīng)鏈來處理VCView的響應(yīng)鏈了直晨,VCView默認(rèn)將響應(yīng)鏈穿給自己的VC搀军,在VC中處理就可以了。

最終效果

觸摸事件

觸摸事件可以通過觸摸鏈響應(yīng)勇皇,也可以使用UIGesture(手勢類)來響應(yīng)罩句。今天我們的例子中用了比較原始的響應(yīng)鏈方式。

XXXSegmentView.m

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];
    
    UITouch* touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    NSInteger detla = self.frame.size.width / self.number;
    NSInteger touchNumber = point.x / detla;
    self.selected = touchNumber;
//    [self setNeedsLayout];
    [self setSelectItemAtIndex:touchNumber animate:YES];
}

解釋 touchesBegan是手指按下事件的方法敛摘,這里按下我們不做響應(yīng)门烂,單寫了super方法調(diào)用父類方法,還是那句話兄淫,父類方法可能是空的不需要調(diào)用屯远,單這是一個好習(xí)慣,可以避免一些因為繼承類不調(diào)用父類方法造成的BUG捕虽。

touchesEnded是手指抬起事件的方法慨丐,首先touches這個集合中的UITouch對象,對應(yīng)的就是觸摸的手指泄私。我們用anyObject取出其中之一房揭,用locationInView:方法獲取觸摸點的位置。這個CGPoint是一個C中的結(jié)構(gòu)體類型晌端,只有x,y兩個屬性表示位置捅暴。

接下來我們根據(jù)Label的數(shù)量(self.number)來計算每個label的間隔,用總寬度除以個數(shù)斩松。

NSInteger detla = self.frame.size.width / self.number;

接下來計算我們觸摸的位置,在第幾個區(qū)域

NSInteger touchNumber = point.x / detla;

這個touchNumber便是我們觸摸的按鈕的index觉既。

接下來我們調(diào)用setSelectItemAtIndex方法來響應(yīng)觸摸這個Label的事件

- (void)setSelectItemAtIndex:(NSUInteger)idx animate:(BOOL)animated
{
    self.idx = idx;
    [self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if ([obj isKindOfClass:[UILabel class]]) {
            UILabel* label = obj;
            if (self.selected + 981 == label.tag) {
                label.textColor = self.tintColor;
            }
            else
            {
                label.textColor = self.baseColor;
            }

        }
    }];
    
//這里是我們下方的那根線惧盹,移動的動畫,下面詳細(xì)說
    [UIView animateWithDuration:0.2 animations:^{
        UILabel* label = (UILabel*)[self viewWithTag:idx + 981];
        [self.selectView setFrame:CGRectMake(label.frame.origin.x - self.leftAndRightLineEdge, label.frame.size.height + label.frame.origin.y   + 10, label.frame.size.width + self.leftAndRightLineEdge*2, 2)];
    }];
}

解釋 每一次觸摸瞪讼,我們都需要遍歷所有的Label钧椰,將選中的,也就是self.selected + 981 == label.tag的Label改變他的顏色符欠。

UIView動畫基礎(chǔ)

我們在上面代碼中嫡霞,調(diào)用self.selectView這個屬性,是這么定義的

@interface

@property (nonatomic, strong) UIView* selectView;

@implementation

- (UIView *)selectView
{
    if (!_selectView) {
        _selectView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 2)];
        [self addSubview:_selectView];
        _selectView.backgroundColor = self.tintColor;
    }
    return _selectView;
}

這個View在第一次調(diào)用self.selectView的時候會初始化希柿,第二次調(diào)用時候诊沪,_selectView已經(jīng)初始化過了养筒,就不會再執(zhí)行初始化方法了。

注意端姚,關(guān)于getter和setter的內(nèi)容晕粪,我們就不細(xì)說了,新手只要知道渐裸,當(dāng)前的版本的編譯器巫湘,自動為@property添加了@synthesize(合成)語句,所以iOS6以后昏鹃,代碼中很少見到

@synthesize selectView = _selectView;

這樣的寫法了尚氛,因為編譯器自動幫你做了。所以_selectView是內(nèi)部變量洞渤,單不要直接這樣調(diào)用阅嘶,而應(yīng)該在除了- (UIView *)selectView這個getter方法外的地方,使用self.selectView來調(diào)用您宪。

我們再來看看上面觸摸代碼中的動畫代碼:

  [UIView animateWithDuration:0.2 animations:^{
        UILabel* label = (UILabel*)[self viewWithTag:idx + 981];
        [self.selectView setFrame:CGRectMake(label.frame.origin.x - self.leftAndRightLineEdge, label.frame.size.height + label.frame.origin.y   + 10, label.frame.size.width + self.leftAndRightLineEdge*2, 2)];
    }];

解釋 先使用viewWithTag:方法獲取Label奈懒,注意這里返回的是UIView類,所以需要使用(UILabel*)進行強行類型轉(zhuǎn)換宪巨。

UIView animateWithDuration:animations:這個方法磷杏,是iOS4.0加入的簡化版的補幀動畫方法。在這個block中去改變一個UIView的frame捏卓,系統(tǒng)則會根據(jù)傳入的Duration(單位:秒)极祸,來自動完成補間動畫,類似于Flash中的補間動畫怠晴。

如果不在animateWithDuration中改變view的frame遥金,view則會直接變?yōu)樾碌膄rame,則不會有中間改變的動畫過程了蒜田,是不是很簡單稿械。

最后self.leftAndRightLineEdge這是一個CGFloat浮點型數(shù)值,用來控制下面線兩端超出Label的長度冲粤,我們demo中第二個例子中這個數(shù)值為20美莫。

這樣,我們的最終效果就完成梯捕。

最終效果

下一篇教程厢呵,我們將著手分析UIView控件響應(yīng)事件并傳遞給其他VC或View的具體實現(xiàn)方法,有什么疑問請在下方留言傀顾。

iOS自定義控件教程(一)UIKit入門襟铭,布局入門
iOS自定義控件教程(二)響應(yīng)鏈原理
iOS自定義控件教程(三)觸摸事件和簡單動畫
iOS自定義控件教程(四)Target-Action響應(yīng)模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子寒砖,更是在濱河造成了極大的恐慌赐劣,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件入撒,死亡現(xiàn)場離奇詭異隆豹,居然都是意外死亡,警方通過查閱死者的電腦和手機茅逮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門璃赡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人献雅,你說我怎么就攤上這事碉考。” “怎么了挺身?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵侯谁,是天一觀的道長。 經(jīng)常有香客問我章钾,道長墙贱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任贱傀,我火速辦了婚禮惨撇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘府寒。我一直安慰自己魁衙,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布株搔。 她就那樣靜靜地躺著剖淀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纤房。 梳的紋絲不亂的頭發(fā)上纵隔,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天,我揣著相機與錄音炮姨,去河邊找鬼捌刮。 笑死,一個胖子當(dāng)著我的面吹牛剑令,可吹牛的內(nèi)容都是我干的糊啡。 我是一名探鬼主播拄查,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼吁津,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起碍脏,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤梭依,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后典尾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體役拴,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年钾埂,在試婚紗的時候發(fā)現(xiàn)自己被綠了河闰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡褥紫,死狀恐怖姜性,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情髓考,我是刑警寧澤部念,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站氨菇,受9級特大地震影響儡炼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜查蓉,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一乌询、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奶是,春花似錦楣责、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至及汉,卻和暖如春沮趣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坷随。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工房铭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人温眉。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓缸匪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親类溢。 傳聞我的和親對象是個殘疾皇子凌蔬,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容