diff --git a/DatadogSessionReplay/SRSnapshotTests/SRHost/Fixtures/Images.storyboard b/DatadogSessionReplay/SRSnapshotTests/SRHost/Fixtures/Images.storyboard index b2a0b4052a..06a778a7b3 100644 --- a/DatadogSessionReplay/SRSnapshotTests/SRHost/Fixtures/Images.storyboard +++ b/DatadogSessionReplay/SRSnapshotTests/SRHost/Fixtures/Images.storyboard @@ -4,6 +4,7 @@ + @@ -25,15 +26,15 @@ - + - - + + - + @@ -183,7 +184,7 @@ - + @@ -198,7 +199,7 @@ - + @@ -210,18 +211,35 @@ - + + + @@ -229,16 +247,21 @@ + + + + + @@ -253,6 +276,7 @@ + @@ -268,6 +292,9 @@ + + + diff --git a/DatadogSessionReplay/SRSnapshotTests/SRHost/Fixtures/ImagesViewControllers.swift b/DatadogSessionReplay/SRSnapshotTests/SRHost/Fixtures/ImagesViewControllers.swift index 8ee2e0f98b..0db8e71456 100644 --- a/DatadogSessionReplay/SRSnapshotTests/SRHost/Fixtures/ImagesViewControllers.swift +++ b/DatadogSessionReplay/SRSnapshotTests/SRHost/Fixtures/ImagesViewControllers.swift @@ -7,10 +7,36 @@ import UIKit internal class ImagesViewController: UIViewController { + @IBOutlet weak var customButton: UIButton! @IBOutlet weak var customImageView: UIImageView! + @IBOutlet weak var tabBar: UITabBar! + @IBOutlet weak var navigationBar: UINavigationBar! override func viewDidLoad() { super.viewDidLoad() + + let color = UIColor(white: 0, alpha: 0.05) + customButton.setBackgroundImage(UIImage(color: color), for: .normal) + + tabBar.backgroundImage = UIImage(color: color) + tabBar.selectedItem = tabBar.items?.first + navigationBar.setBackgroundImage(UIImage(color: color), for: .default) + customImageView.image = UIImage(named: "dd_logo")?.withRenderingMode(.alwaysTemplate) } } + +fileprivate extension UIImage { + convenience init?(color: UIColor, size: CGSize = CGSize(width: 1.0, height: 1.0)) { + let rect = CGRect(origin: .zero, size: size) + UIGraphicsBeginImageContextWithOptions(rect.size, false, 1.0) + defer { UIGraphicsEndImageContext() } + + guard let context = UIGraphicsGetCurrentContext() else { return nil } + context.setFillColor(color.cgColor) + context.fill(rect) + + guard let cgImage = context.makeImage() else { return nil } + self.init(cgImage: cgImage) + } +} diff --git a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIImageViewRecorder.swift b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIImageViewRecorder.swift index 1e728f96fc..fdca087905 100644 --- a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIImageViewRecorder.swift +++ b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIImageViewRecorder.swift @@ -11,24 +11,20 @@ internal struct UIImageViewRecorder: NodeRecorder { private let shouldRecordImagePredicate: (UIImageView) -> Bool /// An option for overriding default semantics from parent recorder. var semanticsOverride: (UIImageView, ViewAttributes) -> NodeSemantics? = { imageView, _ in - let className = "\(type(of: imageView))" - // This gets effective on iOS 15.0+ which is the earliest version that displays some elements in popover views. - // Here we explicitly ignore the "shadow" effect applied to popover. - let isSystemShadow = className == "_UICutoutShadowView" - return isSystemShadow ? IgnoredElement(subtreeStrategy: .ignore) : nil + return imageView.isSystemShadow ? IgnoredElement(subtreeStrategy: .ignore) : nil } internal init( tintColorProvider: @escaping (UIImageView) -> UIColor? = { imageView in if #available(iOS 13.0, *), let image = imageView.image { - return image.isSymbolImage || image.isAlwaysTemplate ? imageView.tintColor : nil + return image.isTinted ? imageView.tintColor : nil } else { return nil } }, shouldRecordImagePredicate: @escaping (UIImageView) -> Bool = { imageView in if #available(iOS 13.0, *), let image = imageView.image { - return image.isSymbolImage || image.isBundled || image.isAlwaysTemplate + return image.isContextual || imageView.isSystemControlBackground } else { return false } @@ -144,7 +140,6 @@ internal struct UIImageViewWireframesBuilder: NodeWireframesBuilder { tintColor: tintColor ) } - if let contentFrame = contentFrame { wireframes.append( builder.createImageWireframe( @@ -160,11 +155,49 @@ internal struct UIImageViewWireframesBuilder: NodeWireframesBuilder { } fileprivate extension UIImage { - var isBundled: Bool { + @available(iOS 13.0, *) + var isContextual: Bool { + return isSymbolImage || isBundled || isAlwaysTemplate + } + + @available(iOS 13.0, *) + var isTinted: Bool { + return isSymbolImage || isAlwaysTemplate + } + + private var isBundled: Bool { return description.contains("named(") } - var isAlwaysTemplate: Bool { + private var isAlwaysTemplate: Bool { return renderingMode == .alwaysTemplate } } + +fileprivate extension UIImageView { + var isSystemControlBackground: Bool { + return isButtonBackground || isBarBackground + } + + var isSystemShadow: Bool { + let className = "\(type(of: self))" + // This gets effective on iOS 15.0+ which is the earliest version that displays some elements in popover views. + // Here we explicitly ignore the "shadow" effect applied to popover. + return className == "_UICutoutShadowView" + } + + var isButtonBackground: Bool { + if let button = superview as? UIButton, button.buttonType == .custom { + return button.backgroundImage(for: button.state) == image + } + return false + } + + var isBarBackground: Bool { + guard let superview = superview else { + return false + } + let superViewType = "\(type(of: superview))" + return superViewType == "_UIBarBackground" + } +}