一句代碼添加新浪彈出框動畫

2021-01-17 iOS開發

版權申明:作者 畢洪博 已將本文在微信公眾平臺的發表權「獨家代理」給 iOS 開發(iOSDevTip)微信公共帳號。本文所有打賞將歸原作者所有。

新浪微博中的「加號按鈕」點擊後的彈出動畫很有意思,每當一個人孤單寂寞冷的時候總會不停的點這個動畫,終於忍不住自己擼了一個。廢話不多說,直接上效果圖:


首先說一下通過這個動畫製作過程給大家分享的技術問題:
1.背景的毛玻璃效果
2.彈性動畫
3.動畫進行時對用戶交互的處理
4.UIControl 的 event 類型

一、背景的毛玻璃效果

對新浪微博中彈出動畫背景的思路分析:

新浪的背景動畫效果是有一個透明漸變的過程,並且最終漸變停止之後顯示的是一個帶有毛玻璃(半透明模糊)效果的視圖。我的模仿思路是當準備要彈出動畫的時候對整個視圖進行截屏操作,將截屏後的圖片進行毛玻璃效果渲染,然後在視圖上加一個背景 UIImageView 來顯示這張圖片,然後通過 alpha 動畫來由透明逐漸漸變出來。

1.對視圖進行截屏:

UIGraphics BeginImageContextWithOptions(size, NO, scale); [view.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage * image = UIGraphicsGetImageFromCurrentImageContext();

2.截屏後加上毛玻璃效果:

製作毛玻璃效果有三種選擇,一是 iOS8 後推出的 UIBlurEffect 和 UIVisualEffectView 來直接顯示一個帶有毛玻璃效果的 View 不過這種方式並不能直接生成一張帶有毛玻璃效果的圖片,而且它的模糊程度設置方法非常有限只有那幾個枚舉類型,無法滿足需求。

第二種是通過 CoreImage 添加濾鏡的方式來實現毛玻璃效果,不過這種方式有個缺點如果濾鏡使用頻繁會對主線程產生影響,如果我不斷頻繁的重複動畫效果就必須要做判斷看濾鏡是否正在起作用,否則會經常出現崩潰和內存洩漏問題。

第三種我們使用蘋果13年 WWDC 上發布的官方 sample 一個 UIImage 的分類 UIImage+ImageEffects.h ,它不但可以製作毛玻璃效果圖片,而且可以調整模糊程度和顏色渲染。下面給出代碼:

image = [image bhb_applyBlurWithRadius:15 tintColor:tintColor saturationDeltaFactor:1 maskImage:nil];//因為OC沒有命名空間,避免你的程序中使用到了這個分類導致衝突,我加了前綴

最終的顯示效果很不錯,我將模糊程度儘量的調節到與新浪微博一致了,不過在這個過程中,我發現當我頻繁的進行彈出操作時,內存會不斷攀升如下圖:



內存暴增的原因肯定是因為剛才的截圖或者毛玻璃效果導致的,我們來用 Instruments 的 Allocations 來進行內存分析,找出元兇。

Xcode -> Product -> Profile -> Allocations, 開啟之後我們來使用右下角的 Mark generation(內存快照功能,進一步了解請移步[這片文章][1])


在彈出 View 和移除 View 的兩個時間點加 Mark generation。




可以觀察到,兩個時間點竟然相差了4.31M,這是一個相當恐怖的數字,怪不得我點擊幾次彈出功能之後內存會暴增,讓我們繼續跟蹤:





跟蹤到最後,我們發現大部分未釋放的內存來自於繪圖和位圖的創建,回想我們當初做的截圖操作,圖片上下文開啟後並沒有進行關閉操作,所以在程序不斷截圖的過程中開啟了無數的圖片上下文而且不會被釋放,添加下面這句關鍵的代碼就可以解決問題(NC的我竟然連這個都忘了加-.-):

UIGraphicsEndImageContext();

二、彈性動畫

新浪動畫中,按鈕彈出的動畫為彈性效果,按鈕到達最終位置後不會直接停止,而是做類似彈簧的一種阻尼運動,要實現這種動畫也很簡單 iOS7 後蘋果非常給力的添加了 spring 彈性動畫的快速創建方式:

[UIView animateWithDuration:(NSTimeInterval) delay:(NSTimeInterval) usingSpringWithDamping:(CGFloat) initialSpringVelocity:(CGFloat) options:(UIViewAnimationOptions) animations:^{} completion:^(BOOL finished) {}];

通過對 Damping 阻力和 Velocity 初速度的設置可以實現彈性動畫動畫效果如下圖:




當然實現彈性動畫還可以使用[Jonathan Willing][2]大大的 JNWSpringAnimation ,你可以像使用 CABasicAnimation 一樣輕鬆的使用它,通過改變關鍵的3個屬性 Damping 阻力, stiffness 硬直, mass 質量來改變彈性動畫的效果代碼如下:

JNWSpringAnimation *scale = [JNWSpringAnimation animationWithKeyPath:@"transform.translation.x"]; scale.damping = 7; scale.stiffness = 7; scale.mass = 1; scale.fromValue = @(0); scale.toValue = @(400); [redBall.layer addAnimation:scale forKey:scale.keyPath]; redBall.transform = CGAffineTransformMakeTranslation(400, 0);

關鍵的三個屬性對動畫的影響如下圖:


為了減輕項目對第三方框架的依賴,我使用了 iOS7 原生的 spring 動畫,如果你想要兼容以前版本,替換成 JNWSpringAnimation 即可。回到新浪動畫的製作,動畫是6個按鈕按照順序依次出現和消失,並且點擊 more 按鈕後可以向左平移到第二屏幕,並且在第二屏幕點擊 叉號 按鈕動畫會加在第二屏幕的6個按鈕上。
效果如圖:


對此我使用了一個 UIScrollView 來承載這些按鈕,並聲明2個數組用來保存所有的按鈕和正在顯示的按鈕(在屏幕上,並且需要做動畫):

@property (nonatomic,strong) NSMutableArray * visableArray;//屏幕顯示的按鈕數組 @property (nonatomic,strong) NSMutableArray * itemsArray;//所有按鈕的數組

這樣我在給這些按鈕加動畫的時候就不會浪費性能,只把動畫加在當前顯示在屏幕的按鈕上。動畫依次按照一定的時間差來執行,解決的辦法我是用的 GCD :

[self.visableArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { BHBCustomBtn * btn = obj; CGFloat x = btn.frame.origin.x; CGFloat y = btn.frame.origin.y; CGFloat width = btn.frame.size.width; CGFloat height = btn.frame.size.height; btn.frame = CGRectMake(x, [UIScreen mainScreen].bounds.size.height + y - self.frame.origin.y, width, height); btn.alpha = 0.0; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(idx * 0.03 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [UIView animateWithDuration:0.35 delay:0 usingSpringWithDamping:0.85 initialSpringVelocity:25 options:UIViewAnimationOptionCurveEaseIn animations:^{ btn.alpha = 1; btn.frame = CGRectMake(x, y, width, height); } completion:^(BOOL finished) { if ([btn isEqual:[self.visableArray lastObject]]) { self.superview.superview.userInteractionEnabled = YES; } }]; }); }];

