diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 63f522f4e..cf152dce9 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -143,6 +143,7 @@ 34641E1C2088DA4100E2EDE5 /* ScreenLockViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 34641E1A2088DA4000E2EDE5 /* ScreenLockViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 34641E1F2088DA6D00E2EDE5 /* SAEScreenLockViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34641E1E2088DA6D00E2EDE5 /* SAEScreenLockViewController.m */; }; 3466087220E550F400AFFE73 /* ConversationStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3466087120E550F300AFFE73 /* ConversationStyle.swift */; }; + 3466087420E5649700AFFE73 /* OWSLayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3466087320E5649700AFFE73 /* OWSLayerView.swift */; }; 34661FB820C1C0D60056EDD6 /* message_sent.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 34661FB720C1C0D60056EDD6 /* message_sent.aiff */; }; 346B66311F4E29B200E5122F /* CropScaleImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */; }; 347850311FD7494A007B8332 /* dripicons-v2.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A5B1E787A9800DF2FB9 /* dripicons-v2.ttf */; }; @@ -752,6 +753,7 @@ 34641E1D2088DA6C00E2EDE5 /* SAEScreenLockViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SAEScreenLockViewController.h; sourceTree = ""; }; 34641E1E2088DA6D00E2EDE5 /* SAEScreenLockViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SAEScreenLockViewController.m; sourceTree = ""; }; 3466087120E550F300AFFE73 /* ConversationStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationStyle.swift; sourceTree = ""; }; + 3466087320E5649700AFFE73 /* OWSLayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSLayerView.swift; path = Signal/src/UserInterface/OWSLayerView.swift; sourceTree = SOURCE_ROOT; }; 34661FB720C1C0D60056EDD6 /* message_sent.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; name = message_sent.aiff; path = Signal/AudioFiles/message_sent.aiff; sourceTree = SOURCE_ROOT; }; 346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CropScaleImageViewController.swift; sourceTree = ""; }; 347850561FD86544007B8332 /* SAEFailedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SAEFailedViewController.swift; sourceTree = ""; }; @@ -2141,6 +2143,7 @@ 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */, 459311FA1D75C948008DD4F0 /* OWSDeviceTableViewCell.h */, 459311FB1D75C948008DD4F0 /* OWSDeviceTableViewCell.m */, + 3466087320E5649700AFFE73 /* OWSLayerView.swift */, 34330AA11E79686200DF2FB9 /* OWSProgressView.h */, 34330AA21E79686200DF2FB9 /* OWSProgressView.m */, 45D308AB2049A439000189E4 /* PinEntryView.h */, @@ -3297,6 +3300,7 @@ 4523149E1F7E916B003A428C /* SlideOffAnimatedTransition.swift in Sources */, 340FC8C0204DB7D2007AEB0F /* OWSBackupExportJob.m in Sources */, 45F32C232057297A00A300D5 /* MediaPageViewController.swift in Sources */, + 3466087420E5649700AFFE73 /* OWSLayerView.swift in Sources */, 340FC8A7204DAC8D007AEB0F /* RegistrationViewController.m in Sources */, 452C468F1E427E200087B011 /* OutboundCallInitiator.swift in Sources */, 34D2CCDA2062E7D000CB1A14 /* OWSScreenLockUI.m in Sources */, diff --git a/Signal/src/UserInterface/OWSLayerView.swift b/Signal/src/UserInterface/OWSLayerView.swift new file mode 100644 index 000000000..ddee5070f --- /dev/null +++ b/Signal/src/UserInterface/OWSLayerView.swift @@ -0,0 +1,40 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +@objc +class OWSLayerView: UIView { + let layoutCallback : (() -> Void) + + @objc + public required init(frame: CGRect, layoutCallback : @escaping () -> Void) { + self.layoutCallback = layoutCallback + super.init(frame: frame) + } + + required init?(coder aDecoder: NSCoder) { + self.layoutCallback = { + } + super.init(coder: aDecoder) + } + + override var bounds: CGRect { + didSet { + layoutCallback() + } + } + + override var frame: CGRect { + didSet { + layoutCallback() + } + } + + override var center: CGPoint { + didSet { + layoutCallback() + } + } +} diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index 00aaa599c..7ebf78eb5 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -422,7 +422,26 @@ NS_ASSUME_NONNULL_BEGIN // Do nothing. } else if (shouldFooterOverlayMedia) { OWSAssert(bodyMediaView); - [self.footerView configureWithConversationViewItem:self.viewItem hasShadows:YES]; + + CGFloat maxGradientHeight = 48.f; + CAGradientLayer *gradientLayer = [CAGradientLayer new]; + gradientLayer.colors = @[ + (id)[UIColor colorWithWhite:0.f alpha:0.f].CGColor, + (id)[UIColor colorWithWhite:0.f alpha:0.4f].CGColor, + ]; + __block OWSLayerView *gradientView; + gradientView = [[OWSLayerView alloc] initWithFrame:CGRectZero + layoutCallback:^{ + CGRect layerFrame = gradientView.bounds; + layerFrame.size.height = MIN(maxGradientHeight, gradientView.height); + layerFrame.origin.y = gradientView.height - layerFrame.size.height; + gradientLayer.frame = layerFrame; + }]; + [gradientView.layer addSublayer:gradientLayer]; + [bodyMediaView addSubview:gradientView]; + [self.viewConstraints addObjectsFromArray:[gradientView autoPinToSuperviewEdges]]; + + [self.footerView configureWithConversationViewItem:self.viewItem isOverlayingMedia:YES]; [bodyMediaView addSubview:self.footerView]; bodyMediaView.layoutMargins = UIEdgeInsetsZero; @@ -432,7 +451,7 @@ NS_ASSUME_NONNULL_BEGIN [self.footerView autoPinBottomToSuperviewMarginWithInset:self.conversationStyle.textInsetBottom], ]]; } else { - [self.footerView configureWithConversationViewItem:self.viewItem hasShadows:NO]; + [self.footerView configureWithConversationViewItem:self.viewItem isOverlayingMedia:NO]; [textViews addObject:self.footerView]; } @@ -1242,6 +1261,9 @@ NS_ASSUME_NONNULL_BEGIN self.loadCellContentBlock = nil; self.unloadCellContentBlock = nil; + for (UIView *subview in self.bodyMediaView.subviews) { + [subview removeFromSuperview]; + } [self.bodyMediaView removeFromSuperview]; self.bodyMediaView = nil; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.h index 6fbe932ad..4f04b3848 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.h @@ -8,7 +8,7 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSMessageFooterView : UIStackView -- (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem hasShadows:(BOOL)hasShadows; +- (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem isOverlayingMedia:(BOOL)isOverlayingMedia; - (CGSize)measureWithConversationViewItem:(ConversationViewItem *)viewItem; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m index b078026e6..609c63d3b 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m @@ -69,7 +69,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Load -- (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem hasShadows:(BOOL)hasShadows +- (void)configureWithConversationViewItem:(ConversationViewItem *)viewItem isOverlayingMedia:(BOOL)isOverlayingMedia { OWSAssert(viewItem); @@ -81,7 +81,7 @@ NS_ASSUME_NONNULL_BEGIN self.timestampLabel, self.statusIndicatorImageView, ]) { - if (hasShadows) { + if (isOverlayingMedia) { subview.layer.shadowColor = [UIColor blackColor].CGColor; subview.layer.shadowOpacity = 0.35f; subview.layer.shadowOffset = CGSizeZero; @@ -95,7 +95,7 @@ NS_ASSUME_NONNULL_BEGIN } UIColor *textColor; - if (hasShadows) { + if (isOverlayingMedia) { textColor = [UIColor whiteColor]; } else if (viewItem.interaction.interactionType == OWSInteractionType_IncomingMessage) { textColor = [UIColor colorWithWhite:1.f alpha:0.7f]; diff --git a/Signal/src/ViewControllers/CropScaleImageViewController.swift b/Signal/src/ViewControllers/CropScaleImageViewController.swift index 5fea8f2d0..430c26ed0 100644 --- a/Signal/src/ViewControllers/CropScaleImageViewController.swift +++ b/Signal/src/ViewControllers/CropScaleImageViewController.swift @@ -6,33 +6,6 @@ import Foundation import MediaPlayer import SignalMessaging -class OWSLayerView: UIView { - let layoutCallback : (() -> Void) - - required init(frame: CGRect, layoutCallback : @escaping () -> Void) { - self.layoutCallback = layoutCallback - super.init(frame: frame) - } - - required init?(coder aDecoder: NSCoder) { - self.layoutCallback = { - } - super.init(coder: aDecoder) - } - - override var bounds: CGRect { - didSet { - layoutCallback() - } - } - - override var frame: CGRect { - didSet { - layoutCallback() - } - } -} - // This kind of view is tricky. I've tried to organize things in the // simplest possible way. //