diff --git a/Signal/src/Loki/Components/VoiceMessageView.swift b/Signal/src/Loki/Components/VoiceMessageView.swift index 1dac2b9e9..6e429458f 100644 --- a/Signal/src/Loki/Components/VoiceMessageView.swift +++ b/Signal/src/Loki/Components/VoiceMessageView.swift @@ -1,21 +1,22 @@ import Accelerate +@objc(LKVoiceMessageViewDelegate) +protocol VoiceMessageViewDelegate { + + func showLoader() + func hideLoader() +} + @objc(LKVoiceMessageView) final class VoiceMessageView : UIView { private let voiceMessage: TSAttachment private let isOutgoing: Bool - private var isAnimating = false private var volumeSamples: [Float] = [] { didSet { updateShapeLayers() } } private var progress: CGFloat = 0 + @objc var delegate: VoiceMessageViewDelegate? @objc var duration: Int = 0 { didSet { updateDurationLabel() } } // MARK: Components - private lazy var loader: UIView = { - let result = UIView() - result.backgroundColor = Colors.text.withAlphaComponent(0.2) - return result - }() - private lazy var durationLabel: UILabel = { let result = UILabel() result.textColor = Colors.text @@ -47,7 +48,6 @@ final class VoiceMessageView : UIView { self.voiceMessage = voiceMessage self.isOutgoing = isOutgoing super.init(frame: CGRect.zero) - initialize() } override init(frame: CGRect) { @@ -58,17 +58,16 @@ final class VoiceMessageView : UIView { preconditionFailure("Use init(voiceMessage:associatedWith:) instead.") } - private func initialize() { + @objc func initialize() { setUpViewHierarchy() if voiceMessage.isDownloaded { - loader.alpha = 0 guard let url = (voiceMessage as? TSAttachmentStream)?.originalMediaURL else { return print("[Loki] Couldn't get URL for voice message.") } let targetSampleCount = 48 if let cachedVolumeSamples = Storage.getVolumeSamples(for: voiceMessage.uniqueId!), cachedVolumeSamples.count == targetSampleCount { self.volumeSamples = cachedVolumeSamples - self.stopAnimating() + self.delegate?.hideLoader() } else { let voiceMessageID = voiceMessage.uniqueId! AudioUtilities.getVolumeSamples(for: url, targetSampleCount: targetSampleCount).done(on: DispatchQueue.main) { [weak self] volumeSamples in @@ -78,22 +77,20 @@ final class VoiceMessageView : UIView { Storage.setVolumeSamples(for: voiceMessageID, to: volumeSamples, using: transaction) } self.durationLabel.alpha = 1 - self.stopAnimating() + self.delegate?.hideLoader() }.catch(on: DispatchQueue.main) { error in print("[Loki] Couldn't sample audio file due to error: \(error).") } } } else { durationLabel.alpha = 0 - showLoader() + delegate?.showLoader() } } private func setUpViewHierarchy() { set(.width, to: 200) set(.height, to: VoiceMessageView.contentHeight) - addSubview(loader) - loader.pin(to: self) layer.insertSublayer(backgroundShapeLayer, at: 0) layer.insertSublayer(foregroundShapeLayer, at: 1) addSubview(durationLabel) @@ -102,27 +99,6 @@ final class VoiceMessageView : UIView { } // MARK: UI & Updating - private func showLoader() { - isAnimating = true - loader.alpha = 1 - animateLoader() - } - - private func animateLoader() { - loader.frame = CGRect(x: 0, y: 0, width: 0, height: VoiceMessageView.contentHeight) - UIView.animate(withDuration: 2) { [weak self] in - self?.loader.frame = CGRect(x: 0, y: 0, width: 200, height: VoiceMessageView.contentHeight) - } completion: { [weak self] _ in - guard let self = self else { return } - if self.isAnimating { self.animateLoader() } - } - } - - private func stopAnimating() { - isAnimating = false - loader.alpha = 0 - } - override func layoutSubviews() { super.layoutSubviews() updateShapeLayers() diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index b134742f1..ad9dcc647 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface OWSMessageBubbleView () +@interface OWSMessageBubbleView () @property (nonatomic) OWSBubbleView *bubbleView; @@ -50,6 +50,10 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, nullable) OWSContactShareButtonsView *contactShareButtonsView; +@property (nonatomic) UIView *loader; + +@property (nonatomic) BOOL isAnimating; + @end #pragma mark - @@ -108,6 +112,10 @@ NS_ASSUME_NONNULL_BEGIN self.linkPreviewView = [[LinkPreviewView alloc] initWithDraftDelegate:nil]; self.footerView = [OWSMessageFooterView new]; + + self.loader = [UIView new]; + self.loader.backgroundColor = [LKColors.text colorWithAlphaComponent:0.6f]; + self.loader.alpha = 0.0f; } - (OWSMessageTextView *)newTextView @@ -414,6 +422,9 @@ NS_ASSUME_NONNULL_BEGIN addObject:[bodyMediaView autoSetDimension:ALDimensionHeight toSize:bodyMediaSize.CGSizeValue.height]]; } + [self.bubbleView addSubview:self.loader]; + [self.loader autoPinEdgesToSuperviewEdges]; + [self insertContactShareButtonsIfNecessary]; [self updateBubbleColor]; @@ -661,6 +672,34 @@ NS_ASSUME_NONNULL_BEGIN } } +- (void)showLoader +{ + self.isAnimating = YES; + self.loader.alpha = 1.0f; + [self animateLoader]; +} + +- (void)animateLoader +{ + __weak OWSMessageBubbleView *weakSelf = self; + self.loader.frame = CGRectMake(0.0f, 0.0f, 0.0f, self.frame.size.height); + [UIView animateWithDuration:2 animations:^{ + if (weakSelf != nil) { + weakSelf.loader.frame = CGRectMake(0.0f, 0.0f, weakSelf.frame.size.width, weakSelf.frame.size.height); + } + } completion:^(BOOL isFinished) { + if (weakSelf != nil && weakSelf.isAnimating) { + [weakSelf animateLoader]; + } + }]; +} + +- (void)hideLoader +{ + self.isAnimating = NO; + self.loader.alpha = 0.0f; +} + #pragma mark - Subviews - (void)configureBodyTextView @@ -842,6 +881,8 @@ NS_ASSUME_NONNULL_BEGIN LKVoiceMessageView *voiceMessageView = [[LKVoiceMessageView alloc] initWithVoiceMessage:attachment isOutgoing:self.isOutgoing]; [voiceMessageView setDuration:(int)self.viewItem.audioDurationSeconds]; [voiceMessageView updateForProgress:self.viewItem.audioProgressSeconds / self.viewItem.audioDurationSeconds]; + [voiceMessageView setDelegate:self]; + [voiceMessageView initialize]; self.viewItem.lastAudioMessageView = voiceMessageView;