這樣按鈕就會依次以彈性動畫的形式彈動出來了,是不是很簡單,對於動畫結束後所作的處理我將在下一節中說明。

三、動畫進行時對用戶交互的處理

動畫過程中,處理用戶交互的問題相當關鍵,視圖動畫中默認自動停止響應用戶交互,因此當按鈕進行彈性動畫時,觸摸它並不會生成任何事件。
但是當你觸摸加號按鈕的時候,會再次進行彈出框動畫,這時就會彈出兩個彈出框,這是我們不希望看到的,我們可以將加號按鈕的 enable 屬性設置為 YES 但是這樣做我們要在封裝的視圖內部獲取外部的加號按鈕,這一點違背了封裝性原則,並不是一個好的設計。所以我在所有按鈕的彈性動畫開始時,設置:

self.superview.superview.userInteractionEnabled = NO;

注意視圖的層級關係,我所設計的 BHBPopView 內部裝著一個 UIScrollView,而它上面放著這些按鈕,所以你要找到父視圖的父視圖才能夠統一屏蔽用戶的交互行為,當所有的按鈕彈性動畫結束時,也就是 visableArray 數組最後一個按鈕動畫結束時,我們恢復用戶的交互:

self.superview.superview.userInteractionEnabled = YES;

這樣當動畫進行過程中,屏蔽了用戶的交互,避免發生一些意外的情況。

四、UIControl 的 event 類型

注意到新浪動畫裡面按鈕有一個放大效果,並且當你手指放上去的時候放大,手指稍微挪動,便恢復原始大小。讓我們先來看一下動畫:


