iOS 實現(xiàn) readme 登錄頁面貓頭鷹動畫

readme.io

前言

注冊、登錄功能是應(yīng)用標(biāo)識用戶身份翁都、記錄并保存用戶數(shù)據(jù)碍论、提供個性化服務(wù)的重要方法谅猾,但是對于用戶來說柄慰,看到復(fù)雜的登錄鳍悠、注冊表單就已經(jīng)讓人厭惡至極了,還要去亂成一團(tuán)的腦海中提取出相應(yīng)的賬號密碼坐搔,最后還要正確無誤地在鍵盤上一個一個地輸入字母藏研。。概行。是不是要生無可戀到?jīng)]有脾氣了蠢挡,感覺人生已經(jīng)走到崩潰懸崖的邊緣了呢? readme 在登錄和注冊中恰到好處地加入了動畫凳忙,這種人性化的設(shè)計讓人眼前一亮业踏,如果嘗試多點幾下觸發(fā)動畫,感覺可以玩一整天啊涧卵。

輸入 「Email 」時攤開雙手勤家,輸入「Password」時遮住眼睛在無形中傳達(dá)了:

  1. 用遮眼、睜眼的動畫巧妙地表達(dá)了:「Email」是明文輸入柳恐,而「Password」是密文輸入伐脖;
  2. 密碼輸入的安全性,貓頭鷹遮住眼睛好像在提示用戶:輸入密碼時要注意防止他人偷窺以竊取你的賬號和密碼乐设,如果此時有小伙伴在邊上讼庇,看到這個動畫,他也應(yīng)該明白:這時候我需要回避一下近尚。
  3. 向使用者傳達(dá):服務(wù)提供方非常注重用戶隱私蠕啄,用戶輸入的密碼服務(wù)提供方是不會隨意偷看的;
  4. 可愛肿男、簡潔而有趣的貓頭鷹動畫減輕并降低了用戶輸入賬號密碼時的反感和焦慮介汹,也傳達(dá)了服務(wù)提供方非常具有創(chuàng)意,對產(chǎn)品及其注重人性化體驗舶沛。
readme.io.gif

動畫分解

將貓頭鷹動畫錄屏嘹承,并提取出關(guān)鍵幀圖片,查看手指的變化:

分解動畫

說明:

  • 兩手抓起的圖片為【放下時的左右手】
  • 遮住眼睛的圖片為【抬起時的左右手】

觀察分解的圖片如庭,從打開雙手狀態(tài) -> 遮住眼睛狀態(tài):

  • 【放下時的左右手】初始化時位置分別在貓頭鷹頭像圖片左右兩側(cè)叹卷,正常大小。動畫開始后坪它,向右平移的同時逐漸縮小為0骤竹;

  • 【抬起時的左右手】初始化時位置在【放下時的左右手】圖片的中心點位置,但是大小為 0往毡。動畫開始后蒙揣,向右平移的同時放大至正常大小并遮住眼睛;

  • 注意到开瞭,雙手遮住眼睛后懒震,還有一張【眼睛圖片】會覆蓋住眼睛白色的部分罩息,

    眼睛圖片并不是簡單的設(shè)置隱藏/顯示:self.owlEyeImgView.hidden = YES; 來添加的。注意到第四張分解圖片个扰,貓頭鷹的白色部分是有一點點灰色漸變的瓷炮,在動畫中可以通過設(shè)置 alpha 值來設(shè)置眼睛圖片。

遮住眼睛狀態(tài)->打開雙手狀態(tài)的動畫則與上所述相反递宅。

準(zhǔn)備素材

readme 登錄頁面下載圖片素材:

Safari 瀏覽器 - 開發(fā) - 顯示頁面資源 - 打開圖像并下載:

核心方法

設(shè)置一個枚舉類型表示貓頭鷹當(dāng)前的動畫狀態(tài):

