模倣性の高い WeChat 記事の浮き玉
WeChatの最新バージョン6.6.7では、新しい記事フローティングボール機能が追加されました。記事を読んでいるときに、友人から突然緊急のメッセージが届いたので、すぐに返信する必要があります。あるいは、たまたまスナックバーの前を通りかかったときに、一時的に WeChat 決済を開いたり、読書を一時的に中断したりする必要があるかもしれません。以前は記事詳細ページからしか抜けられず、作業が終わった後は元の記事が隣り合って見つけることができました。私たちのような WeChat のヘビーユーザーにとって、このような状況に遭遇するたびに本当に苦痛になります。そのため、この機能がリリースされると、すぐに最新バージョンにアップデートされましたが、この機能は親しい友人に会うような感覚で、非常に使いやすいです。以下のアニメーションを通してそれを感じることができます
実際、フローティング ボールの概念は長い間存在していました。たとえば、360 アシスタントの交通監視ボール、iPhone 独自の AssistiveTouch (あのかわいい小さな白いボール) などです。
Github アドレスが気に入ったら、クリックしてください❤️
体験すると手がかゆくなり、思わず真似したくなります。あなたのアプリにこの機能を組み込むことができれば、あなたのアプリを瞬時に新しいレベルに引き上げることができると思います。それでは、WeChat 記事のフローティング ボールの中核となる技術的なポイントを 1 つずつ分析してみましょう。
画面端のジェスチャを使用してビューをポップすると、右下隅に丸い角のプロンプトが表示され、ジェスチャの進行に合わせて移動します。 UIScreenEdgePanGestureRecognizer
の進行状況を取得するにはどうすればよいですか?システムに付属するinteractivePopGestureRecognizer
カプセル化されているため、そのアクションをフックして内部のジェスチャの進行状況を取得することはできません。したがって、別の方法を見つける必要があります。
self . interactivePopGestureRecognizer ? . delegate = self
func gestureRecognizer ( _ gestureRecognizer : UIGestureRecognizer , shouldRecognizeSimultaneouslyWith otherGestureRecognizer : UIGestureRecognizer ) -> Bool {
return true
}
UIScreenEdgePanGestureRecognizer
UINavigationController に追加して、ポップ ジェスチャの進行状況を取得します。 let gesture = UIScreenEdgePanGestureRecognizer ( target : self , action : #selector ( handleNavigationTransition ( gesture : ) ) )
gesture . edges = . left
self . view . addGestureRecognizer ( gesture )
このように、同時に応答できる 2 つのUIScreenEdgePanGestureRecognizer
があり、システムに付属する 1 つは元のロジックを維持しており、追加した新しい 1 つはポップ ジェスチャの進行状況を取得するために使用されます。調和して楽しんでください。私のこのテクニックは、iOS に関するこの記事でも使用されています: 主流の APP プロフィール ページ (Jianshu、Weibo など) を 1 分で統合する
フローティング ボールはどのページでも一時停止できるため、新しい UIWindow に配置する必要があります。たとえば、システム キーボードがポップアップする場合、それはUIRemoteKeyboardWindow
によってホストされます。 この場合、このウィンドウのライフサイクルは特定のページに依存しないため、シングルトンを使用して実装する方が良いでしょう。このコードは比較的分散されており、ソース コードを直接見ることで理解できます。
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 の使用法を理解するための記事
浮遊ボールをクリックして開いた記事は、浮遊ボールの位置から始まるカスタム遷移アニメーションによって実装されていることがわかります。 トランジション アニメーションのカスタマイズ方法を説明する記事はたくさんありますが、カスタム トランジション エフェクトと完全なジェスチャ ドライバーを数行のコードですばやく統合するには、この記事を読むことをお勧めします。
WeChat のフローティング ボールは比較的多くの技術的なポイントを使用しており、コードは比較的分散しています。アプリにこの機能を統合したい場合は、コードを慎重にパッケージ化する必要があります。 要件をどのように実装するかよりも、要件をどのように設計するかが重要だと思います。真似してみると、細かいロジックがたくさんあり、それらが連動して、最終的に使用している浮き玉機能が提示されることがわかりました。 プログラマーとプロダクトマネージャーは相思相愛と言われていますが、ここでこの機能のプロダクトマネージャーに賛辞を送りたいと思います。
コードに問題がある場合、またはご質問がある場合は、フィードバックをお送りください。できるだけ早く対応させていただきます。 Github アドレスが気に入ったら、クリックしてください❤️