要實現這個效果只需要做一個形變動畫就可以了,關鍵是我們如何控制它放大和恢復大小。

思路如下:按鈕是繼承自 UIControl ,UIControl 有不同的事件狀態:

UIControlEventTouchDown = 1 << 0, // 手指落在按鈕的一瞬間觸發 UIControlEventTouchDownRepeat = 1 << 1, // 多點觸碰的時候,當第二根以上的手指觸摸瞬間出發 UIControlEventTouchDragInside = 1 << 2, // 手指在視圖範圍內拖動觸發 UIControlEventTouchDragOutside = 1 << 3, // 手指在視圖範圍外拖動觸發 UIControlEventTouchDragEnter = 1 << 4, // 手指從視圖外拖動到視圖內時觸發 UIControlEventTouchDragExit = 1 << 5, // 手指從視圖內部拖動到視圖外時觸發 UIControlEventTouchUpInside = 1 << 6, // 手指在視圖內部抬起時觸發 UIControlEventTouchUpOutside = 1 << 7, // 手指在視圖外部抬起時觸發 UIControlEventTouchCancel = 1 << 8, // 取消事件,放上了太多手指或者被上鎖或者電話呼叫打斷。 UIControlEventValueChanged = 1 << 12, // 當視圖的值發生改變時,發送通知。 UIControlEventEditingDidBegin = 1 << 16, // UITextField UIControlEventEditingChanged = 1 << 17, UIControlEventEditingDidEnd = 1 << 18, UIControlEventEditingDidEndOnExit = 1 << 19, // 'return key' ending editing UIControlEventAllTouchEvents = 0x00000FFF, // for touch events UIControlEventAllEditingEvents = 0x000F0000, // for UITextField UIControlEventApplicationReserved = 0x0F000000, // range available for application use UIControlEventSystemReserved = 0xF0000000, // range reserved for internal framework use UIControlEventAllEvents = 0xFFFFFFFF

我們所用到的事件是 TouchDown 和 DragInside,手指放上去觸發 TouchDown 放大視圖,在視圖內部移動 DragInside 時恢復視圖,注意按鈕的作用範圍是整個矩形區域包含了圖片和文字,當你的手指移出圖片的時候並非一定會移出按鈕作用範圍,所以依然會觸發 TouchUpInsite 事件,這時候我們需要做一個屬性來記錄用戶拖拽之後取消按鈕的 TouchUpInsite 執行。

@property (nonatomic,assign) BOOL btnCanceled;

這樣我們就可以實現動畫效果了,具體代碼如下:

//處理按鈕有效的點擊事件,當前按鈕放大消失,其他按鈕縮小消失,回調點擊事件 [btn addTarget:self action:@selector(didClickBtn:) forControlEvents:UIControlEventTouchUpInside]; //處理手指按下事件,放大按鈕 [btn addTarget:self action:@selector(didTouchBtn:) forControlEvents:UIControlEventTouchDown]; //處理手指拖動事件,恢復按鈕大小 [btn addTarget:self action:@selector(didCancelBtn:) forControlEvents:UIControlEventTouchDragInside];

總結:

製作類似新浪微博這種彈出框動畫,我的思路是先分析邏輯,這些特效都由哪些組成,毛玻璃背景,加頂部一個 logo ,加中間 UIScrollView和上面的很多按鈕,加底部工具條。研究透徹動畫的執行順序,動畫執行結果有哪些分支。然後針對特效中的難點,比如毛玻璃,按鈕彈性動畫等等進行逐一研究攻破,最後將這些組件整合在一起變成一個好玩的動畫,最後不要忘了動畫的內存和性能測試。這次我模仿的新浪微博動畫彈性效果並不是太理想,比起新浪原生來說不是特別一致,也希望有興趣的你來給我一些建議優化它。最終在你的項目中加入我的彈出框動畫真的只需要一句話哦:

/** * 直接顯示一個popView在某個view上 * * @param view 父view * @param imageArray 圖標數組 * @param titles 標題數組 * @param block 回調 * @return pop視圖 */ + (BHB_INSTANCETYPE)showToView:(UIView *)view andImages:(NSArray *)imageArray andTitles:(NSArray *)titles andSelectBlock:(DidSelectItemBlock)block; /** * 如果顯示一個帶more功能的,請使用此方法 * * @param view 父view * @param array BHBItem類型的集合 * @param block 回調 * @return pop視圖 */ + (BHB_INSTANCETYPE)showToView:(UIView *)view withItems:(NSArray *)array andSelectBlock:(DidSelectItemBlock)block;