// 貓頭鷹動畫
typedef NS_ENUM(NSUInteger, RMIOLoginViewOwlAnimationState) {
    RMIOLoginViewOwlAnimationStateDefaule, // 默認(rèn)初始狀態(tài)
    RMIOLoginViewOwlAnimationStateDown,    // 睜眼狀態(tài)(輸入用戶名時)
    RMIOLoginViewOwlAnimationStateUp,      // 遮眼狀態(tài)(輸入密碼時)
};

我將抬手動畫和打開雙手的兩個動畫分別封裝在兩個方法中娘香,方便復(fù)用:

抬起左右手遮眼動畫方法

// 抬起左右手動畫:打開雙手狀態(tài) -> 遮住眼睛狀態(tài)
- (void)armUpImageAnimation {
    
    [UIView animateWithDuration:KOwlAnimationDuration animations:^{
        
        // 眼睛圖片 alpha 值從0變?yōu)?
        self.owlEyeImgView.alpha = 1;
        
        // 【放下時的左右手】向右平移并縮小到0
        CGRect armDownLeftRect = CGRectMake(_faceRect.origin.x +29, 148, 0, 0);
        self.armDownLeftImgView.frame = armDownLeftRect;
        CGRect armDownRightRect = CGRectMake(CGRectGetMaxX(_faceRect) - 29, 148, 0, 0);
        self.armDownRightImgView.frame = armDownRightRect;
        
        // 抬起時的左右手】向右平移并放大
        CGRect armUpLeftRect = CGRectMake(_faceRect.origin.x - 6, 108, 51, 42);
        self.armUpLeftImgView.frame = armUpLeftRect;
        CGRect armUpRightRect = CGRectMake(_faceRect.origin.x + 60, 107, 51, 43);
        self.armUpRightImgView.frame = armUpRightRect;
    }];
}

放下左右手睜眼動畫方法

// 放下左右手動畫:遮住眼睛狀態(tài)->打開雙手狀態(tài)
- (void)armDownImageAnimation {
    [UIView animateWithDuration:KOwlAnimationDuration animations:^{
        
        // 眼睛圖片 alpha 值從1變?yōu)?
        self.owlEyeImgView.alpha = 0;
        
        // 【放下時的左右手】向左平移還原
        CGRect armDownLeftRect = CGRectMake(_faceRect.origin.x - 36, 134, 43, 25);
        self.armDownLeftImgView.frame = armDownLeftRect;
        CGRect armDownRightRect = CGRectMake(CGRectGetMaxX(_faceRect), 134, 43, 26);
        self.armDownRightImgView.frame = armDownRightRect;
        
        // 抬起時的左右手】向左平移并縮小到0
        CGRect armUpLeftRect = CGRectMake(_faceRect.origin.x - 15, 150, 0, 0);
        self.armUpLeftImgView.frame = armUpLeftRect;
        CGRect armUpRightRect = CGRectMake(CGRectGetMaxX(_faceRect) + 15, 150, 0, 0);
        self.armUpRightImgView.frame = armUpRightRect;
    }];
}

需要實現(xiàn) UITextFieldDelegate 部分協(xié)議以調(diào)用動畫方法:

#pragma mark - UITextFieldDelegate

// 開始編輯
- (void)textFieldDidBeginEditing:(UITextField *)textField {
    // 1.開始輸入用戶名
    if (textField.tag == KUsernameTextFieldTag) {
        switch (self.owlAnimationState) {
            case RMIOLoginViewOwlAnimationStateDefaule: {
                self.owlAnimationState = RMIOLoginViewOwlAnimationStateDown;
                break;
            }
            case RMIOLoginViewOwlAnimationStateDown: {
                break;
            }
            case RMIOLoginViewOwlAnimationStateUp: {
                self.owlAnimationState = RMIOLoginViewOwlAnimationStateDown;
                [self armDownImageAnimation];
                break;
            }
        }
    }
    
    // 2.開始輸入密碼
    if (textField.tag == KPasswordTextFieldTag) {
        switch (self.owlAnimationState) {
            case RMIOLoginViewOwlAnimationStateDefaule:
            case RMIOLoginViewOwlAnimationStateDown: {
                self.owlAnimationState = RMIOLoginViewOwlAnimationStateUp;
                [self armUpImageAnimation];
                break;
            }
            case RMIOLoginViewOwlAnimationStateUp: {
                break;
            }
        }
    }
}

