From 79dcfa7e08a85778ede61fd5ecefba1e19257b6c Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 4 Aug 2021 21:24:48 +0300 Subject: [PATCH] Various Fixes --- .../CameraPlaceholder.jpg | Bin 0 -> 13897 bytes .../Sources/TGCameraController.m | 27 +++++--- .../MediaNavigationAccessoryHeaderNode.swift | 64 +++++++++++------- 3 files changed, 57 insertions(+), 34 deletions(-) create mode 100644 submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/CameraPlaceholder.jpg diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/CameraPlaceholder.jpg b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/CameraPlaceholder.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d7d326fa0ecd3db0e318a352497817a4eecd0072 GIT binary patch literal 13897 zcmeHNd0bQ1p1(=h!@jCejgeKz3JHNHDys$)1i~s<$#MgMKtd8KQr=W>DIi+*O<$iP zVimQ5$b;4u0R^S4;DSr7AlTNn7OG6OZ)P&*-YlT?y?Hb5GxN`QlY8&){Lb(E&iD5_ zzjN+6_a60_dK_O8o-YwY5SI%rhakuVvco_S4p12USKxxt&{h`!jYVlB?-z`bh7Vx@ z-wL@xM&Nvbu|{cIK>r8ERx1M(VjQ$ID2-VJ8G?L-YmIT%(+e=3^K{ZsJxhTM0YgV= zdq4+cR_OF&;xQqRfkqD&!i3JzSi8A;Ka}YBn2ZuP9gX`Df(*U`wt`=ejS5{St)@#5Fpg~ntbM$_lEi@{1zc#kiY6#ko z)wK;Yy&pG6H#=$T0ugplgCB$>1hr^l5h0-#oK^;*4Yl-YKn^Yr`p1BSQvc9@7sQtH0Ip!QBL$NZ&C>?tde4 z2?D+_i=a|SKwJ}q{A^gkC-{>Y+1Ug!g-)h}k`QD8YD4-gHVR2;+8^{xgFV0lC?5r7 zfYycZTLFdOL;VeUI)VYv2Il}0RDKSpYj1>i1q-Hb&(C4%}0O79KHoXo5w-E5k?m; zAOhybB17ifSY)_z{#b;?=;F~Z0;`Wxqd;w4Yr!4CiQd&?>YpHUbgbAE%7rlMr;sI@ zf<#*pIrTkgJ7jKRVq$7yZfa_7gEzz5I9QpRTRAMSvvaVsTVP|39@?8G@%j~GfyY}| zT3B0JTH9M%TG}HQOM6Wbn}0-s`T}HQ0_}(P<1j=BYlFerVANj&BhA!(m{kC!AqG7F zVC2Aok>9}3$k@cx43Bv|6T}ftmuU@Q3@|vX0oKsi$ixt5?h7()a0W|lX@(jojf^kK0RVdFvsPvn0GeQ6uyEA%6 z0EmN5qVRa83ZBqI_}d6P<{+r=LgqNMP8(Up!B zgPU!u#ZpJNu$rfyQ3@t@+$eTmfJqNwlGXdv;NcMi#~zy#5f?Y4Rd{T_$Z@Yd;~f%x z$TY<9V@GO@S4ebi+m3QKv*;SP%yFUVfhbU;i_cO|-u46DiFoz(=go00JSWe9!N(jT zo^>E#@WG^0&l;~!ZBl!v1vZL3&mS=Ele*42?1^i7E)H7hyFu`^u(IhW?a3vVKle+; zUa{MYt0FNOTr+k>ybmz6xu(V=s!CGsv3>eu>(1%(>XGLb-|wmjFLnClMQ!ir_qYD) z)pLcS;8#6d&@<$GBAKbJ@^|j%w>Ih><NvLw&U^KA##t6>w+%sksU`Hdnn0& zDCO@e+)zSF?wz7-Zc2?jygKFapKm>j3sMa{@2*|1zHuKeDeeyk;2mOzoZoHU_VKnS zo_p;glY_pa6r=~4ei7_l-|DzD;ED4HFSqXC?aVK-OOBn!r@oVrT1HMTKW)G9aqWrL zm!A)>zJJuY#$xcov5M@y)dfS9J^sf&#xGPE4u#C_Bd`DAVAjSZn})+Cw;Wx3zNz&5 zuzS=KCl{M>XQYY>D4hNX`c!1L_*VT%b zUDh>4cs|n2?!Fgec(kIR{*zkrMD}k(?o)Ahn}6UHkGc<@UUMtn;>g)}g3zt!2ank^ zH!1O}j#k~vDHw9U$-Bvs1{<7L-#X?K>$tqQpLsUUE!|EUv^FRq>&B&O_Ilet4!I3s z1|uC$xY_QHRK%IGT_W9>Q?_xiTkfF;e=e|`vVFdK<3PjMerX6Lni7SHkZ{XZHk%gg zc|LW9`w?%L)t>O6WpPn>m;d1hTU}br9NRun-|n{x%etA+<8UJGszq1RWJFIwR^_6} z-ZrVRn`iLguY_IV$Re*@HSBQ5p5+EDkwu=(x5+VNPGpfnzTS3f$Z&914ZgZKTQPaB zhRyXF>f#RtUs-(w+Y~R^R&z1fkYnEqYWeE%rOFLcsR1^imW>^^iHxCBsU^4FYf{nz zR!&+^{9&~V28RLhu-3MTXWJCRJ!QDo zo*GxgUEaC>Nbz>F2Cp3y^&Rz3RvB}%OJ6b-Vui4|2FMH-f?Qp|qYeCBu>OzgE%7f&H zD#%Q(mj)?RO^8&LqeWyc$dgD_k{nbe$Tvl$L%GT8G~W9Iq5?|-mpO}V=UKd7u9C=d zlpx;;kqniqa&$%jBLxb5A~s#g)h9v~IiYij9MxQcBakZfIZ^4V_wmfA|e zZPivt6K@tM&p@qK_o0#tHED2NjB0&`rcKc+xM0vyV2z0!X4*~y0v45>MY(GsidYPC zq~MCIu#rL<&Vj}zJqMZ9W1tP-s*_=$93=9!98~8EkcN<+1rWaBzo;Uh50(?i1(jWdP`Ge#(V$0?5HzhlO&~VJh1NiuAUfm=`GZqC zG9W_p4sCZrIl8j*vfo??X$)<_oIEKqo*}CWBY`Y0N2GkMp9)oA;i!R)1h+nd0Q0p7 zghJ*b6@dc9&IKZpHd0C@LRgs~AOS)TkAmXS;$ zNtftH$#5Q`eLczpIxP5kD%l!12doTLFtVhA;jF`6{+@Z5)@Q8?a1t>{oJc~C^IP-%6`W+7obDm@A!Do#2K%Pqdm$KsR(^_@RC;s3BV$d0G5RrfL4K1iBfVPX8&JE5Wu963QlBZ zrhTeu=8(qdL=M!V9=E!dp;E~MDU=)~nU8$eAq!>MlzhINLL*Zt(27<0a_|lvRuKd+ zm_-EPMmZk*L%_-+(|rKZ5okg6b3^abRp%gtjt}3L)5gwG zs`xoV7|{mwlO;fth$aw-Sxh0xpDqX>`B8oSNPH%ZPT~vw0{9|-roT|^rz@{n*YTzE zQPIOgBErJM!kA1ZD}olv0;E49!Y?E&oW}GIU_=lJB4MCdrpV@l*q3DU(_u=c98O0@ zg*hX13WEVSB?w)mcGL;sr)w#xWTj?^d)K?WS4f}o)^YAJueeoX_cl^}SwXi@OsTsA}aas!6KrT zDS3mVeFNxzd|#%BB%;AWk{^RbBME3U7D))x`7jOi1R5(qSAH{i8TGr^n%|)`bt`3J z)n>i|4o(Llq8sX?btBpDHV){t3p4mR=`aXh3X$?ZHs>FU8x79?Q*+R^)-)95^)^KF z<~@jX0Rf(F2&sRruGBsL-rnDO;H?MVdf=@G-g@Az2mT*^D|>q70b>Wv4ySQnN5QnKqIkwF)Q?xy+#yuxh!rSid|mxQiyfQ!n=lt;l~T}k2C7`k{fKe;;XG2X&X{FH!kxSw9|(=hdFt^ zJZHcddm?UScK(I=2d4KrCGVZR_ObWJejGbaYRwcLX=*sWO8wQ-g-K6fapCE32bcQ`i&Zp~w$wUCDQv}Ov6tP5Kc~J^%DL>K-N&h#(AM zhK9kQbxt4GxZ^IeW$dZzBleKTqNt5I{&-HVdw_JT+NHF5sl&0cYGz6oak#e8*GAfv z=)>vq{Wx@Yy%~WSv}?(>-Nr-%xGOquObI zpOY`bDnb~WoYq`0d-2&y4!hK}Y^6y=?~88?BVMLeJbo>dO-@ zCpxq~6AztO{^PO!_A@7&>%{|#;j4?nDOU$}UdEM;ZaD>)n>V#?^cD9b``d2(Q20!F zVS;mR%NkorT$lUsZc4fMf@r8o+_$m)+So3aQm;ehT9;LHj{HY5e+K(9b-K87S7QGN z2kH%Hc1uzcrracwv3;X5zyywJ>% zR~y@oxF2LhEPW!LrA=_b=CtpLocuzA4nff~+j|m+FBhfQd|Q)Zc$^G?cid@rlsYzT z_^IR+{JFsVj`KRW{mcwg$}fz!b?vw17%W-bAu0+-P(P|Siv_5Ayu;nE0~@N%g!P85 z-TC!9e3NOx?oP+L_t)f)PrPd{JF=UewkdHD-0RxcZz~#DDEF#g01xI8wr^@{U~fIU zsj!{w9KNitd$tN{tL%#1n%380!)#TdHF}UM< zcb`ui;GRWGXocpkSHG?FicjxroGE{?ccnqkJ!|o`FH1g8OZ1(9XO4|k7RIN1%wiX< zdu%JrY;f%+2FTk*{UfQ_62_iX`>OGfb;Yr%`Hiuerv%NZKFRIDOyIgTzjcq2akFP< zTBQ}9OC4c=d1<{JeX=Mm#Q)|sw_h);{PdhG%-Gvzg)$#iL35VVt*ay66wGq*UJSB=IlMQ zbT`jp0G%irsK3&Y`X%tZ?*wPep-al%yJ)!dl%Q?b<8Mpq2A=iDtaJCNN`qcB4;EE$ zs#2}1#y7BcwRDpzIJZ)xxj*l?N?x(<=iP^vJo)P*&bb#>HwPLOtJ^(Kk*QaU%+I~J zL{3^XE3S^i2JS7UoHN$tdwd}7|m{gD?Bf{|KY|30cZ1(N}uC)PlW3x7PrO}mSxACb001z z?IsmvUmM%Y6pZb4Nw}Y~AZ1aMUzgYEsHEu^&UHzJhJ=HToMh33r8D8x#A|F9x68kE z8vq~B*4z5t9HKv5&szi!?sRP~@4iGfq(3rmq+X~=uJ^uq;a^%4+p5wc{cm2Km0Pvd zKN$}{cvkvp$~TyT#gySW`GrQVmyNH{x5gB6%Je0pIJn4S32X^dxDJB)=?-iD|UyGvc@y@m@_wd=r*_T-Q+2g5lkLBHVeaD<# zmRL{KIpdbPt}Q&z!-0Ng;ZbTW8fc_pU-Z5tY;#k(QwKpe+{tN36P3_~f8>~phGwbF z*Z-we#=|PFep^Oz0-cUZS#d7kCgbBR;WL&1L1&73#M^3*RT1vdrN^dOok;+!*%JH2 zD>ZjrnJbva79UwurX)-?bvH;qdwBYEpJp&-c3o%nDh3DacDO`=v00GYGI54`n~_^P zG*y;g8vr}?T@|hq73bIDAK@q9_8lF;E|EKCcFfSndhZarj&Ab)n3&>SXTRb6ijyyv z)Fnv`v#poBa}@{1h%2?H)5e}Al}Pgx%v~R-M|XdCxh*Yvthp=Aac{V{;OLk`ibKMc z+Rn%>PiI`4ps2$;^WoGleMOfOv3Gxa#2&cqxi2QW8u1>V)qFpSy00U5`*=!D_lTl& zz^)3PT{loSP`%s3)$tywoKyqyE4W=CEh!giKH)*djvaf4tXF}$LF#} za$BbCc37g#xP9DgB6s@~@Sm%#4uV5-Zp&kk%O-m)?`f+oJ8^;~q+h&Ja}=PkTQF0( S{1Z!0OIO;%$;eIWJO2siC@7Ht literal 0 HcmV?d00001 diff --git a/submodules/LegacyComponents/Sources/TGCameraController.m b/submodules/LegacyComponents/Sources/TGCameraController.m index 7e9deb2fc0..5258fe8e96 100644 --- a/submodules/LegacyComponents/Sources/TGCameraController.m +++ b/submodules/LegacyComponents/Sources/TGCameraController.m @@ -1102,7 +1102,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus __strong TGCameraController *strongSelf = weakSelf; if (strongSelf == nil) return; - + TGDispatchOnMainThread(^ { strongSelf->_shutterIsBusy = false; @@ -1120,6 +1120,10 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus strongSelf->_camera.disabled = false; } }); + + [[SQueue concurrentDefaultQueue] dispatch:^{ + [TGCameraController generateStartImageWithImage:result]; + }]; }]; } else if (cameraMode == PGCameraModeVideo || cameraMode == PGCameraModeSquareVideo || cameraMode == PGCameraModeSquareSwing) @@ -2246,6 +2250,15 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus _interfaceView.previewViewFrame = toFrame; } ++ (void)generateStartImageWithImage:(UIImage *)frameImage { + CGFloat minSize = MIN(frameImage.size.width, frameImage.size.height); + UIImage *image = TGPhotoEditorCrop(frameImage, nil, UIImageOrientationUp, 0.0f, CGRectMake((frameImage.size.width - minSize) / 2.0f, (frameImage.size.height - minSize) / 2.0f, minSize, minSize), false, CGSizeMake(240.0f, 240.0f), frameImage.size, true); + UIImage *startImage = TGSecretBlurredAttachmentImage(image, image.size, NULL, false, 0); + TGDispatchOnMainThread(^{ + [TGCameraController saveStartImage:startImage]; + }); +} + - (void)beginTransitionOutWithVelocity:(CGFloat)velocity { _dismissing = true; @@ -2269,13 +2282,9 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus __weak TGCameraController *weakSelf = self; [_camera captureNextFrameCompletion:^(UIImage *frameImage) { - CGFloat minSize = MIN(frameImage.size.width, frameImage.size.height); - CGFloat maxSize = MAX(frameImage.size.width, frameImage.size.height); - - UIImage *image = TGPhotoEditorCrop(frameImage, nil, UIImageOrientationUp, 0.0f, CGRectMake((maxSize - minSize) / 2.0f, 0.0f, minSize, minSize), false, CGSizeMake(240.0f, 240.0f), frameImage.size, true); - - UIImage *startImage = TGSecretBlurredAttachmentImage(image, image.size, NULL, false, 0); - [TGCameraController saveStartImage:startImage]; + [[SQueue concurrentDefaultQueue] dispatch:^{ + [TGCameraController generateStartImageWithImage:frameImage]; + }]; }]; if (_standalone) { @@ -3156,7 +3165,7 @@ static UIImage *startImage = nil; + (UIImage *)startImage { if (startImage == nil) - startImage = TGComponentsImageNamed (@"VideoMessagePlaceholder.jpg"); + startImage = TGComponentsImageNamed (@"CameraPlaceholder.jpg"); return startImage; } diff --git a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift index 396dfa7ffc..f40dead068 100644 --- a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift +++ b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift @@ -184,16 +184,16 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi } switch playbackBaseRate { case .x0_5: - self.rateButton.setContent(.image(optionsRateImage(rate: "0.5x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor))) + self.rateButton.setContent(.image(optionsRateImage(rate: "0.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x1: - self.rateButton.setContent(.image(optionsRateImage(rate: "1x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor))) + self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.controlColor))) self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate self.rateButton.accessibilityValue = self.strings.VoiceOver_Media_PlaybackRateNormal self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange case .x1_5: - self.rateButton.setContent(.image(optionsRateImage(rate: "1.5x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor))) + self.rateButton.setContent(.image(optionsRateImage(rate: "1.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x2: - self.rateButton.setContent(.image(optionsRateImage(rate: "2x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor))) + self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.accentTextColor))) self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate self.rateButton.accessibilityValue = self.strings.VoiceOver_Media_PlaybackRateFast self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange @@ -372,13 +372,13 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi if let playbackBaseRate = self.playbackBaseRate { switch playbackBaseRate { case .x0_5: - self.rateButton.setContent(.image(optionsRateImage(rate: "0.5x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor))) + self.rateButton.setContent(.image(optionsRateImage(rate: "0.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x1: - self.rateButton.setContent(.image(optionsRateImage(rate: "1x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor))) + self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.controlColor))) case .x1_5: - self.rateButton.setContent(.image(optionsRateImage(rate: "1.5x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor))) + self.rateButton.setContent(.image(optionsRateImage(rate: "1.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x2: - self.rateButton.setContent(.image(optionsRateImage(rate: "2x", isLarge: false, color: self.theme.rootController.navigationBar.controlColor))) + self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.accentTextColor))) default: break } @@ -486,7 +486,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0)) transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 44.0 - rightInset, y: 0.0), size: CGSize(width: 44.0, height: minHeight))) let rateButtonSize = CGSize(width: 30.0, height: minHeight) - transition.updateFrame(node: self.rateButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 20.0 - closeButtonSize.width - rateButtonSize.width - rightInset, y: -4.0), size: rateButtonSize)) + transition.updateFrame(node: self.rateButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 33.0 - closeButtonSize.width - rateButtonSize.width - rightInset, y: -4.0), size: rateButtonSize)) transition.updateFrame(node: self.playPauseIconNode, frame: CGRect(origin: CGPoint(x: 6.0, y: 4.0 + UIScreenPixel), size: CGSize(width: 28.0, height: 28.0))) transition.updateFrame(node: self.actionButton, frame: CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: 40.0, height: 37.0))) transition.updateFrame(node: self.scrubbingNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 37.0 - 2.0), size: CGSize(width: size.width, height: 2.0))) @@ -501,7 +501,18 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi } @objc public func rateButtonPressed() { - self.rateButton.contextAction?(self.rateButton.containerNode, nil) + let nextRate: AudioPlaybackRate + if let rate = self.playbackBaseRate { + switch rate { + case .x1: + nextRate = .x2 + default: + nextRate = .x1 + } + } else { + nextRate = .x2 + } + self.setRate?(nextRate) } private func speedList(strings: PresentationStrings) -> [(String, String, AudioPlaybackRate)] { @@ -542,7 +553,6 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi let items: Signal<[ContextMenuItem], NoError> = self.contextMenuSpeedItems() let contextController = ContextController(account: self.context.account, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode)), items: items, reactionItems: [], gesture: gesture) self.presentInGlobalOverlay?(contextController) -// controller.presentInGlobalOverlay(contextController) } @objc public func actionButtonPressed() { @@ -606,38 +616,42 @@ private final class PlayPauseIconNode: ManagedAnimationNode { } } -private func optionsRateImage(rate: String, isLarge: Bool, color: UIColor = .white) -> UIImage? { - return generateImage(isLarge ? CGSize(width: 30.0, height: 30.0) : CGSize(width: 24.0, height: 24.0), rotatedContext: { size, context in +private func optionsRateImage(rate: String, color: UIColor = .white) -> UIImage? { + return generateImage(CGSize(width: 30.0, height: 16.0), rotatedContext: { size, context in UIGraphicsPushContext(context) context.clear(CGRect(origin: CGPoint(), size: size)) - if let image = generateTintedImage(image: UIImage(bundleImageName: isLarge ? "Chat/Context Menu/Playspeed30" : "Chat/Context Menu/Playspeed24"), color: color) { - image.draw(at: CGPoint(x: 0.0, y: 0.0)) - } + let lineWidth = 1.0 + UIScreenPixel + context.setLineWidth(lineWidth) + context.setStrokeColor(color.cgColor) + - let string = NSMutableAttributedString(string: rate, font: Font.with(size: 11.0, design: .round, weight: .semibold), textColor: color) + let string = NSMutableAttributedString(string: rate, font: Font.with(size: 11.0, design: .round, weight: .bold), textColor: color) var offset = CGPoint(x: 1.0, y: 0.0) + var width: CGFloat if rate.count >= 3 { - if rate == "0.5x" { + if rate == "0.5X" { string.addAttribute(.kern, value: -0.8 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string)) offset.x += -0.5 } else { string.addAttribute(.kern, value: -0.5 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string)) offset.x += -0.3 } + width = 29.0 } else { + string.addAttribute(.kern, value: -0.5 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string)) + width = 19.0 offset.x += -0.3 } - - if !isLarge { - offset.x *= 0.5 - offset.y *= 0.5 - } - + + let path = UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - width) / 2.0), y: 0.0, width: width, height: 16.0).insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0), byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 2.0, height: 2.0)) + context.addPath(path.cgPath) + context.strokePath() + let boundingRect = string.boundingRect(with: size, options: [], context: nil) - string.draw(at: CGPoint(x: offset.x + floor((size.width - boundingRect.width) / 2.0), y: offset.y + floor((size.height - boundingRect.height) / 2.0))) + string.draw(at: CGPoint(x: offset.x + floor((size.width - boundingRect.width) / 2.0), y: offset.y + UIScreenPixel + floor((size.height - boundingRect.height) / 2.0))) UIGraphicsPopContext() })