hexo出點問題修復到下半夜啊(升級到3.0太蛋疼了),現在腦子暈暈的,明天還要去新公司入職,動畫中還有很多細節我不能一一分享了,歡迎大家來搞我的[Demo][3]

good luck!

版權聲明:我已將本文在微信公眾平臺的發表權「獨家代理」給 iOS開發( iOSDevTip ) 微信公眾號。掃下方二維碼即可關注「iOS 開發」:




相關焦點

  • 300行代碼帶你進入敘/麗/亞轟炸戰場
    其中:<button> 是表情選擇按鈕,使用一個笑臉 svg 圖片表示,裡邊的 <div> 是表情選擇框彈出層,裡邊的表情將在 JS 中動態加載,目的是為了實現動畫預覽。<input /> 是聊天消息輸入框,沒什麼特別的。<button> 是發送按鈕這個是 HTML 的基本結構,接下來看一下 CSS 樣式。2.
  • 300 多行代碼搞定微信 8.0 的炸裂特效
    其中:<button> 是表情選擇按鈕,使用一個笑臉 svg 圖片表示,裡邊的 <div> 是表情選擇框彈出層,裡邊的表情將在 JS 中動態加載,目的是為了實現動畫預覽。<input /> 是聊天消息輸入框,沒什麼特別的。
  • 300 多行代碼搞定微信 8.0 的「炸」「裂」特效!
    其中:<button> 是表情選擇按鈕,使用一個笑臉 svg 圖片表示,裡邊的 <div> 是表情選擇框彈出層,裡邊的表情將在 JS 中動態加載,目的是為了實現動畫預覽。<input /> 是聊天消息輸入框,沒什麼特別的。<button> 是發送按鈕這個是 HTML 的基本結構,接下來看一下 CSS 樣式。2.
  • 0基礎學習JavaScript一定要知道如何使用VS2019去編寫代碼
    然後在此項目中再創建一個HTML頁面,用於編寫JavaScript代碼。圖1創建的是一個空的Web項目,項目中除了基本的引用、Properties、Web.config之外,沒有其他任何多餘的文件。在圖1右擊項目名稱,添加一個新項目。
  • 新浪微博如何設置籤名檔
    您可以在微博首頁找到 帳號-我的工具-籤名檔 根據您的需要選擇用途,獲得代碼之後就可以將代碼複製到需要添加到的地方即可,籤名檔會將您的微博內容及時顯示出來,您可以通過籤名檔和好友一起分享您的新鮮事。
  • PowerPoint 2013幻燈片中插入Flash動畫及對動畫播放進行控制的方法
    在「開發工具」選項卡的「控制項」組中單擊「其他控制項」按鈕,如圖1所示。此時打開「其他控制項」對話框,選擇「Shockwave Flash Object」控制項,如圖2所示,然後單擊「確定」按鈕關閉「其他控制項」對話框。
  • 如何設置幻燈片的動畫播放方式?
    在PowerPoint中為對象設置動畫的確很方便,但隨之而來的問題是,添加動畫後並不能完全達到預期想要實現的效果,能否對添加的動畫進行一些細節上的設置
  • 【建議收藏】找不到免費的角色動畫?來試試mixamo
    這裡有個小技巧,這時候如果下載,只能把這一個動畫下載下來。但是Mixamo裡面有很多動畫包,在搜索框中搜索pack,就可以一次性預覽多個動畫了。後面下載的時候也能一起打包下載下來。這時候會彈出一個對話框(上一步動畫選擇的是動畫包):
  • ppt2020動畫效果,幻燈片切換,插入聲音和影片等媒體
    其中,要添加動畫效果的對象叫動畫對象。在Power Point2010中,設置動畫效果很簡單,直接在「動畫」選項卡中完成設置。選定要設置動畫的對象,在動畫選項卡的「動畫」組的樣式列表中選擇一種動畫樣式,即為該對象設置了選定樣式的動畫效果。
  • 如何在PPT中插入動畫、視頻、音頻、解說等
    3.這時,滑鼠變成「+」,在幻燈片中需要插入flash動畫的地方畫出一個框。如圖3。4.在框中點擊滑鼠右鍵,點擊屬性,如圖4,然後出現ShockwaveFlash Object屬性設置欄,如圖5。5.左鍵雙擊「自定義」,在彈出的對話框中,在影片URL(M)後的輸入欄中,填上要插入的swf檔案的路徑和文件名,當然,直接讀取網上的swf文件也是可以的。如圖6。
  • 在PPT 中插入動畫、視頻、音頻、解說的方法
    3.這時,滑鼠變成「 」,在幻燈片中需要插入flash 動畫的地方畫出一個框。4.在框中點擊滑鼠右鍵,點擊屬性,然後出現Shockwave Flash Object 屬性設置欄。5.左鍵雙擊「自定義」,在彈出的對話框中,在影片URL(M)後的輸入欄中,填上要插入的sw f 檔案的路徑和文件名,當然,直接讀取網上的swf 文件也是可以的。
  • Excel基礎知識:使用電子表格複選框及條件格式管理工作進度
    現在,點擊複選框試驗一下,效果就達了。複選框太多怎麼辦如果項目太多,一個個手動設置複選框顯然不是一個好辦法。陳Sir準備了一段現成的代碼,使用前要記住添加代碼的工作表名稱,比如這裡是Sheet1,使用Alt和F11,打開VAB編輯器,找到工作表Sheet1,雙擊打開,複製這段代碼,直接粘貼過來,關閉編輯器。
  • 精通React/Vue系列之帶你實現一個功能強大的通知提醒框
    的彈出位置能自定義關閉圖標可以手動選擇通知窗類型能自定義通知框的偏移量能設置通知框的信息和提示文本但是真正要實現以上需求討論的那些通知框的功能,實際上我們還是要寫很多代碼來處理不同的情況的,所以為了方便大家理解,我們這裡使用React Notification這個第三方庫來幫我們處理基本的邏輯,筆者會基於它,來實現上面我們討論的那些功能。
  • 教你如何在PPT中插入動畫、視頻、音頻.
    2.在控制項工具箱中選擇「其他控制項」,這時會列出電腦中安裝的Active X 控制項,找到Shockw ave Flash Object 控制項。3.這時,滑鼠變成「 」,在幻燈片中需要插入flash 動畫的地方畫出一個框。4.在框中點擊滑鼠右鍵,點擊屬性,然後出現Shockwave Flash Object 屬性設置欄。
  • 如何在PPT中插入動畫、視頻、音頻、解說……
    2.在控制項工具箱中選擇「其他控制項」,這時會列出電腦中安裝的Active X控制項,找到Shockwave Flash Object控制項。   3.這時,滑鼠變成「+」,在幻燈片中需要插入flash動畫的地方畫出一個框。   4.在框中點擊滑鼠右鍵,點擊屬性,如圖4,然後出現Shockwave Flash Object屬性設置欄。
  • 【值得收藏】在PPT 中插入動畫、視頻、音頻、解說的方法
    3.這時,滑鼠變成「 」,在幻燈片中需要插入flash 動畫的地方畫出一個框。4.在框中點擊滑鼠右鍵,點擊屬性,然後出現Shockwave Flash Object 屬性設置欄。5.左鍵雙擊「自定義」,在彈出的對話框中,在影片URL(M)後的輸入欄中,填上要插入的sw f 檔案的路徑和文件名,當然,直接讀取網上的swf 文件也是可以的。
  • 如何在幻燈片中插入Flash動畫?
    → 操作方法:使用ShockwaveFlash Object控制項插入Flash動畫步驟1        將「開發工具」選項卡添加到功能區中,具體方法請參閱:疑難122。圖6-39  單擊「其他控制項」按鈕步驟3        打開「其他控制項」對話框,在列表框中選擇「Shockwave Flash Object」選項,如圖6-40所示。
  • 【小動畫製作軟體】萬彩動畫教程
    我想為人物添加手繪動畫效果,萬彩動畫大師可以實現這個效果嗎? 答:您可以為不同元素或者物體添加手繪動畫效果,包括人物形象,圖片,文本等元素。 手繪動畫效果如下:
  • 如何在python語言代碼實現間隔加減法
    打開Visual Studio工具,新建python項2、在指定文件夾滑鼠右鍵,選擇添加新建項在指定文件夾滑鼠右鍵,選擇添加新建項3、打開新加新項窗口,選擇空python文件,添加定義變量,循環進行計算,列印結果5、保存代碼並滑鼠右鍵選擇開始執行,運行python文件