/*
 結(jié)束編輯
 每當(dāng)密碼輸入結(jié)束編輯時,需要打開雙手办龄。
 遮住眼睛->打開雙手:
 * 密碼輸入時烘绽,返回到賬號輸入,打開雙手俐填。
 * 密碼輸入時诀姚,點擊空白部分或點擊登錄,打開雙手玷禽。
 
 */
- (void)textFieldDidEndEditing:(UITextField *)textField {
    if (textField.tag == KPasswordTextFieldTag) {
        if (self.owlAnimationState == RMIOLoginViewOwlAnimationStateUp) {
            self.owlAnimationState = RMIOLoginViewOwlAnimationStateDown;
            [self armDownImageAnimation];
        }
    }
}

// 用戶點擊鍵盤"Return"按鈕
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [self endEditing:YES];
    return YES;
}

看看實現(xiàn)效果如何:

bingo赫段!I Do It!??????


iOS 實現(xiàn)效果圖.gif

放慢10倍速度看看:

放慢動畫.gif

源碼已上傳 GitHub

參考

君子性非異也矢赁,善假于物也——荀子

參考了各位大佬的Demo源碼糯笙,還有各種資料:

關(guān)于像素對齊

我在設(shè)置貓頭鷹圖片 Frame 時的方法:

self.faceRect = CGRectMake(CGFloatPixelRound((kScreenWidth - 116) / 2), 69, 116, 92); //第 203 行

// 【原因解析】
// 在 iPhone 7 (尺寸為 375*667)上的值為:(129.5,69,116,92),因為 Scale = 2抄邀,所以實際像素值為(259耘眨,138,232境肾,184)剔难,像素對齊
// 在 iPhone X (尺寸為 375*812)上的值為(129.666667,69.000000,116.000000,92.000000),因為 Scale = 3 ,所以實際像素值為 (389奥喻,207偶宫,348,276)环鲤,像素對齊

//-------------------------
// 如果不使用像素對齊函數(shù)纯趋,那么
// 在 iPhone 7 上的值為(129.5,69,116,92),實際像素值為:(259,138吵冒,232唇兑,184),像素對齊
// 在 iPhone X 上的值為(129.5,69,116,92)桦锄,實際像素值為:(388.5,207蔫耽,348结耀,276),像素沒有對齊??

// 【總結(jié)】
// 設(shè)置 CGRect 值的時候盡量小心匙铡,特別是如果你用到了除法(如 KScreenSize / 5)图甜,可能會導(dǎo)致最終取出來的值不是整數(shù),
// 就會造成像素不對齊鳖眼,就會增加 CPU/GPU 的消耗黑毅,會影響性能或者說頁面不會以 60 FPS 進(jìn)行渲染,造成頁面卡頓钦讳。

其中有一個 CGFloatPixelRound() 函數(shù)可以設(shè)置像素對齊值(CGFloat)矿瘦,來自于 YYKit 框架的 YYCGUtilities.h 類中,里面還有其他像設(shè)置像素對齊點(CGPoint)愿卒、像素對齊大懈咳ァ(CGSize)、像素對齊矩形(CGRect)等方法:

// 像素對齊值
CGFloatPixelFloor(CGFloat value)
CGFloatPixelRound(CGFloat value)
CGFloatPixelCeil(CGFloat value)
CGFloatPixelHalf(CGFloat value)

// 像素對齊點
CGPointPixelFloor(CGPoint point)
CGPointPixelRound(CGPoint point)
CGPointPixelCeil(CGPoint point)
CGPointPixelHalf(CGPoint point)

// 像素對齊大小
CGSizePixelFloor(CGSize size)
CGSizePixelRound(CGSize size)
CGSizePixelCeil(CGSize size)
CGSizePixelHalf(CGSize size)

