From a84400a4fe41bb7c1510b136a9a3ab3e654a3326 Mon Sep 17 00:00:00 2001 From: Marissa Le Coz <129999395+marissa-signal@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:57:38 -0400 Subject: [PATCH] Make call links icon into UIImage using UIGraphics --- .../CreateCallLinkViewController.swift | 52 ++++++- .../LinkPreviewCallLink.swift | 9 +- SignalUI/LinkPreview/LinkPreviewState.swift | 2 +- SignalUI/LinkPreview/LinkPreviewView.swift | 139 ------------------ 4 files changed, 56 insertions(+), 146 deletions(-) diff --git a/Signal/Calls/UserInterface/CreateCallLinkViewController.swift b/Signal/Calls/UserInterface/CreateCallLinkViewController.swift index 4d0598dbd8..447ae44a3a 100644 --- a/Signal/Calls/UserInterface/CreateCallLinkViewController.swift +++ b/Signal/Calls/UserInterface/CreateCallLinkViewController.swift @@ -407,8 +407,14 @@ private class _CreateCallLinkViewController: OWSTableViewController2 { // MARK: - CallLinkCardView private class CallLinkCardView: UIView { - private lazy var circleView: UIView = { - return SignalUI.CallLinkComponentFactory.callLinkIconView() + private lazy var iconView: UIImageView = { + let image = CommonCallLinksUI.callLinkIcon() + let imageView = UIImageView(image: image) + imageView.autoSetDimensions(to: CGSize( + width: Constants.circleViewDimension, + height: Constants.circleViewDimension + )) + return imageView }() private lazy var textStack: UIStackView = { @@ -506,7 +512,7 @@ private class CallLinkCardView: UIView { super.init(frame: .zero) let stackView = UIStackView() - stackView.addArrangedSubviews([circleView, textStack, joinButton]) + stackView.addArrangedSubviews([iconView, textStack, joinButton]) stackView.axis = .horizontal stackView.distribution = .fillProportionally stackView.alignment = .center @@ -525,5 +531,45 @@ private class CallLinkCardView: UIView { static let spacingTextToButton: CGFloat = 16 static let spacingIconToText: CGFloat = 12 static let textStackSpacing: CGFloat = 2 + + static let circleViewDimension: CGFloat = CommonCallLinksUI.Constants.circleViewDimension + } +} + +public class CommonCallLinksUI { + public static func callLinkIcon() -> UIImage? { + guard let image = UIImage(named: "video-compact") else { return nil } + let newSize = CGSize(square: Constants.circleViewDimension) + + let renderer = UIGraphicsImageRenderer(size: newSize) + let finalImage = renderer.image { context in + let rect = CGRect(origin: .zero, size: newSize) + let circlePath = UIBezierPath(ovalIn: rect) + + Constants.iconBackgroundColor.setFill() + circlePath.fill() + + context.cgContext.addPath(circlePath.cgPath) + context.cgContext.clip() + + Constants.iconTintColor.set() + let centerOffset = Constants.circleViewDimension/2 - Constants.iconDimension/2 + let imageRect = CGRect( + x: centerOffset, + y: centerOffset, + width: Constants.iconDimension, + height: Constants.iconDimension + ) + image.withRenderingMode(.alwaysTemplate).draw(in: imageRect) + } + + return finalImage + } + + enum Constants { + static let circleViewDimension: CGFloat = 64 + fileprivate static let iconDimension: CGFloat = 36 + fileprivate static let iconBackgroundColor = UIColor(rgbHex: 0xE4E4FD) + fileprivate static let iconTintColor = UIColor(rgbHex: 0x5151F6) } } diff --git a/Signal/ConversationView/LinkPreviewCallLink.swift b/Signal/ConversationView/LinkPreviewCallLink.swift index ca3dc134c9..00a599214e 100644 --- a/Signal/ConversationView/LinkPreviewCallLink.swift +++ b/Signal/ConversationView/LinkPreviewCallLink.swift @@ -48,13 +48,16 @@ class LinkPreviewCallLink: LinkPreviewState { return .loaded } - public func imageAsync(thumbnailQuality: AttachmentThumbnailQuality, completion: @escaping (UIImage) -> Void) {} + public func imageAsync(thumbnailQuality: AttachmentThumbnailQuality, completion: @escaping (UIImage) -> Void) { + if let image = CommonCallLinksUI.callLinkIcon() { + completion(image) + } + } public func imageCacheKey(thumbnailQuality: AttachmentThumbnailQuality) -> LinkPreviewImageCacheKey? { return nil } public var imagePixelSize: CGSize { - // This value does not matter since the image is local for call links. - return .zero + return CGSize(square: CommonCallLinksUI.Constants.circleViewDimension) } public var previewDescription: String? { diff --git a/SignalUI/LinkPreview/LinkPreviewState.swift b/SignalUI/LinkPreview/LinkPreviewState.swift index 4d38aff5fd..963d9ec943 100644 --- a/SignalUI/LinkPreview/LinkPreviewState.swift +++ b/SignalUI/LinkPreview/LinkPreviewState.swift @@ -336,7 +336,7 @@ public class LinkPreviewSent: LinkPreviewState { public var date: Date? { linkPreview.date } public let isGroupInviteLink = false - public var isCallLink = false + public let isCallLink = false public var activityIndicatorStyle: UIActivityIndicatorView.Style { LinkPreviewView.defaultActivityIndicatorStyle diff --git a/SignalUI/LinkPreview/LinkPreviewView.swift b/SignalUI/LinkPreview/LinkPreviewView.swift index d24d864468..84d11dc990 100644 --- a/SignalUI/LinkPreview/LinkPreviewView.swift +++ b/SignalUI/LinkPreview/LinkPreviewView.swift @@ -139,8 +139,6 @@ public class LinkPreviewView: ManualStackViewWithLayer { return LinkPreviewViewAdapterDraft(state: state) } else if state.isGroupInviteLink { return LinkPreviewViewAdapterGroupLink(state: state) - } else if state.isCallLink { - return LinkPreviewViewAdapterCallLink(state: state) } else { if state.hasLoadedImage { if Self.sentIsHero(state: state) { @@ -914,143 +912,6 @@ private class LinkPreviewViewAdapterGroupLink: LinkPreviewViewAdapter { // MARK: - -private class LinkPreviewViewAdapterCallLink: LinkPreviewViewAdapter { - - let state: LinkPreviewState - - init(state: LinkPreviewState) { - self.state = state - } - - var rootStackConfig: ManualStackView.Config { - ManualStackView.Config( - axis: .horizontal, - alignment: .fill, - spacing: LinkPreviewView.sentNonHeroHSpacing, - layoutMargins: LinkPreviewView.sentNonHeroLayoutMargins - ) - } - - var textStackConfig: ManualStackView.Config { - return ManualStackView.Config( - axis: .vertical, - alignment: .leading, - spacing: LinkPreviewView.sentVSpacing, - layoutMargins: .zero - ) - } - - func configureForRendering( - linkPreviewView: LinkPreviewView, - hasAsymmetricalRounding: Bool, - cellMeasurement: CVCellMeasurement - ) { - - linkPreviewView.backgroundColor = Theme.secondaryBackgroundColor - - var rootStackSubviews = [UIView]() - - let iconView = CallLinkComponentFactory.callLinkIconView() - iconView.contentMode = .scaleAspectFill - iconView.clipsToBounds = true - rootStackSubviews.append(iconView) - - let textStack = linkPreviewView.textStack - var textStackSubviews = [UIView]() - if let titleLabel = sentTitleLabel(state: state) { - textStackSubviews.append(titleLabel) - } - if let descriptionLabel = sentDescriptionLabel(state: state) { - textStackSubviews.append(descriptionLabel) - } - textStack.configure( - config: textStackConfig, - cellMeasurement: cellMeasurement, - measurementKey: Self.measurementKey_textStack, - subviews: textStackSubviews - ) - rootStackSubviews.append(textStack) - - linkPreviewView.configure( - config: rootStackConfig, - cellMeasurement: cellMeasurement, - measurementKey: Self.measurementKey_rootStack, - subviews: rootStackSubviews - ) - } - - func measure( - maxWidth: CGFloat, - measurementBuilder: CVCellMeasurement.Builder, - state: LinkPreviewState - ) -> CGSize { - - var maxLabelWidth = (maxWidth - ( - textStackConfig.layoutMargins.totalWidth + rootStackConfig.layoutMargins.totalWidth - )) - - var rootStackSubviewInfos = [ManualStackSubviewInfo]() - - let imageSize = LinkPreviewView.sentNonHeroImageSize - rootStackSubviewInfos.append(CGSize.square(imageSize).asManualSubviewInfo(hasFixedSize: true)) - maxLabelWidth -= imageSize + rootStackConfig.spacing - - maxLabelWidth = max(0, maxLabelWidth) - - var textStackSubviewInfos = [ManualStackSubviewInfo]() - if let labelConfig = sentTitleLabelConfig(state: state) { - let labelSize = CVText.measureLabel(config: labelConfig, maxWidth: maxLabelWidth) - textStackSubviewInfos.append(labelSize.asManualSubviewInfo) - } - if let labelConfig = sentDescriptionLabelConfig(state: state) { - let labelSize = CVText.measureLabel(config: labelConfig, maxWidth: maxLabelWidth) - textStackSubviewInfos.append(labelSize.asManualSubviewInfo) - } - - let textStackMeasurement = ManualStackView.measure( - config: textStackConfig, - measurementBuilder: measurementBuilder, - measurementKey: Self.measurementKey_textStack, - subviewInfos: textStackSubviewInfos - ) - rootStackSubviewInfos.append(textStackMeasurement.measuredSize.asManualSubviewInfo) - - let rootStackMeasurement = ManualStackView.measure( - config: rootStackConfig, - measurementBuilder: measurementBuilder, - measurementKey: Self.measurementKey_rootStack, - subviewInfos: rootStackSubviewInfos, - maxWidth: maxWidth - ) - return rootStackMeasurement.measuredSize - } -} - -public class CallLinkComponentFactory { - public static func callLinkIconView() -> UIView { - let circleView = CircleView() - circleView.backgroundColor = UIColor(rgbHex: Constants.iconBackgroundColor) - circleView.autoSetDimensions(to: CGSize(width: Constants.circleViewDimension, height: Constants.circleViewDimension)) - - let iconImageView = UIImageView(image: UIImage(named: "video-compact")) - iconImageView.tintColor = UIColor(rgbHex: Constants.iconTintColor) - iconImageView.autoSetDimensions(to: CGSize(width: Constants.iconDimension, height: Constants.iconDimension)) - circleView.addSubview(iconImageView) - iconImageView.autoCenterInSuperview() - - return circleView - } - - private enum Constants { - static let circleViewDimension: CGFloat = 64 - static let iconDimension: CGFloat = 36 - static let iconBackgroundColor: UInt32 = 0xE4E4FD - static let iconTintColor: UInt32 = 0x5151F6 - } -} - -// MARK: - - private class LinkPreviewViewAdapterSentHero: LinkPreviewViewAdapter { let state: LinkPreviewState