From 15c42642e04186c379fc6ae649aeea6b66d38e5f Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 8 Nov 2018 16:11:53 -0500 Subject: [PATCH 1/3] Apply invalid and rety assets. --- .../media_invalid.imageset/Contents.json | 23 ++++++++++++++ .../media_invalid.imageset/photo-error-36.png | Bin 0 -> 485 bytes .../photo-error-36@2x.png | Bin 0 -> 766 bytes .../photo-error-36@3x.png | Bin 0 -> 1161 bytes .../media_retry.imageset/Contents.json | 23 ++++++++++++++ .../media_retry.imageset/retry-36.png | Bin 0 -> 561 bytes .../media_retry.imageset/retry-36@2x.png | Bin 0 -> 1164 bytes .../media_retry.imageset/retry-36@3x.png | Bin 0 -> 1798 bytes .../Cells/ConversationMediaView.swift | 29 ++++++++++++++++-- 9 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 Signal/Images.xcassets/media_invalid.imageset/Contents.json create mode 100644 Signal/Images.xcassets/media_invalid.imageset/photo-error-36.png create mode 100644 Signal/Images.xcassets/media_invalid.imageset/photo-error-36@2x.png create mode 100644 Signal/Images.xcassets/media_invalid.imageset/photo-error-36@3x.png create mode 100644 Signal/Images.xcassets/media_retry.imageset/Contents.json create mode 100644 Signal/Images.xcassets/media_retry.imageset/retry-36.png create mode 100644 Signal/Images.xcassets/media_retry.imageset/retry-36@2x.png create mode 100644 Signal/Images.xcassets/media_retry.imageset/retry-36@3x.png diff --git a/Signal/Images.xcassets/media_invalid.imageset/Contents.json b/Signal/Images.xcassets/media_invalid.imageset/Contents.json new file mode 100644 index 000000000..01c88da49 --- /dev/null +++ b/Signal/Images.xcassets/media_invalid.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "photo-error-36.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "photo-error-36@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "photo-error-36@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/media_invalid.imageset/photo-error-36.png b/Signal/Images.xcassets/media_invalid.imageset/photo-error-36.png new file mode 100644 index 0000000000000000000000000000000000000000..a45e6b6977283c8ebd895f097f03df685bdb6fb0 GIT binary patch literal 485 zcmV7i)u4Mape5uGN3Try1ndH@4YG1ee! zvqo9_r*FvX|_BBBFJ8Lx&xEs%h4(jkP}3R49Xz;vQP+)7|d`$daYKoN|O&by^N+M`z1 z+0g#twbr5ZAx)#W@*P~8^g~|rmNw0~xOHTliNia?dXF7k>QoVEB@CD8M;y9VL20%H zCHt)`lfO5fd-3C1&Lzi>l5#LbX*st7Rm1E7v9JS00000NkvXXu0mjfS(DGh literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/media_invalid.imageset/photo-error-36@2x.png b/Signal/Images.xcassets/media_invalid.imageset/photo-error-36@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cbdd8386e08e12ed484b1b8426b3ff5f72ec349a GIT binary patch literal 766 zcmVQb))L86hL28*~J3 zKt)6%WR9l|++j|-AH~Eyf1>z-8zH5H5$^bKZW-~60ThS<6o|tE2_Yt{(TGC>X`H(d z;%aStg*uDnBmkTMK<#)@yCIs3uJN=!MgUMa52NlY=3q6%Bmm6BVb-Z?ojo{G|2%C5 z(j$ix&MOyPdo)fi&~>%*8vvZKzAaPLq-$H(8jX_+WR9L2)`u9>8Yvn_Wztzp&-$N6 z(~s4OUbt%f)gyngwivPLG%uyRTaEPM52+{$2U3Azc1S7oh`;6HBm*(TQ6v1zXima` zUV-+)fgXYO#DU&`_QrvpfMRfj5Etu%p};PVd}&^2`TAA97!2M%0Kg@uW~+f4Pzp{? z&B+h#g%Ix#NGXNYp<=|&Ia_LGI|r4Rkg;G1R0{_<0@cI;Za}qhfD=#)9B2ikRuQ}_ z33mS`IM2Y&{l46K9khC6i>@T3Ugwmo*=i{I%k1abUZg#6*Z?_joJf1&umB3+SV)n# z578?rq=6!F-bjh}R@TVFk}Oan&MVi8k}e!;`PLJ6?7l4|U(MBk3jbL>wjjA`t_Dv z_-qDL8)tK3$K*@zMl3nwZ~|(A!v&}r4hNv7IMjif<4^`7fkPFD3=TygQaIFr$l*`| zB8fuu1Y&_x97sQ`VvAE0DD`d9Ljm=IQv|3doZW$X!%^%0*m3&O zxv@hWi7hH-0d1koA+M=W$C)vJ+!181C84jqasO(H*mV9gi+)XauJWoa w;cx7f?UDPkU)Z9dENJlb7poXgCqp|1%xjMiE|E&oqV8`tq{yu2xcq#D53TR8YywPi)$B~C2RZm@cNm_pNP6ISe9;&4Xq&bh&_7Y{Jqy79E<}#YHpDeAr7Zwr^P2KAO#+uB5d1E0KnD3k4W7@sV z^TetJVS$6canm0gRAwBE1%ky(4lEEXPI6&^VByJ$1%iboHx>xypAuLgm|sd^fneL? zgC!zQqi1@CU>g#E6>ZdqU@H@dwKNdCIs{vhTChN{<~ zo9SB5d@AMb_i`lr_p^NCoRb*yv3wz^8LXJG3bsou7zNuc77T*z8Vkn2c8>)^V8m); z0gPC^)DA|hL23pgmXcb*h&7QK!H6}J+Q5jlkea}VwIa2E5o={?03+6hlm;W##*_pj zmPbl~5z8|rz=#!)++f5COinOjMMy3%Vns^IA1&=dtcVE)>)l#G3$bD+@M_=9zpd|i zMXveM`sdYiZ6dMam)65=jHW-J6V7J?ZI!Hk7q bmIB~6h>s}n303FC00000NkvXXu0mjf;%pym literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/media_retry.imageset/Contents.json b/Signal/Images.xcassets/media_retry.imageset/Contents.json new file mode 100644 index 000000000..939049743 --- /dev/null +++ b/Signal/Images.xcassets/media_retry.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "retry-36.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "retry-36@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "retry-36@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/media_retry.imageset/retry-36.png b/Signal/Images.xcassets/media_retry.imageset/retry-36.png new file mode 100644 index 0000000000000000000000000000000000000000..77b4b3dfacadb712c4a8f8d4002c9da18fe66c8a GIT binary patch literal 561 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 literal 0 HcmV?d00001 diff --git a/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift b/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift index aa2ffeb8e..6cf805808 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift +++ b/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift @@ -265,10 +265,33 @@ public class ConversationMediaView: UIView { } } + private func isFailedDownload() -> Bool { + guard let attachmentPointer = attachment as? TSAttachmentPointer else { + return false + } + return attachmentPointer.state == .failed + } + private func configureForMissingOrInvalid() { - // TODO: Get final value from design. - backgroundColor = UIColor.ows_gray45 - // TODO: Add error icon. + backgroundColor = UIColor.ows_gray05 + let icon: UIImage + if isFailedDownload() { + guard let asset = UIImage(named: "media_retry") else { + owsFailDebug("Missing image") + return + } + icon = asset + } else { + guard let asset = UIImage(named: "media_invalid") else { + owsFailDebug("Missing image") + return + } + icon = asset + } + let iconView = UIImageView(image: icon.withRenderingMode(.alwaysTemplate)) + iconView.tintColor = Theme.primaryColor.withAlphaComponent(0.6) + self.addSubview(iconView) + iconView.autoCenterInSuperview() } private func tryToLoadMedia(loadMediaBlock: @escaping () -> AnyObject?, From 34b4ea377f82b8ac13829130905e22a6dfb3c377 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 8 Nov 2018 16:55:54 -0500 Subject: [PATCH 2/3] Revise media progress views. --- Signal.xcodeproj/project.pbxproj | 12 +- .../Cells/ConversationMediaView.swift | 45 ++++--- ...loadView.swift => MediaDownloadView.swift} | 19 ++- .../Cells/MediaUploadView.swift | 117 ++++++++++++++++++ 4 files changed, 163 insertions(+), 30 deletions(-) rename Signal/src/ViewControllers/ConversationView/Cells/{AttachmentDownloadView.swift => MediaDownloadView.swift} (76%) create mode 100644 Signal/src/ViewControllers/ConversationView/Cells/MediaUploadView.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index b2e3f6447..1063dc953 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -283,7 +283,8 @@ 34E5DC8220D8050D00C08145 /* RegistrationUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E5DC8120D8050D00C08145 /* RegistrationUtils.m */; }; 34E88D262098C5AE00A608F4 /* ContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E88D252098C5AE00A608F4 /* ContactViewController.swift */; }; 34E8A8D12085238A00B272B1 /* ProtoParsingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E8A8D02085238900B272B1 /* ProtoParsingTest.m */; }; - 34EA69402194933900702471 /* AttachmentDownloadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EA693F2194933900702471 /* AttachmentDownloadView.swift */; }; + 34EA69402194933900702471 /* MediaDownloadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EA693F2194933900702471 /* MediaDownloadView.swift */; }; + 34EA69422194DE8000702471 /* MediaUploadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EA69412194DE7F00702471 /* MediaUploadView.swift */; }; 34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */; }; 34FD93701E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FD936F1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m */; }; 4503F1BE20470A5B00CEE724 /* classic-quiet.aifc in Resources */ = {isa = PBXBuildFile; fileRef = 4503F1BB20470A5B00CEE724 /* classic-quiet.aifc */; }; @@ -982,7 +983,8 @@ 34E5DC8120D8050D00C08145 /* RegistrationUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegistrationUtils.m; sourceTree = ""; }; 34E88D252098C5AE00A608F4 /* ContactViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactViewController.swift; sourceTree = ""; }; 34E8A8D02085238900B272B1 /* ProtoParsingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProtoParsingTest.m; sourceTree = ""; }; - 34EA693F2194933900702471 /* AttachmentDownloadView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentDownloadView.swift; sourceTree = ""; }; + 34EA693F2194933900702471 /* MediaDownloadView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaDownloadView.swift; sourceTree = ""; }; + 34EA69412194DE7F00702471 /* MediaUploadView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaUploadView.swift; sourceTree = ""; }; 34F308A01ECB469700BB7697 /* OWSBezierPathView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBezierPathView.h; sourceTree = ""; }; 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBezierPathView.m; sourceTree = ""; }; 34FD936E1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAnyTouchGestureRecognizer.h; path = views/OWSAnyTouchGestureRecognizer.h; sourceTree = ""; }; @@ -1830,13 +1832,14 @@ 34D1F0951F867BFC0066283D /* Cells */ = { isa = PBXGroup; children = ( - 34EA693F2194933900702471 /* AttachmentDownloadView.swift */, 34D1F0BB1F8D108C0066283D /* AttachmentUploadView.h */, 34D1F0BC1F8D108C0066283D /* AttachmentUploadView.m */, 3488F9352191CC4000E524CC /* ConversationMediaView.swift */, 34D1F0961F867BFC0066283D /* ConversationViewCell.h */, 34D1F0971F867BFC0066283D /* ConversationViewCell.m */, 34A8B3502190A40E00218A25 /* MediaAlbumCellView.swift */, + 34EA693F2194933900702471 /* MediaDownloadView.swift */, + 34EA69412194DE7F00702471 /* MediaUploadView.swift */, 34D1F0B81F8800D90066283D /* OWSAudioMessageView.h */, 34D1F0B91F8800D90066283D /* OWSAudioMessageView.m */, 34DBF005206C3CB100025978 /* OWSBubbleShapeView.h */, @@ -3422,7 +3425,7 @@ 458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */, 34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */, 4517642B1DE939FD00EDB8B9 /* ContactCell.swift in Sources */, - 34EA69402194933900702471 /* AttachmentDownloadView.swift in Sources */, + 34EA69402194933900702471 /* MediaDownloadView.swift in Sources */, 340FC8AB204DAC8D007AEB0F /* DomainFrontingCountryViewController.m in Sources */, 3496744D2076768700080B5F /* OWSMessageBubbleView.m in Sources */, 34B3F8751E8DF1700035BE1A /* CallViewController.swift in Sources */, @@ -3468,6 +3471,7 @@ 34DBF004206BD5A500025978 /* OWSBubbleView.m in Sources */, 34E88D262098C5AE00A608F4 /* ContactViewController.swift in Sources */, 34AC0A23211C829F00997B47 /* OWSLabel.m in Sources */, + 34EA69422194DE8000702471 /* MediaUploadView.swift in Sources */, 76EB054018170B33006006FC /* AppDelegate.m in Sources */, 34D1F0831F8678AA0066283D /* ConversationInputTextView.m in Sources */, 340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */, diff --git a/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift b/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift index 6cf805808..22a2d6eb3 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift +++ b/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift @@ -67,8 +67,6 @@ public class ConversationMediaView: UIView { } // - typealias ProgressCallback = (Bool) -> Void - private func addDownloadProgressIfNecessary() { guard let attachmentPointer = attachment as? TSAttachmentPointer else { owsFailDebug("Attachment has unexpected type.") @@ -76,11 +74,10 @@ public class ConversationMediaView: UIView { return } guard let attachmentId = attachmentPointer.uniqueId else { - owsFailDebug("Attachment stream missing unique ID.") + owsFailDebug("Attachment missing unique ID.") configureForMissingOrInvalid() return } - guard nil != attachmentDownloads.downloadProgress(forAttachmentId: attachmentId) else { // Not being downloaded. configureForMissingOrInvalid() @@ -88,29 +85,30 @@ public class ConversationMediaView: UIView { } backgroundColor = UIColor.ows_gray05 - let progressView = AttachmentDownloadView(attachmentId: attachmentId, radius: maxMessageWidth * 0.1) + let progressView = MediaDownloadView(attachmentId: attachmentId, radius: maxMessageWidth * 0.1) self.addSubview(progressView) progressView.autoPinEdgesToSuperviewEdges() } - private func addUploadProgressIfNecessary(_ subview: UIView, - progressCallback: ProgressCallback? = nil) { + private func addUploadProgressIfNecessary(_ subview: UIView) -> Bool { guard isOutgoing else { - return + return false } guard let attachmentStream = attachment as? TSAttachmentStream else { - return + return false } - guard !attachmentStream.isUploaded else { - return + guard let attachmentId = attachmentStream.uniqueId else { + owsFailDebug("Attachment missing unique ID.") + configureForMissingOrInvalid() + return false } - let uploadView = AttachmentUploadView(attachment: attachmentStream) { (isAttachmentReady) in - if let progressCallback = progressCallback { - progressCallback(isAttachmentReady) - } + guard !attachmentStream.isUploaded else { + return false } - subview.addSubview(uploadView) - uploadView.autoPinEdgesToSuperviewEdges() + let progressView = MediaUploadView(attachmentId: attachmentId, radius: maxMessageWidth * 0.1) + self.addSubview(progressView) + progressView.autoPinEdgesToSuperviewEdges() + return true } private func configureForAnimatedImage(attachmentStream: TSAttachmentStream) { @@ -224,15 +222,14 @@ public class ConversationMediaView: UIView { stillImageView.layer.magnificationFilter = kCAFilterTrilinear stillImageView.backgroundColor = Theme.offBackgroundColor - let videoPlayIcon = UIImage(named: "play_button") - let videoPlayButton = UIImageView(image: videoPlayIcon) - stillImageView.addSubview(videoPlayButton) - videoPlayButton.autoCenterInSuperview() - addSubview(stillImageView) stillImageView.autoPinEdgesToSuperviewEdges() - addUploadProgressIfNecessary(stillImageView) { (isAttachmentReady) in - videoPlayButton.isHidden = !isAttachmentReady + + if !addUploadProgressIfNecessary(stillImageView) { + let videoPlayIcon = UIImage(named: "play_button") + let videoPlayButton = UIImageView(image: videoPlayIcon) + stillImageView.addSubview(videoPlayButton) + videoPlayButton.autoCenterInSuperview() } loadBlock = { [weak self] in diff --git a/Signal/src/ViewControllers/ConversationView/Cells/AttachmentDownloadView.swift b/Signal/src/ViewControllers/ConversationView/Cells/MediaDownloadView.swift similarity index 76% rename from Signal/src/ViewControllers/ConversationView/Cells/AttachmentDownloadView.swift rename to Signal/src/ViewControllers/ConversationView/Cells/MediaDownloadView.swift index 1915d6939..e7b5dfa52 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/AttachmentDownloadView.swift +++ b/Signal/src/ViewControllers/ConversationView/Cells/MediaDownloadView.swift @@ -5,7 +5,7 @@ import Foundation @objc -public class AttachmentDownloadView: UIView { +public class MediaDownloadView: UIView { // MARK: - Dependencies @@ -27,6 +27,17 @@ public class AttachmentDownloadView: UIView { super.init(frame: .zero) layer.addSublayer(shapeLayer) + + NotificationCenter.default.addObserver(forName: NSNotification.Name.attachmentDownloadProgress, object: nil, queue: nil) { [weak self] notification in + guard let strongSelf = self else { return } + guard let notificationAttachmentId = notification.userInfo?[kAttachmentDownloadAttachmentIDKey] as? String else { + return + } + guard notificationAttachmentId == strongSelf.attachmentId else { + return + } + strongSelf.updateLayers() + } } @available(*, unavailable, message: "use other init() instead.") @@ -34,6 +45,10 @@ public class AttachmentDownloadView: UIView { notImplemented() } + deinit { + NotificationCenter.default.removeObserver(self) + } + @objc public override var bounds: CGRect { didSet { if oldValue != bounds { @@ -53,7 +68,7 @@ public class AttachmentDownloadView: UIView { internal func updateLayers() { AssertIsOnMainThread() - self.shapeLayer.frame = self.bounds + shapeLayer.frame = self.bounds guard let progress = attachmentDownloads.downloadProgress(forAttachmentId: attachmentId) else { Logger.warn("No progress for attachment.") diff --git a/Signal/src/ViewControllers/ConversationView/Cells/MediaUploadView.swift b/Signal/src/ViewControllers/ConversationView/Cells/MediaUploadView.swift new file mode 100644 index 000000000..4bd7dfabf --- /dev/null +++ b/Signal/src/ViewControllers/ConversationView/Cells/MediaUploadView.swift @@ -0,0 +1,117 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +@objc +public class MediaUploadView: UIView { + + // MARK: - + + private let attachmentId: String + private let radius: CGFloat + private let shapeLayer1 = CAShapeLayer() + private let shapeLayer2 = CAShapeLayer() + + private var isAttachmentReady: Bool = false + private var lastProgress: CGFloat = 0 + + @objc + public required init(attachmentId: String, radius: CGFloat) { + self.attachmentId = attachmentId + self.radius = radius + + super.init(frame: .zero) + + layer.addSublayer(shapeLayer1) + layer.addSublayer(shapeLayer2) + + NotificationCenter.default.addObserver(forName: NSNotification.Name.attachmentUploadProgress, object: nil, queue: nil) { [weak self] notification in + guard let strongSelf = self else { return } + guard let notificationAttachmentId = notification.userInfo?[kAttachmentUploadAttachmentIDKey] as? String else { + return + } + guard notificationAttachmentId == strongSelf.attachmentId else { + return + } + guard let progress = notification.userInfo?[kAttachmentUploadProgressKey] as? NSNumber else { + return + } + strongSelf.lastProgress = CGFloat(progress.floatValue) + strongSelf.updateLayers() + } + } + + @available(*, unavailable, message: "use other init() instead.") + required public init?(coder aDecoder: NSCoder) { + notImplemented() + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + @objc public override var bounds: CGRect { + didSet { + if oldValue != bounds { + updateLayers() + } + } + } + + @objc public override var frame: CGRect { + didSet { + if oldValue != frame { + updateLayers() + } + } + } + + internal func updateLayers() { + AssertIsOnMainThread() + + shapeLayer1.frame = self.bounds + shapeLayer2.frame = self.bounds + + guard !isAttachmentReady else { + shapeLayer1.path = nil + shapeLayer2.path = nil + return + } + + // Prevent the shape layer from animating changes. + CATransaction.begin() + CATransaction.setDisableActions(true) + + let center = CGPoint(x: self.bounds.width * 0.5, + y: self.bounds.height * 0.5) + let outerRadius: CGFloat = radius * 1.0 + let innerRadius: CGFloat = radius * 0.9 + let startAngle: CGFloat = CGFloat.pi * 1.5 + let endAngle: CGFloat = CGFloat.pi * (1.5 + 2 * lastProgress) + + let bezierPath1 = UIBezierPath() + bezierPath1.addArc(withCenter: center, radius: outerRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true) + bezierPath1.addArc(withCenter: center, radius: innerRadius, startAngle: endAngle, endAngle: startAngle, clockwise: false) + shapeLayer1.path = bezierPath1.cgPath + shapeLayer1.fillColor = UIColor.ows_white.cgColor + + let innerCircleBounds = CGRect(x: center.x - innerRadius, + y: center.y - innerRadius, + width: innerRadius * 2, + height: innerRadius * 2) + let outerCircleBounds = CGRect(x: center.x - outerRadius, + y: center.y - outerRadius, + width: outerRadius * 2, + height: outerRadius * 2) + let bezierPath2 = UIBezierPath() + bezierPath2.append(UIBezierPath(ovalIn: innerCircleBounds)) + bezierPath2.append(UIBezierPath(ovalIn: outerCircleBounds)) + shapeLayer2.path = bezierPath2.cgPath + shapeLayer2.fillColor = UIColor(white: 1.0, alpha: 0.4).cgColor + shapeLayer2.fillRule = kCAFillRuleEvenOdd + + CATransaction.commit() + } +} From b475695f548837ae8ba7e49eddfe6e0391621765 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 9 Nov 2018 13:58:31 -0500 Subject: [PATCH 3/3] Respond to CR. --- .../ConversationView/Cells/ConversationMediaView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift b/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift index 22a2d6eb3..f6514cc8c 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift +++ b/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift @@ -262,7 +262,7 @@ public class ConversationMediaView: UIView { } } - private func isFailedDownload() -> Bool { + private var isFailedDownload: Bool { guard let attachmentPointer = attachment as? TSAttachmentPointer else { return false } @@ -272,7 +272,7 @@ public class ConversationMediaView: UIView { private func configureForMissingOrInvalid() { backgroundColor = UIColor.ows_gray05 let icon: UIImage - if isFailedDownload() { + if isFailedDownload { guard let asset = UIImage(named: "media_retry") else { owsFailDebug("Missing image") return