iOS:高仿微信文章懸浮球

金字塔程式設計師發表於2018-06-06

頭圖.PNG

前言

微信在最新版本6.6.7,新加了一個文章懸浮球功能。當你正在閱讀文章的時候,突然有好友發來了緊急訊息,你需要立即回覆。又或者你剛好路過小吃店,需要臨時開啟微信支付,等等臨時中斷閱讀的情況。以前只有退出文章詳情頁面,處理完事情之後,再挨著挨著找到原來的文章。對於我們這種重度微信使用者來說,每次遭遇這種情況,真的很蛋疼。所以,當這個功能推出的事情,立馬更新了最新版本,這個功能感覺就像遇到了知心人一樣,用起來十分順手。可以通過下面的動圖感受一下

JXWeChatFloatView.gif

其實懸浮球的概念早就有了。比如360助手的流量監控球,iPhone自帶的AssitiveTouch(就是那個可愛的小白球)等等。

倉庫地址

Github地址喜歡就點顆❤️

核心技術點

體驗過後,讓人手癢癢,情不自禁得想要模仿一把。如果你的APP可以整合該功能,我覺得可以讓你的APP逼格瞬間提升一個level。好了,下面讓我們來一一解剖,微信文章懸浮球的核心技術點:

1.懸浮球的出現

當我們通過螢幕邊緣手勢pop檢視的時候,右下角會有一個圓角提示圖,跟著手勢進度移動。 如何獲取到UIScreenEdgePanGestureRecognizer的進度呢? 因為系統自帶的interactivePopGestureRecognizer是被封裝起來的,它的action我們無法掛鉤拿到裡面的手勢進度。所以,需要另闢蹊徑了。

  • 首先,讓UINavigationController的delegate等於自己,然後讓多個手勢可以同時響應。
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個人資料頁(如簡書、微博等)

2.懸浮球全域性置頂

既然懸浮球可以在懸浮在任何一個頁面,必然是放在一個新的UIWindow上面。比如系統的鍵盤彈出的時候,就是一個UIRemoteKeyboardWindow在承載。 然後這個window的生命週期不依賴某一個頁面,所以用單例實現比較好。這塊程式碼比較分散,直接看原始碼就可以瞭解

3.事件響應

  • 懸浮UIWindow的事件傳遞 只要事件位置沒有在圓球和右下角上,就不響應
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
    }
複製程式碼
  • 右下角四分之一圓,事件響應 可以看到微信,只有當手指移動進右下角圓內,才能進行懸浮。而不是按著檢視的frame來響應。 首先,通過UIBezierPath畫一個四分之一圓,然後用CGPathcontains(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的使用

4.自定義轉場動畫

可以看到點選懸浮球開啟的文章,是通過一個自定義轉場動畫實現的,從懸浮球的位置開始展開。 有許多文章都有講解如何自定義轉場動畫,但是我推薦你看這篇文章幾句程式碼快速整合自定義轉場效果+ 全手勢驅動

實現效果

JXWeChatFloatView-2.gif

總結

微信的懸浮球,用到的技術點相對比較多,程式碼也比較分散,如果你的APP要整合該功能,需要認真封裝程式碼。 相對於如何實現,我認為如何設計好一個需求更重要。我在模仿的過程中,發現其中有許多細節的邏輯,彼此環環相扣,最終就呈現出了你正在使用的懸浮球功能。 都說程式設計師和產品經理是相愛相殺,在這裡我要為該功能的產品經理點個贊?

倉庫地址

如果程式碼中有任何問題,否則你有任何疑問,都可以反饋給我,我將第一時間處理。 Github地址喜歡就點顆❤️

相關文章