// 像素對齊矩形
CGRectPixelFloor(CGRect rect)
CGRectPixelRound(CGRect rect)
CGRectPixelCeil(CGRect rect)
CGRectPixelHalf(CGRect rect)

// 像素對齊邊緣插入量
UIEdgeInsetPixelFloor(UIEdgeInsets insets)
UIEdgeInsetPixelCeil(UIEdgeInsets insets)

其中底層調(diào)用的 Floor()琼开、Round()易结、Ceil() 分別是數(shù)學(xué)函數(shù):

  • floor() 取不大于傳入值的最大整數(shù)
  • round() 四舍五入
  • ceil() 返回大于或者等于指定表達(dá)式的最小整數(shù)

示例:

scale = 2.000000 時:

"CGFloatPixelCeil(2.1)" = 5;
"CGFloatPixelCeil(2.25)" = 5;
"CGFloatPixelCeil(2.45)" = 5;
"CGFloatPixelFloor(2.1)" = 4;
"CGFloatPixelFloor(2.25)" = 4;
"CGFloatPixelFloor(2.45)" = 4;
"CGFloatPixelHalf(2.1)" = "4.5";
"CGFloatPixelHalf(2.25)" = "4.5";
"CGFloatPixelHalf(2.45)" = "4.5";
"CGFloatPixelRound(2.1)" = 4;
"CGFloatPixelRound(2.25)" = 5;
"CGFloatPixelRound(2.45)" = 5;

最后的話

興趣是最好的老師,寫代碼可能是一件枯燥的事柜候「愣看到這個動畫的第一眼就是:哇!好酷渣刷!如果在 iOS 上實現(xiàn)這個動畫應(yīng)該會很好玩吧鹦肿!

所以做這個Demo的時候可以一直盯著電腦研究搗鼓好久,就連吃飯辅柴、走路的時候也會想著初始化時各個圖片應(yīng)該放在哪里狮惜?如何正確獲取及調(diào)整圖片的位置呢?應(yīng)該是什么狀態(tài)的碌识?動畫的路徑是如何進(jìn)行的呢碾篡?觸摸點擊事件的邏輯是怎樣的呢?所以實現(xiàn)起來一點也不費力筏餐,做完還會有滿滿的成就感开泽。。魁瞪。

So穆律,愿永遠(yuǎn)年輕惠呼,永遠(yuǎn)熱淚盈眶。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末峦耘,一起剝皮案震驚了整個濱河市剔蹋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辅髓,老刑警劉巖泣崩,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異洛口,居然都是意外死亡矫付,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進(jìn)店門第焰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來买优,“玉大人,你說我怎么就攤上這事挺举∩庇” “怎么了?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵湘纵,是天一觀的道長葵陵。 經(jīng)常有香客問我,道長瞻佛,這世上最難降的妖魔是什么脱篙? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮伤柄,結(jié)果婚禮上绊困,老公的妹妹穿的比我還像新娘。我一直安慰自己适刀,他們只是感情好秤朗,可當(dāng)我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著笔喉,像睡著了一般取视。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上常挚,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天作谭,我揣著相機(jī)與錄音,去河邊找鬼奄毡。 笑死折欠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播锐秦,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼咪奖,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了酱床?” 一聲冷哼從身側(cè)響起羊赵,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扇谣,沒想到半個月后昧捷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡揍堕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了汤纸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衩茸。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖贮泞,靈堂內(nèi)的尸體忽然破棺而出楞慈,到底是詐尸還是另有隱情,我是刑警寧澤啃擦,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布囊蓝,位于F島的核電站,受9級特大地震影響令蛉,放射性物質(zhì)發(fā)生泄漏聚霜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一珠叔、第九天 我趴在偏房一處隱蔽的房頂上張望蝎宇。 院中可真熱鬧,春花似錦祷安、人聲如沸姥芥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凉唐。三九已至,卻和暖如春霍骄,著一層夾襖步出監(jiān)牢的瞬間台囱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工读整, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留玄坦,地道東北人。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像煎楣,于是被迫代替她去往敵國和親豺总。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,922評論 2 361