高仿微信文章懸浮球
微信在最新版本6.6.7,新加了一個文章懸浮球功能。當你正在閱讀文章的時候,突然有好友發了緊急訊息,你需要立即回覆。又或者你剛好路過小吃店,需要臨時打開微信支付,等等暫時中斷閱讀的情況。以前只有退出文章詳情頁面,處理完事情之後,再挨著找到原來的文章。對我們這種重度微信使用者來說,每次遭遇這種情況,真的很痛。所以,當這個功能推出的事情,立刻更新了最新版本,這個功能感覺就像遇到了知心人一樣,用起來十分順手。可以透過下面的動圖感受一下
其實懸浮球的概念早就有了。例如360助手的流量監控球,iPhone附贈的AssitiveTouch(就是那個可愛的小白球)等等。
Github地址喜歡就點顆❤️
體驗過後,讓人手癢癢,情不自禁得想要模仿一把。如果你的APP可以整合該功能,我覺得可以讓你的APP逼格瞬間提升一個level。好了,下面讓我們來一一解剖,微信文章懸浮球的核心技術點:
當我們通過螢幕邊緣手勢pop視圖的時候,右下角會有一個圓角提示圖,跟著手勢進度移動。如何取得到UIScreenEdgePanGestureRecognizer
的進度呢?因為系統自備的interactivePopGestureRecognizer
是被封裝起來的,它的action我們無法掛鉤拿到裡面的手勢進度。所以,需要另闢蹊徑了。
self . interactivePopGestureRecognizer ? . delegate = self
func gestureRecognizer ( _ gestureRecognizer : UIGestureRecognizer , shouldRecognizeSimultaneouslyWith otherGestureRecognizer : UIGestureRecognizer ) -> Bool {
return true
}
UIScreenEdgePanGestureRecognizer
到UINavigationController上面,用於取得pop手勢的進度。 let gesture = UIScreenEdgePanGestureRecognizer ( target : self , action : #selector ( handleNavigationTransition ( gesture : ) ) )
gesture . edges = . left
self . view . addGestureRecognizer ( gesture )
這樣子,有兩個UIScreenEdgePanGestureRecognizer
可以同時回應,系統自帶的依然保持原有邏輯不動,我們新增的用於獲取pop手勢進度,兩者井水不犯河水,其樂融融。該技巧我的這篇文章也有使用iOS:一分鐘整合主流APP個人資料頁(如簡書、微博等)
既然懸浮球可以在任何一個頁面,必然是放在一個新的UIWindow上面。例如係統的鍵盤彈出的時候,就是一個UIRemoteKeyboardWindow
在承載。 然後這個window的生命週期不依賴某一個頁面,所以用單例實作比較好。這塊程式碼比較分散,直接看原始碼就可以了解
override func point ( inside point : CGPoint , with event : UIEvent ? ) -> Bool {
let roundEntryViewPoint = self . convert ( point , to : roundEntryView )
if roundEntryView . point ( inside : roundEntryViewPoint , with : event ) == true {
return true
}
let collectViewPoint = self . convert ( point , to : collectView )
if collectView . point ( inside : collectViewPoint , with : event ) == true {
return true
}
return false
}
UIBezierPath
畫一個四分之一圓,然後用CGPath
的contains(point)
方法判斷。 func updateBGLayerPath ( isSmall : Bool ) {
var ratio : CGFloat = 1
if !isSmall {
ratio = 1.3
}
let path = UIBezierPath ( )
path . move ( to : CGPoint ( x : viewSize . width , y : ( 1 - ratio ) * viewSize . height ) )
path . addLine ( to : CGPoint ( x : viewSize . width , y : viewSize . height ) )
path . addLine ( to : CGPoint ( x : ( 1 - ratio ) * viewSize . width , y : viewSize . height ) )
path . addArc ( withCenter : CGPoint ( x : viewSize . width , y : viewSize . height ) , radius : viewSize . width*ratio , startAngle : CGFloat ( Double . pi ) , endAngle : CGFloat ( Double . pi*3 / 2 ) , clockwise : true )
path . close ( )
bgLayer . path = path . cgPath
}
override func point ( inside point : CGPoint , with event : UIEvent ? ) -> Bool {
return bgLayer . path! . contains ( point )
}
對這塊不太了解的同學,可以參考這篇文章。一篇搞定事件傳遞、回應者鏈條、hitTest和pointInside的使用
可以看到點擊懸浮球打開的文章,是透過一個自訂轉場動畫實現的,從懸浮球的位置開始展開。 有許多文章都有講解如何自訂轉場動畫,但是我推薦你看這篇文章幾句程式碼快速整合自訂轉場效果+ 全手勢驅動
微信的懸浮球,用到的技術點相對比較多,程式碼也比較分散,如果你的APP要整合該功能,需要認真封裝程式碼。 相對於如何實現,我認為如何設計一個需求更重要。我在模仿的過程中,發現其中有許多細節的邏輯,彼此環環相扣,最終就呈現出了你正在使用的懸浮球功能。 都說程式設計師和產品經理是相愛相殺,在這裡我要為該功能的產品經理點個讚?
如果程式碼中有任何問題,否則你有任何疑問,都可以回饋給我,我將第一時間處理。 Github地址喜歡就點顆❤️