From e6423c450ceb7c00d78dcb528795d7ce83bf30dc Mon Sep 17 00:00:00 2001 From: gmc Date: Thu, 25 Jul 2024 16:29:23 +0200 Subject: [PATCH 1/4] [TF-1552] basic notification image animation --- .../NavibarNewsIcon.imageset/Contents.json | 3 -- .../NavibarNewsIcon.pdf | Bin 4153 -> 2546 bytes .../NavibarNewsIconBadge.pdf | Bin 7178 -> 2645 bytes ...ViewController+TokensViewControlling.swift | 51 ++++++++++++++---- 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/TwoFAS/TwoFAS/Other/Assets.xcassets/NavibarNewsIcon.imageset/Contents.json b/TwoFAS/TwoFAS/Other/Assets.xcassets/NavibarNewsIcon.imageset/Contents.json index 544c4545..c9cedbec 100644 --- a/TwoFAS/TwoFAS/Other/Assets.xcassets/NavibarNewsIcon.imageset/Contents.json +++ b/TwoFAS/TwoFAS/Other/Assets.xcassets/NavibarNewsIcon.imageset/Contents.json @@ -8,8 +8,5 @@ "info" : { "author" : "xcode", "version" : 1 - }, - "properties" : { - "template-rendering-intent" : "template" } } diff --git a/TwoFAS/TwoFAS/Other/Assets.xcassets/NavibarNewsIcon.imageset/NavibarNewsIcon.pdf b/TwoFAS/TwoFAS/Other/Assets.xcassets/NavibarNewsIcon.imageset/NavibarNewsIcon.pdf index 7383b1aed79e1e2ce119debc093fa2900cb4c9e8..6ec1c8bb0ddcba60761c22c00954856bb5d33788 100644 GIT binary patch literal 2546 zcmZWrdpy&7A2(vNa>;N+ho9k?$jppQE*o+yHM2-qjSX|z!rV(Emo9E&SuUps$tCGT za=K6{Wlp(YDv2CINeD-h&a-uKPS5lC>iI^8Wn(c)xXscJ|v*hGtMG3NQj# zzClm`u&@BEte|uzO>~7C{~JfzGeX$(Z~$o^LS@tK=zc63U6>&ZT+ z@^@9Ap0>8?R?Q}Sni;V^cxUZ>?{cR`!qFqkpQo2r<~waK8z1X@KR-7U`|Sk5 z#U*s3LAr$1^bU&j4Gg7Wm$sLm%RYwB(zE=*@c81MQ}T1g3RC%koKGD+o@$K;!=2sP_hgCm8id+@{vSlfE>uFu3TCiz8+tljhy66H~$V5+CdUKJXHE z=m;6Rx?#ptFgij`qM$Bm+65L91(GdZ`~toECi^;KOCkOWY6fSCa<+1V3HocT2_up29F~<$V?;oHpg5 z#U0dKAY;gTY4gR4;8k{OU?Qk~zvB`*yx=A3|Jn{IegofaUUE`wm%Kis1Iv1F zBy*uZQF5zfoD|=EfWqe%{dLkbW71 z)Md=7qKcCM-vE2zET$Eo)LF4zWtg;7m0Vbd=idA!;L=rX$ip+VbDX!=c~A+qawC4k z)dWD}2q3h3vK(!W(_Qq=5z;06FYp=UY#vaA47fLSix)u+WPbBIj?yiI>7hp7l zcXO*P;U|b2Aa=nesN&Gq5kdaHworev5*G`@6B^vu<(_&uPTU z*!-$l)*TJ&V>4;+=UQ|D?zh_$wzUoSTu!$Q?L=T(RWqDg!_Ft|JbMAznbfBz)$rNA zYggFYTy5FZ!4>n-z5{aJ)&u(XHKXY`$HDrr*|SM%0)qRE0^ zpUdvUaU-}@Y<&DAzg7AtdGe=+?(ghhTf^@zE{U1%m}F zOnUay*yc)15enQ^xL3AfLAF6%V{ljig)>B(7#SIx0pBZ;=n<^Qa6ftk zAaW8S3m|d@wk#%_&J=bO*@*8Tks2<<0L(Y&UuHuDt(y?!E(T3FDj;G&TJK}aiWKIe z{%`(>ucqVsHcWr0BqA8G-cU5ySK_u*HZ_D5@MCIBINcv=1eig;(ieRJQw#=;0fg|k zKp6pFP5^TqLJQ{<#X5w-;e>pCfQ*e$*#Cu4IN`#6d}VC<$_Sx{Lq&hj$xgTq zLT}4pMU)F zn;{TJ$@vFHzi*vATq^6myXPM$KR2w?)p@RgX_77sYcxvM_b}K}0sr1NteMJ^RqCq@ zNkC`TTPnWDA0Wap9Sn>tPn8G5ChFtRSF_hgXnfj!^E2ZkgP6GZAzVLno2RSaxXEk* z_Q+ot*mSAO35AgayAJ01ffHg&l=-p2;e`<}CBVlph3|_N$W4i*;2hbeBd9A0B96(3 zh+=3i*U7OgDT5Zs#4C1Y$Lv2Z5H?YY|yC%0A#D_$_YSr*pR99#8w`Q!|&;e*n zsVN+cA$xSKRD=|=T~ew&uO?-X6NOddT7wi*?3!)X*>JU?YD>X37fNa;_D*>nyLK zjILjBGrD2?&spG_L<$V)+=Srq^3s?>v}BHKqfX=vHDkvA}~}OR3Z#d zTHv)zmqHKvTw$P_wfI;1%?dG}hEc9kYZ`gEnkYXB)JD_Szu*3o{`%bserkvp!PlRE z_47MFspnsK`it+U-=G&iz5DdDUw-oKYv`G$p8L!&uDYQo&~9*1J7I5}sG1M-q9crS zXPOP*R~tGT!#wqtOBJBKxff+>->}BeZ}KEXd#5T5=V&;ehjExcL=%;T7qLnaMbreL z0@JfFj}+Rw<9!^Sd+Cv%#COzXR&#_&Jb_iCy-5!Qh+&69iGW7R+nBzL@ylR$oCJ?E zIZ2aX<-@dl&~2me{PxM@2)QOmbT>rz_ZFUi_Z0qm=|b}R`}g<58+kAKf|gAARV%LFd(hf#BC)_tuN$0@%@d5ye?=ebDhB zl~oU(HEeZCLoiK)d2cqER)+9)(0P#O%bsOjUS67)%uLb?3pC!fFtsS{f<-rb80X%) z8)q*yJQWipjh}|g9C86^X74;%p;5@X#I7;b>A5x5hTaeM_&q6^A;~ zH;&N1$EOwXat*zrfR(PRfv&qQcDmF#!??#~uPDAA#)Zdi%f{b|1S&I}KfDP^%W{{b zl9IC|i3X2Rla@nCS`64FtBd1CK2?QO*fvD}b1RmuTG&ce_yDQOcJdU^UM*gOYqYD9 zu2Q-7_xsk?fG6lQ6nj;{yI!xacYEEdn!=#y3591H>`8eR_>Eu7RT>q#68M&ilrFyv zTq8RX4g6lNJI zQ1`8?Zv10nl^;M(OJYd!T8Ib6M$X{~anMP%SYJ76=;ol?1rb=*&715to-wqHD7k>n z1`VicY<0E3#ZC8@P1jndYHq+Kh8o3@r}z&^A(%jORAF$->0FtYVi$t7J(k(HWd}(eMk>|!^V9H= zf)H4z)+P#beNPYTBthttuRjrid47P{|4 TmrC;@=D;S5TelAHPL00-)e0O7 diff --git a/TwoFAS/TwoFAS/Other/Assets.xcassets/NavibarNewsIconBadge.imageset/NavibarNewsIconBadge.pdf b/TwoFAS/TwoFAS/Other/Assets.xcassets/NavibarNewsIconBadge.imageset/NavibarNewsIconBadge.pdf index 56ebcd71a46bc67d785cc2acdb274747562ad918..a50f059aa1895f8190807dc778ebd64d69254b34 100644 GIT binary patch literal 2645 zcmZXWc|6qlAIHri%{X$8$@O)NBQ%E@a>QU#V-b^|b1^d+jF_=AqmX8u#YByeTrm>m z3K?sYR#LI85y{H0EKzH8Y%P*RJJVsW_aEQK`}2By-sks^&lB#0ceg@qw}n7Z01{wO zVjuwE=meP0O1}>W>fLh{fr2zI6`a)Pbxi%9SvBEuM@?R zI0K8#q>|$xiJ+oluM;}93R%lXxM6a8f@<*G2@_D8H#767qFjrj%qHe~N+D+cX!o}< zsL%ytkM~I_#O~D>Yd?PHJo#Z|6|o%t;Y#=6_2sw8tG}59lnixW?S7v!c}>3|;6-HI zL;Xs|lQur3Vwe$r&FLDlQ8OX8D{P|ga914xTpS#fgt^pwrp0ngcEG}VTk$sn1?_|1 z)jUek(xD9JLNEGTL0eE`J{Dn?$LjMas27gxJxbitcckIR)kjhbRRwhxWB3jWwWNp7 z>zl@C-?#3SKT!G%+5tfHo+VtfB<6#s|o>5Yy33j<4w=#z;FXO>ir&sz|g zyUb0DgH0ne+YFixbc-O*?B4UXuFC`sT|y4b>ifJqKW|g8?tg|O7zU+zuSp$dRF`Ci zMglsmCFz4%ri-p60rA%IW~0-FcWd=|`#3R)f%aY@dm^*6LNq8cM8xqyUqP)ACr``C z-pfEXz|m`MJ|=x1+apm$^+~|xv0z!Iwsi%E@Vh1m6GPkoRMW;X#1h|gbXRN6Xj0M(i+v)dv8g1xBng=5NMutaElny;0n{AVt<`G1&=ukXK zgWSq`?n`+Cf2kJ;*Bo-LoG3^@%%A7pI=epCzq9hp83N+ zc~b#4wn@$$=B^j`1b0$9$?z-HRhj8hWy7a4Z{1vj#nOj_ljrqyEO$7@umtf zX6K_K5U(R2guH@us!1Oz4Viy65Z={J)FG~)noC})aqc?H?RjRCZN(4WF_s$LnEyjk zL;M@?!xzTKejJeLPH9(CAxLMXZwJQQVvc&&USv-Epc%N^vMfct>dH&VFI01?=jD{M*YaTVT z6E+`2vJWl6hv$F3O<(gf^D}YyU?#JhsLhj&>fpu4yP${Iu>ylUA2N4P<^0J9jLBn6 z_p0b~^7NEjN6;?|*GZLOs0o_SN}uP#9F^c=)ZnckZ**AUjR&{qmAavUKV;Y45_NQ5 z(g?a+!AdbRm_K_}J+PysHz#{*`o+B2;c2CN4i#sMk2PvMkI43@R;AgN6K8FVEc;z& zGS3n+@)sc(zEXKTT;&`iWCC@;NH*tSReLE`HuYp-*-aHy<%!!D(K$1X({tVq7~Vcb zbkRgUF;ZZXaZa^@ZZt~+vkRIlZRt`|qrK2r z9LYM{q~zJJ%ay+FFmCS!b~Tr+?_Dv#Utb9oeOn4ij}NLBZJu}Ms8vgM(nwrmvt|A2 zs&R;ZCcdoIAstVpZK>vP=hO6T7jKGenTK$TJ8ng@Cd1-zYSDJOc3niZgZ+M~^yQvy zSid(reT{Eb)TDQ3mPPNYfe%*{_XVmff&MR&J}{U3lhb&}S(m0+f!S~5j{MD1(Y6t;x4?zHN8V}j z4m&_odLB)J6ENZzAVfN~->FVkxeRep3h!yIJ>{TXWp$E22uOKRRtOTA0`6;TJgqJ%EQT5a1MeNRa#954f6p&MbwfGOKiFZyw9w!!Gns zlrpfH%D=)$W2M6KjM+icx_e1-A&8%Y-5S|EffNgXTv}-ve8XKGo~kYCf1`7{I%xFw zzGA^jhwwvX_@cbC#3S@MP#?kqff>gn8EE|M$xJ{7x0`t8Xo|1xe8>il!x zx?IZkb(CG{|3kV7Q3}Tn3JR^}dI0x}HEkfKXAen%;QOAvuAdW@-r+?NbuKBVJn}1}I z49MCjC~54|;BI6#IhGOi&(K6BH4=gZY$2b|QLINK>fW9GFi~E$= zhJ;4i{YOHfZ2x@=ZTIh7Xlt9l0@+M5EtblJNd6uHFJ6ZkixEdP2h3;@=AV{=ATk(i iKr-;h1ta#+lBweEKSCav$^JMF8iTfmz~Q_0y8j7Nc1Mi> literal 7178 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 SP;D5UfwCxOW=@{Yl>Y`KBfne# diff --git a/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift b/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift index 4b174899..26121326 100644 --- a/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift +++ b/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift @@ -125,22 +125,53 @@ extension TokensViewController: TokensViewControlling { func disableDragging() { tokensView.dragInteractionEnabled = false } - + + private func animate(_ barButtonItem: UIBarButtonItem) { + let angle: Double = .pi / 8 + let numberOfFrames: Double = 5 + let frameDuration = Double(1/numberOfFrames) + + UIView.animateKeyframes(withDuration: 1, delay: 0) { + UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: frameDuration) { + barButtonItem.customView?.transform = CGAffineTransform(rotationAngle: -angle) + } + + UIView.addKeyframe(withRelativeStartTime: frameDuration, relativeDuration: frameDuration) { + barButtonItem.customView?.transform = CGAffineTransform(rotationAngle: +angle) + } + + UIView.addKeyframe(withRelativeStartTime: 2*frameDuration, relativeDuration: frameDuration) { + barButtonItem.customView?.transform = CGAffineTransform(rotationAngle: -angle) + } + + UIView.addKeyframe(withRelativeStartTime: 3*frameDuration, relativeDuration: frameDuration) { + barButtonItem.customView?.transform = CGAffineTransform(rotationAngle: +angle) + } + + UIView.addKeyframe(withRelativeStartTime: 4*frameDuration, relativeDuration: frameDuration) { + barButtonItem.customView?.transform = CGAffineTransform.identity + } + } + } + // MARK: - Navibar icons func updateAddIcon(using state: TokensViewControllerAddState) { func createNewsIcon() -> UIBarButtonItem { let img: UIImage = { presenter.hasUnreadNews ? Asset.navibarNewsIconBadge.image : Asset.navibarNewsIcon.image }() - img.withTintColor(Theme.Colors.Icon.theme) - let button = UIBarButtonItem( - image: img, - style: .plain, - target: self, - action: #selector(showNotifications) - ) - button.accessibilityLabel = T.Commons.notifications - return button + let naviButton = UIButton(type: .custom) + naviButton.translatesAutoresizingMaskIntoConstraints = false + naviButton.setBackgroundImage(img, for: .normal) + naviButton.addTarget(self, action: #selector(showNotifications), for: .touchUpInside) + naviButton.accessibilityLabel = T.Commons.notifications + + let uiBarButtonItem = UIBarButtonItem(customView: naviButton) + if presenter.hasUnreadNews { + animate(uiBarButtonItem) + } + + return uiBarButtonItem } func createAddButton(image: UIImage) -> UIBarButtonItem { From a39dfba70d5411af3e1c645460d57d4a5041bd39 Mon Sep 17 00:00:00 2001 From: gmc Date: Fri, 26 Jul 2024 18:33:17 +0200 Subject: [PATCH 2/4] [TF-1552] custom navi button --- ...ViewController+TokensViewControlling.swift | 88 +++++++++++++++---- 1 file changed, 72 insertions(+), 16 deletions(-) diff --git a/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift b/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift index 26121326..4c578a05 100644 --- a/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift +++ b/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift @@ -126,48 +126,104 @@ extension TokensViewController: TokensViewControlling { tokensView.dragInteractionEnabled = false } + class DualImageButton: UIButton { + let imageView1 = UIImageView(image: Asset.navibarNewsIcon.image) + let imageView2 = UIImageView(image: Asset.badge.image) + + override init(frame: CGRect) { + super.init(frame: frame) + setupViews() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupViews() + } + + private func setupViews() { + imageView1.translatesAutoresizingMaskIntoConstraints = false + addSubview(imageView1) + + imageView2.translatesAutoresizingMaskIntoConstraints = false + addSubview(imageView2) + imageView2.isHidden = true + + // Constraints for imageView1 to be centered + NSLayoutConstraint.activate([ + imageView1.centerXAnchor.constraint(equalTo: centerXAnchor), + imageView1.centerYAnchor.constraint(equalTo: centerYAnchor) + ]) + + NSLayoutConstraint.activate([ + imageView2.topAnchor.constraint(equalTo: topAnchor, constant: 4), + imageView2.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -2), + imageView2.widthAnchor.constraint(equalToConstant: 3), + imageView2.heightAnchor.constraint(equalToConstant: 3) + ]) + } + } + + private func animate(_ barButtonItem: UIBarButtonItem) { let angle: Double = .pi / 8 let numberOfFrames: Double = 5 let frameDuration = Double(1/numberOfFrames) - UIView.animateKeyframes(withDuration: 1, delay: 0) { + let button = barButtonItem.customView as? DualImageButton + + UIView.animateKeyframes(withDuration: 1, delay: 0, animations: { UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: frameDuration) { - barButtonItem.customView?.transform = CGAffineTransform(rotationAngle: -angle) + button?.imageView1.transform = CGAffineTransform(rotationAngle: -angle) } UIView.addKeyframe(withRelativeStartTime: frameDuration, relativeDuration: frameDuration) { - barButtonItem.customView?.transform = CGAffineTransform(rotationAngle: +angle) + button?.imageView1.transform = CGAffineTransform(rotationAngle: +angle) } UIView.addKeyframe(withRelativeStartTime: 2*frameDuration, relativeDuration: frameDuration) { - barButtonItem.customView?.transform = CGAffineTransform(rotationAngle: -angle) + button?.imageView1.transform = CGAffineTransform(rotationAngle: -angle) } UIView.addKeyframe(withRelativeStartTime: 3*frameDuration, relativeDuration: frameDuration) { - barButtonItem.customView?.transform = CGAffineTransform(rotationAngle: +angle) + button?.imageView1.transform = CGAffineTransform(rotationAngle: +angle) } UIView.addKeyframe(withRelativeStartTime: 4*frameDuration, relativeDuration: frameDuration) { - barButtonItem.customView?.transform = CGAffineTransform.identity + button?.imageView1.transform = CGAffineTransform.identity } - } + }, completion: { _ in + button?.imageView2.isHidden = false + UIView.animate(withDuration: 0.4, + animations: { + button?.imageView2.transform = CGAffineTransform(scaleX: 12.0/3.0, y: 12.0/3.0) + }, + completion: { _ in + UIView.animate(withDuration: 0.2, + animations: { + button?.imageView2.transform = CGAffineTransform(scaleX: 8.0/3.0, y: 8.0/3.0) + }) + }) + }) } // MARK: - Navibar icons func updateAddIcon(using state: TokensViewControllerAddState) { func createNewsIcon() -> UIBarButtonItem { - let img: UIImage = { - presenter.hasUnreadNews ? Asset.navibarNewsIconBadge.image : Asset.navibarNewsIcon.image - }() - let naviButton = UIButton(type: .custom) - naviButton.translatesAutoresizingMaskIntoConstraints = false - naviButton.setBackgroundImage(img, for: .normal) - naviButton.addTarget(self, action: #selector(showNotifications), for: .touchUpInside) - naviButton.accessibilityLabel = T.Commons.notifications +// let img: UIImage = { +// presenter.hasUnreadNews ? Asset.navibarNewsIconBadge.image : Asset.navibarNewsIcon.image +// }() + + let naviButton = DualImageButton() +// naviButton.translatesAutoresizingMaskIntoConstraints = false +// naviButton.setBackgroundImage(img, for: .normal) + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(showNotifications)) + +// naviButton.target(forAction: #selector(showNotifications), withSender: self) +// naviButton.accessibilityLabel = T.Commons.notifications let uiBarButtonItem = UIBarButtonItem(customView: naviButton) - if presenter.hasUnreadNews { + uiBarButtonItem.customView?.addGestureRecognizer(tapGestureRecognizer) + if !presenter.hasUnreadNews { animate(uiBarButtonItem) } From b2cd42d48d5337b8dff825099c4e774dac28b888 Mon Sep 17 00:00:00 2001 From: gmc Date: Sat, 27 Jul 2024 18:09:28 +0200 Subject: [PATCH 3/4] [TF-1552] cleanup --- .../Contents.json | 2 +- .../Badge.imageset/Ellipse 10.pdf | 71 ++++++ .../NavibarNewsIconBadge.pdf | Bin 2645 -> 0 bytes TwoFAS/TwoFAS/Other/Generated/Assets.swift | 2 +- .../News/Presenter/NewsPresenter.swift | 3 +- .../Tokens/Presenter/TokensPresenter.swift | 20 +- ...ViewController+TokensViewControlling.swift | 227 +++++++++--------- .../Tokens/View/TokensViewController.swift | 2 + 8 files changed, 208 insertions(+), 119 deletions(-) rename TwoFAS/TwoFAS/Other/Assets.xcassets/{NavibarNewsIconBadge.imageset => Badge.imageset}/Contents.json (72%) create mode 100644 TwoFAS/TwoFAS/Other/Assets.xcassets/Badge.imageset/Ellipse 10.pdf delete mode 100644 TwoFAS/TwoFAS/Other/Assets.xcassets/NavibarNewsIconBadge.imageset/NavibarNewsIconBadge.pdf diff --git a/TwoFAS/TwoFAS/Other/Assets.xcassets/NavibarNewsIconBadge.imageset/Contents.json b/TwoFAS/TwoFAS/Other/Assets.xcassets/Badge.imageset/Contents.json similarity index 72% rename from TwoFAS/TwoFAS/Other/Assets.xcassets/NavibarNewsIconBadge.imageset/Contents.json rename to TwoFAS/TwoFAS/Other/Assets.xcassets/Badge.imageset/Contents.json index cd45fd1c..b2f50124 100644 --- a/TwoFAS/TwoFAS/Other/Assets.xcassets/NavibarNewsIconBadge.imageset/Contents.json +++ b/TwoFAS/TwoFAS/Other/Assets.xcassets/Badge.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "NavibarNewsIconBadge.pdf", + "filename" : "Ellipse 10.pdf", "idiom" : "universal" } ], diff --git a/TwoFAS/TwoFAS/Other/Assets.xcassets/Badge.imageset/Ellipse 10.pdf b/TwoFAS/TwoFAS/Other/Assets.xcassets/Badge.imageset/Ellipse 10.pdf new file mode 100644 index 00000000..758b5af2 --- /dev/null +++ b/TwoFAS/TwoFAS/Other/Assets.xcassets/Badge.imageset/Ellipse 10.pdf @@ -0,0 +1,71 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.929412 0.109804 0.141176 scn +3.000000 1.500000 m +3.000000 0.671573 2.328427 0.000000 1.500000 0.000000 c +0.671573 0.000000 0.000000 0.671573 0.000000 1.500000 c +0.000000 2.328427 0.671573 3.000000 1.500000 3.000000 c +2.328427 3.000000 3.000000 2.328427 3.000000 1.500000 c +h +f +n +Q + +endstream +endobj + +3 0 obj + 371 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 3.000000 3.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000000461 00000 n +0000000483 00000 n +0000000654 00000 n +0000000728 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +787 +%%EOF \ No newline at end of file diff --git a/TwoFAS/TwoFAS/Other/Assets.xcassets/NavibarNewsIconBadge.imageset/NavibarNewsIconBadge.pdf b/TwoFAS/TwoFAS/Other/Assets.xcassets/NavibarNewsIconBadge.imageset/NavibarNewsIconBadge.pdf deleted file mode 100644 index a50f059aa1895f8190807dc778ebd64d69254b34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2645 zcmZXWc|6qlAIHri%{X$8$@O)NBQ%E@a>QU#V-b^|b1^d+jF_=AqmX8u#YByeTrm>m z3K?sYR#LI85y{H0EKzH8Y%P*RJJVsW_aEQK`}2By-sks^&lB#0ceg@qw}n7Z01{wO zVjuwE=meP0O1}>W>fLh{fr2zI6`a)Pbxi%9SvBEuM@?R zI0K8#q>|$xiJ+oluM;}93R%lXxM6a8f@<*G2@_D8H#767qFjrj%qHe~N+D+cX!o}< zsL%ytkM~I_#O~D>Yd?PHJo#Z|6|o%t;Y#=6_2sw8tG}59lnixW?S7v!c}>3|;6-HI zL;Xs|lQur3Vwe$r&FLDlQ8OX8D{P|ga914xTpS#fgt^pwrp0ngcEG}VTk$sn1?_|1 z)jUek(xD9JLNEGTL0eE`J{Dn?$LjMas27gxJxbitcckIR)kjhbRRwhxWB3jWwWNp7 z>zl@C-?#3SKT!G%+5tfHo+VtfB<6#s|o>5Yy33j<4w=#z;FXO>ir&sz|g zyUb0DgH0ne+YFixbc-O*?B4UXuFC`sT|y4b>ifJqKW|g8?tg|O7zU+zuSp$dRF`Ci zMglsmCFz4%ri-p60rA%IW~0-FcWd=|`#3R)f%aY@dm^*6LNq8cM8xqyUqP)ACr``C z-pfEXz|m`MJ|=x1+apm$^+~|xv0z!Iwsi%E@Vh1m6GPkoRMW;X#1h|gbXRN6Xj0M(i+v)dv8g1xBng=5NMutaElny;0n{AVt<`G1&=ukXK zgWSq`?n`+Cf2kJ;*Bo-LoG3^@%%A7pI=epCzq9hp83N+ zc~b#4wn@$$=B^j`1b0$9$?z-HRhj8hWy7a4Z{1vj#nOj_ljrqyEO$7@umtf zX6K_K5U(R2guH@us!1Oz4Viy65Z={J)FG~)noC})aqc?H?RjRCZN(4WF_s$LnEyjk zL;M@?!xzTKejJeLPH9(CAxLMXZwJQQVvc&&USv-Epc%N^vMfct>dH&VFI01?=jD{M*YaTVT z6E+`2vJWl6hv$F3O<(gf^D}YyU?#JhsLhj&>fpu4yP${Iu>ylUA2N4P<^0J9jLBn6 z_p0b~^7NEjN6;?|*GZLOs0o_SN}uP#9F^c=)ZnckZ**AUjR&{qmAavUKV;Y45_NQ5 z(g?a+!AdbRm_K_}J+PysHz#{*`o+B2;c2CN4i#sMk2PvMkI43@R;AgN6K8FVEc;z& zGS3n+@)sc(zEXKTT;&`iWCC@;NH*tSReLE`HuYp-*-aHy<%!!D(K$1X({tVq7~Vcb zbkRgUF;ZZXaZa^@ZZt~+vkRIlZRt`|qrK2r z9LYM{q~zJJ%ay+FFmCS!b~Tr+?_Dv#Utb9oeOn4ij}NLBZJu}Ms8vgM(nwrmvt|A2 zs&R;ZCcdoIAstVpZK>vP=hO6T7jKGenTK$TJ8ng@Cd1-zYSDJOc3niZgZ+M~^yQvy zSid(reT{Eb)TDQ3mPPNYfe%*{_XVmff&MR&J}{U3lhb&}S(m0+f!S~5j{MD1(Y6t;x4?zHN8V}j z4m&_odLB)J6ENZzAVfN~->FVkxeRep3h!yIJ>{TXWp$E22uOKRRtOTA0`6;TJgqJ%EQT5a1MeNRa#954f6p&MbwfGOKiFZyw9w!!Gns zlrpfH%D=)$W2M6KjM+icx_e1-A&8%Y-5S|EffNgXTv}-ve8XKGo~kYCf1`7{I%xFw zzGA^jhwwvX_@cbC#3S@MP#?kqff>gn8EE|M$xJ{7x0`t8Xo|1xe8>il!x zx?IZkb(CG{|3kV7Q3}Tn3JR^}dI0x}HEkfKXAen%;QOAvuAdW@-r+?NbuKBVJn}1}I z49MCjC~54|;BI6#IhGOi&(K6BH4=gZY$2b|QLINK>fW9GFi~E$= zhJ;4i{YOHfZ2x@=ZTIh7Xlt9l0@+M5EtblJNd6uHFJ6ZkixEdP2h3;@=AV{=ATk(i iKr-;h1ta#+lBweEKSCav$^JMF8iTfmz~Q_0y8j7Nc1Mi> diff --git a/TwoFAS/TwoFAS/Other/Generated/Assets.swift b/TwoFAS/TwoFAS/Other/Generated/Assets.swift index 47f3c54b..723d1ffb 100644 --- a/TwoFAS/TwoFAS/Other/Generated/Assets.swift +++ b/TwoFAS/TwoFAS/Other/Generated/Assets.swift @@ -33,6 +33,7 @@ internal enum Asset { internal static let deleteSettingsIcon = ImageAsset(name: "DeleteSettingsIcon") internal static let backupDeleted = ImageAsset(name: "backupDeleted") internal static let backupSettingsIcon = ImageAsset(name: "backupSettingsIcon") + internal static let badge = ImageAsset(name: "Badge") internal static let barsBackground = ImageAsset(name: "BarsBackground") internal static let bracket = ImageAsset(name: "Bracket") internal static let aboutExtension = ImageAsset(name: "AboutExtension") @@ -109,7 +110,6 @@ internal enum Asset { internal static let naviIconAddFirst = ImageAsset(name: "NaviIconAddFirst") internal static let naviSortIcon = ImageAsset(name: "NaviSortIcon") internal static let navibarNewsIcon = ImageAsset(name: "NavibarNewsIcon") - internal static let navibarNewsIconBadge = ImageAsset(name: "NavibarNewsIconBadge") internal static let notificationFeatures = ImageAsset(name: "NotificationFeatures") internal static let notificationNews = ImageAsset(name: "NotificationNews") internal static let notificationTips = ImageAsset(name: "NotificationTips") diff --git a/TwoFAS/TwoFAS/Root/Modules/News/Presenter/NewsPresenter.swift b/TwoFAS/TwoFAS/Root/Modules/News/Presenter/NewsPresenter.swift index 16af6a36..cff3f054 100644 --- a/TwoFAS/TwoFAS/Root/Modules/News/Presenter/NewsPresenter.swift +++ b/TwoFAS/TwoFAS/Root/Modules/News/Presenter/NewsPresenter.swift @@ -91,7 +91,8 @@ private extension NewsPresenter { func reload() { let now = Date() interactor.fetchList { [weak self] news in - let cells = news.map { entry in + let sortedNews = news.sorted { $0.publishedAt > $1.publishedAt } + var cells = sortedNews.map { entry in NewsCell( icon: entry.icon.image, title: entry.message ?? entry.link?.absoluteString ?? "", diff --git a/TwoFAS/TwoFAS/Root/Modules/Tokens/Presenter/TokensPresenter.swift b/TwoFAS/TwoFAS/Root/Modules/Tokens/Presenter/TokensPresenter.swift index 92a7489d..6086eede 100644 --- a/TwoFAS/TwoFAS/Root/Modules/Tokens/Presenter/TokensPresenter.swift +++ b/TwoFAS/TwoFAS/Root/Modules/Tokens/Presenter/TokensPresenter.swift @@ -531,7 +531,7 @@ private extension TokensPresenter { }() if interactor.hasServices { - updateAddServiceIcon() + updateNaviIcons() view?.showList() if Set(currentServices) != Set(newServices) || changeRequriesTokenRefresh { @@ -552,7 +552,7 @@ private extension TokensPresenter { if !isSearching && currentState == .edit { setCurrentState(.normal) } - updateAddServiceIcon() + updateNaviIcons() interactor.stopCounters() updateEditStateButton() @@ -595,17 +595,23 @@ private extension TokensPresenter { } func updateNewsIcon() { - updateAddServiceIcon() + updateNaviIcons() interactor.fetchNews { [weak self] in - self?.updateAddServiceIcon() + self?.updateNaviIcons(hasUnreadNews: self?.hasUnreadNews ?? false) } } - private func updateAddServiceIcon() { + private func updateNaviIcons(hasUnreadNews: Bool = false) { if interactor.hasServices { - view?.updateAddIcon(using: mapButtonStateFor(currentState, isFirst: false)) + view?.updateNaviIcons( + using: mapButtonStateFor(currentState, isFirst: false), + hasUnreadNews: hasUnreadNews + ) } else { - view?.updateAddIcon(using: mapButtonStateFor(currentState, isFirst: !isSearching)) + view?.updateNaviIcons( + using: mapButtonStateFor(currentState, isFirst: !isSearching), + hasUnreadNews: hasUnreadNews + ) } } } diff --git a/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift b/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift index 4c578a05..bedce86d 100644 --- a/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift +++ b/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift @@ -29,7 +29,7 @@ protocol TokensViewControlling: AnyObject { func enableDragging() func disableDragging() - func updateAddIcon(using state: TokensViewControllerAddState) + func updateNaviIcons(using state: TokensViewControllerAddState, hasUnreadNews: Bool) func updateEditState(using state: TokensViewControllerEditState) func lockBars() @@ -126,108 +126,25 @@ extension TokensViewController: TokensViewControlling { tokensView.dragInteractionEnabled = false } - class DualImageButton: UIButton { - let imageView1 = UIImageView(image: Asset.navibarNewsIcon.image) - let imageView2 = UIImageView(image: Asset.badge.image) - - override init(frame: CGRect) { - super.init(frame: frame) - setupViews() - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - setupViews() - } - - private func setupViews() { - imageView1.translatesAutoresizingMaskIntoConstraints = false - addSubview(imageView1) - - imageView2.translatesAutoresizingMaskIntoConstraints = false - addSubview(imageView2) - imageView2.isHidden = true - - // Constraints for imageView1 to be centered - NSLayoutConstraint.activate([ - imageView1.centerXAnchor.constraint(equalTo: centerXAnchor), - imageView1.centerYAnchor.constraint(equalTo: centerYAnchor) - ]) - - NSLayoutConstraint.activate([ - imageView2.topAnchor.constraint(equalTo: topAnchor, constant: 4), - imageView2.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -2), - imageView2.widthAnchor.constraint(equalToConstant: 3), - imageView2.heightAnchor.constraint(equalToConstant: 3) - ]) - } - } - - - private func animate(_ barButtonItem: UIBarButtonItem) { - let angle: Double = .pi / 8 - let numberOfFrames: Double = 5 - let frameDuration = Double(1/numberOfFrames) - - let button = barButtonItem.customView as? DualImageButton - - UIView.animateKeyframes(withDuration: 1, delay: 0, animations: { - UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: frameDuration) { - button?.imageView1.transform = CGAffineTransform(rotationAngle: -angle) - } - - UIView.addKeyframe(withRelativeStartTime: frameDuration, relativeDuration: frameDuration) { - button?.imageView1.transform = CGAffineTransform(rotationAngle: +angle) - } - - UIView.addKeyframe(withRelativeStartTime: 2*frameDuration, relativeDuration: frameDuration) { - button?.imageView1.transform = CGAffineTransform(rotationAngle: -angle) - } - - UIView.addKeyframe(withRelativeStartTime: 3*frameDuration, relativeDuration: frameDuration) { - button?.imageView1.transform = CGAffineTransform(rotationAngle: +angle) - } - - UIView.addKeyframe(withRelativeStartTime: 4*frameDuration, relativeDuration: frameDuration) { - button?.imageView1.transform = CGAffineTransform.identity - } - }, completion: { _ in - button?.imageView2.isHidden = false - UIView.animate(withDuration: 0.4, - animations: { - button?.imageView2.transform = CGAffineTransform(scaleX: 12.0/3.0, y: 12.0/3.0) - }, - completion: { _ in - UIView.animate(withDuration: 0.2, - animations: { - button?.imageView2.transform = CGAffineTransform(scaleX: 8.0/3.0, y: 8.0/3.0) - }) - }) - }) - } - // MARK: - Navibar icons - func updateAddIcon(using state: TokensViewControllerAddState) { - func createNewsIcon() -> UIBarButtonItem { -// let img: UIImage = { -// presenter.hasUnreadNews ? Asset.navibarNewsIconBadge.image : Asset.navibarNewsIcon.image -// }() - - let naviButton = DualImageButton() -// naviButton.translatesAutoresizingMaskIntoConstraints = false -// naviButton.setBackgroundImage(img, for: .normal) - let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(showNotifications)) - -// naviButton.target(forAction: #selector(showNotifications), withSender: self) -// naviButton.accessibilityLabel = T.Commons.notifications - - let uiBarButtonItem = UIBarButtonItem(customView: naviButton) - uiBarButtonItem.customView?.addGestureRecognizer(tapGestureRecognizer) - if !presenter.hasUnreadNews { - animate(uiBarButtonItem) + func updateNaviIcons(using state: TokensViewControllerAddState, hasUnreadNews: Bool) { + func createNewsButton() -> UIBarButtonItem { + print("Create news icon called") + if presenter.hasUnreadNews { + let naviButton = UnreadNewsNaviButton() + naviButton.translatesAutoresizingMaskIntoConstraints = false + naviButton.accessibilityLabel = T.Commons.notifications + naviButton.addTarget(self, action: #selector(showNotifications), for: .touchUpInside) + naviButton.animate() + return UIBarButtonItem(customView: naviButton) + } else { + let naviButton = UIButton(type: .custom) + naviButton.setBackgroundImage(Asset.navibarNewsIcon.image, for: .normal) + naviButton.addTarget(self, action: #selector(showNotifications), for: .touchUpInside) + naviButton.translatesAutoresizingMaskIntoConstraints = false + naviButton.accessibilityLabel = T.Commons.notifications + return UIBarButtonItem(customView: naviButton) } - - return uiBarButtonItem } func createAddButton(image: UIImage) -> UIBarButtonItem { @@ -243,15 +160,33 @@ extension TokensViewController: TokensViewControlling { switch state { case .firstTime: - navigationItem.rightBarButtonItems = [ - createAddButton(image: Asset.naviIconAddFirst.image), - createNewsIcon() - ] + if let newsButton, hasUnreadNews { + navigationItem.rightBarButtonItems = [ + createAddButton(image: Asset.naviIconAddFirst.image), + newsButton + ] + } else { + let newsButton = createNewsButton() + self.newsButton = newsButton + navigationItem.rightBarButtonItems = [ + createAddButton(image: Asset.naviIconAddFirst.image), + newsButton + ] + } case .normal: - navigationItem.rightBarButtonItems = [ - createAddButton(image: Asset.naviIconAdd.image), - createNewsIcon() - ] + if let newsButton, !hasUnreadNews { + navigationItem.rightBarButtonItems = [ + createAddButton(image: Asset.naviIconAdd.image), + newsButton + ] + } else { + let newsButton = createNewsButton() + self.newsButton = newsButton + navigationItem.rightBarButtonItems = [ + createAddButton(image: Asset.naviIconAdd.image), + newsButton + ] + } case .none: let buttonSection = UIBarButtonItem( image: Asset.addCategory.image, @@ -441,3 +376,77 @@ extension TokensViewController { presenter.handleAppUnlocked() } } + +private extension TokensViewController { + final class UnreadNewsNaviButton: UIButton { + let newsImageView = UIImageView(image: Asset.navibarNewsIcon.image) + let badgeImageView = UIImageView(image: Asset.badge.image) + + private let badgeWidth: CGFloat = 3 + + override init(frame: CGRect) { + super.init(frame: frame) + setupViews() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupViews() + } + + private func setupViews() { + newsImageView.translatesAutoresizingMaskIntoConstraints = false + addSubview(newsImageView) + + badgeImageView.translatesAutoresizingMaskIntoConstraints = false + addSubview(badgeImageView) + badgeImageView.isHidden = true + + NSLayoutConstraint.activate([ + newsImageView.centerXAnchor.constraint(equalTo: centerXAnchor), + newsImageView.centerYAnchor.constraint(equalTo: centerYAnchor), + badgeImageView.topAnchor.constraint(equalTo: topAnchor, constant: Theme.Metrics.halfSpacing), + badgeImageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -Theme.Metrics.quaterSpacing), + badgeImageView.widthAnchor.constraint(equalToConstant: badgeWidth), + badgeImageView.heightAnchor.constraint(equalToConstant: badgeWidth) + ]) + } + + func animate() { + let angle: Double = .pi / 12 + let numberOfFrames: Double = 5 + let frameDuration = Double(1/numberOfFrames) + + UIView.animateKeyframes(withDuration: 1, delay: 0, animations: { [newsImageView] in + UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: frameDuration) { + newsImageView.transform = CGAffineTransform(rotationAngle: -angle) + } + + UIView.addKeyframe(withRelativeStartTime: frameDuration, relativeDuration: frameDuration) { + newsImageView.transform = CGAffineTransform(rotationAngle: +angle) + } + + UIView.addKeyframe(withRelativeStartTime: 2*frameDuration, relativeDuration: frameDuration) { + newsImageView.transform = CGAffineTransform(rotationAngle: -angle) + } + + UIView.addKeyframe(withRelativeStartTime: 3*frameDuration, relativeDuration: frameDuration) { + newsImageView.transform = CGAffineTransform(rotationAngle: +angle) + } + + UIView.addKeyframe(withRelativeStartTime: 4*frameDuration, relativeDuration: frameDuration) { + newsImageView.transform = CGAffineTransform.identity + } + }, completion: { [badgeImageView, badgeWidth] _ in + badgeImageView.isHidden = false + UIView.animate(withDuration: 0.3, animations: { + badgeImageView.transform = CGAffineTransform(scaleX: 12.0/badgeWidth, y: 12.0/badgeWidth) + }, completion: { _ in + UIView.animate(withDuration: 0.2, animations: { + badgeImageView.transform = CGAffineTransform(scaleX: 8.0/badgeWidth, y: 8.0/badgeWidth) + }) + }) + }) + } + } +} diff --git a/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController.swift b/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController.swift index 7a56a64a..02f4bc1f 100644 --- a/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController.swift +++ b/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController.swift @@ -26,6 +26,8 @@ final class TokensViewController: UIViewController { var addButton: UIBarButtonItem? { navigationItem.rightBarButtonItem } + var newsButton: UIBarButtonItem? + private(set) var tokensView: TokensView! private(set) var dataSource: UICollectionViewDiffableDataSource! From 32d7692dfaaa4079df5d0457bac59d8d4f4c2452 Mon Sep 17 00:00:00 2001 From: gmc Date: Tue, 30 Jul 2024 19:31:58 +0200 Subject: [PATCH 4/4] [TF-1552] pr changes and fix formatting --- ...ViewController+TokensViewControlling.swift | 72 +++++++++++-------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift b/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift index bedce86d..78df38ae 100644 --- a/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift +++ b/TwoFAS/TwoFAS/Root/Modules/Tokens/View/TokensViewController+TokensViewControlling.swift @@ -129,7 +129,6 @@ extension TokensViewController: TokensViewControlling { // MARK: - Navibar icons func updateNaviIcons(using state: TokensViewControllerAddState, hasUnreadNews: Bool) { func createNewsButton() -> UIBarButtonItem { - print("Create news icon called") if presenter.hasUnreadNews { let naviButton = UnreadNewsNaviButton() naviButton.translatesAutoresizingMaskIntoConstraints = false @@ -405,8 +404,11 @@ private extension TokensViewController { NSLayoutConstraint.activate([ newsImageView.centerXAnchor.constraint(equalTo: centerXAnchor), newsImageView.centerYAnchor.constraint(equalTo: centerYAnchor), - badgeImageView.topAnchor.constraint(equalTo: topAnchor, constant: Theme.Metrics.halfSpacing), - badgeImageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -Theme.Metrics.quaterSpacing), + badgeImageView.topAnchor.constraint(equalTo: topAnchor, constant: Theme.Metrics.halfSpacing), + badgeImageView.trailingAnchor.constraint( + equalTo: trailingAnchor, + constant: -Theme.Metrics.quaterSpacing + ), badgeImageView.widthAnchor.constraint(equalToConstant: badgeWidth), badgeImageView.heightAnchor.constraint(equalToConstant: badgeWidth) ]) @@ -415,38 +417,46 @@ private extension TokensViewController { func animate() { let angle: Double = .pi / 12 let numberOfFrames: Double = 5 - let frameDuration = Double(1/numberOfFrames) + let frameDuration = Double(0.7 / numberOfFrames) - UIView.animateKeyframes(withDuration: 1, delay: 0, animations: { [newsImageView] in - UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: frameDuration) { - newsImageView.transform = CGAffineTransform(rotationAngle: -angle) + UIView.animateKeyframes( + withDuration: 1, + delay: 0, + animations: { [newsImageView] in + UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: frameDuration) { + newsImageView.transform = CGAffineTransform(rotationAngle: -angle) + } + UIView.addKeyframe(withRelativeStartTime: frameDuration, relativeDuration: frameDuration) { + newsImageView.transform = CGAffineTransform(rotationAngle: +angle) + } + UIView.addKeyframe(withRelativeStartTime: 2 * frameDuration, relativeDuration: frameDuration) { + newsImageView.transform = CGAffineTransform(rotationAngle: -angle) + } + UIView.addKeyframe(withRelativeStartTime: 3 * frameDuration, relativeDuration: frameDuration) { + newsImageView.transform = CGAffineTransform(rotationAngle: +angle) + } + UIView.addKeyframe(withRelativeStartTime: 4 * frameDuration, relativeDuration: frameDuration) { + newsImageView.transform = CGAffineTransform.identity + } + }, + completion: { [weak self] _ in + self?.badgeImageView.isHidden = false + self?.animateBadge() } + ) + } - UIView.addKeyframe(withRelativeStartTime: frameDuration, relativeDuration: frameDuration) { - newsImageView.transform = CGAffineTransform(rotationAngle: +angle) + private func animateBadge() { + UIView.animate( + withDuration: 0.2, + animations: { [badgeImageView, badgeWidth] in + badgeImageView.transform = CGAffineTransform(scaleX: 12.0 / badgeWidth, y: 12.0 / badgeWidth) + }, completion: { [badgeImageView, badgeWidth] _ in + UIView.animate(withDuration: 0.15) { + badgeImageView.transform = CGAffineTransform(scaleX: 8.0 / badgeWidth, y: 8.0 / badgeWidth) + } } - - UIView.addKeyframe(withRelativeStartTime: 2*frameDuration, relativeDuration: frameDuration) { - newsImageView.transform = CGAffineTransform(rotationAngle: -angle) - } - - UIView.addKeyframe(withRelativeStartTime: 3*frameDuration, relativeDuration: frameDuration) { - newsImageView.transform = CGAffineTransform(rotationAngle: +angle) - } - - UIView.addKeyframe(withRelativeStartTime: 4*frameDuration, relativeDuration: frameDuration) { - newsImageView.transform = CGAffineTransform.identity - } - }, completion: { [badgeImageView, badgeWidth] _ in - badgeImageView.isHidden = false - UIView.animate(withDuration: 0.3, animations: { - badgeImageView.transform = CGAffineTransform(scaleX: 12.0/badgeWidth, y: 12.0/badgeWidth) - }, completion: { _ in - UIView.animate(withDuration: 0.2, animations: { - badgeImageView.transform = CGAffineTransform(scaleX: 8.0/badgeWidth, y: 8.0/badgeWidth) - }) - }) - }) + ) } } }