Skip to content

Commit

Permalink
fix: Masking for fast animations (#4574)
Browse files Browse the repository at this point in the history
Speeding up mask calculation
  • Loading branch information
brustolin authored Nov 28, 2024
1 parent b07998b commit ed64658
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- Use `options.reportAccessibilityIdentifier` for Breadcrumbs and UIEvents (#4569)
- Session replay transformed view masking (#4529)
- Load integration from same binary (#4541)
- Masking for fast animations #4574


### Improvements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,39 @@ class SRRedactSampleViewController: UIViewController {

@IBOutlet var label: UILabel!

private let animatedLabel: UILabel = {
let label = UILabel()
label.text = "Animated"
label.sizeToFit()
return label
}()

private var continueAnimating = true

override func viewDidLoad() {
super.viewDidLoad()

notRedactedView.backgroundColor = .green
notRedactedView.transform = CGAffineTransform(rotationAngle: 45 * .pi / 180.0)
SentrySDK.replay.unmaskView(notRedactedLabel)
}

animatedLabel.frame = CGRect(origin: .zero, size: animatedLabel.frame.size)
view.addSubview(animatedLabel)
animate()

}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
continueAnimating = false
}

private func animate() {
UIView.animate(withDuration: 0.3, delay: 0, options: [.repeat, .autoreverse], animations: { [weak self] in
guard let self = self else { return }
self.animatedLabel.frame = CGRect(
origin: CGPoint(x: 0, y: self.view.frame.height),
size: self.animatedLabel.frame.size)
})
}
}
2 changes: 1 addition & 1 deletion Sources/Swift/Tools/SentryViewPhotographer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ class SentryViewPhotographer: NSObject, SentryViewScreenshotProvider {
}

func image(view: UIView, options: SentryRedactOptions, onComplete: @escaping ScreenshotCallback ) {
let redact = redactBuilder.redactRegionsFor(view: view)
let image = renderer.render(view: view)

let redact = redactBuilder.redactRegionsFor(view: view)
let imageSize = view.bounds.size
dispatchQueue.dispatchAsync {
let screenshot = UIGraphicsImageRenderer(size: imageSize, format: .init(for: .init(displayScale: 1))).image { context in
Expand Down
14 changes: 6 additions & 8 deletions Sources/Swift/Tools/UIRedactBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ class UIRedactBuilder {
func redactRegionsFor(view: UIView) -> [RedactRegion] {
var redactingRegions = [RedactRegion]()

self.mapRedactRegion(fromView: view,
self.mapRedactRegion(fromLayer: view.layer.presentation() ?? view.layer,
relativeTo: nil,
redacting: &redactingRegions,
rootFrame: view.frame,
Expand Down Expand Up @@ -238,10 +238,8 @@ class UIRedactBuilder {
return image.imageAsset?.value(forKey: "_containingBundle") == nil
}

private func mapRedactRegion(fromView view: UIView, relativeTo parentLayer: CALayer?, redacting: inout [RedactRegion], rootFrame: CGRect, transform: CGAffineTransform, forceRedact: Bool = false) {
guard !redactClassesIdentifiers.isEmpty && !view.isHidden && view.alpha != 0 else { return }
let layer = view.layer.presentation() ?? view.layer
guard !redactClassesIdentifiers.isEmpty && !layer.isHidden && layer.opacity != 0 else { return }
private func mapRedactRegion(fromLayer layer: CALayer, relativeTo parentLayer: CALayer?, redacting: inout [RedactRegion], rootFrame: CGRect, transform: CGAffineTransform, forceRedact: Bool = false) {
guard !redactClassesIdentifiers.isEmpty && !layer.isHidden && layer.opacity != 0, let view = layer.delegate as? UIView else { return }

let newTransform = concatenateTranform(transform, from: layer, withParent: parentLayer)

Expand All @@ -265,15 +263,15 @@ class UIRedactBuilder {
}
}

guard view.subviews.count > 0 else { return }
guard let subLayers = layer.sublayers, subLayers.count > 0 else { return }

if view.clipsToBounds {
/// Because the order in which we process the redacted regions is reversed, we add the end of the clip region first.
/// The beginning will be added after all the subviews have been mapped.
redacting.append(RedactRegion(size: layer.bounds.size, transform: newTransform, type: .clipEnd))
}
for subview in view.subviews.sorted(by: { $0.layer.zPosition < $1.layer.zPosition }) {
mapRedactRegion(fromView: subview, relativeTo: layer, redacting: &redacting, rootFrame: rootFrame, transform: newTransform, forceRedact: enforceRedact)
for subLayer in subLayers.sorted(by: { $0.zPosition < $1.zPosition }) {
mapRedactRegion(fromLayer: subLayer, relativeTo: layer, redacting: &redacting, rootFrame: rootFrame, transform: newTransform, forceRedact: enforceRedact)
}
if view.clipsToBounds {
redacting.append(RedactRegion(size: layer.bounds.size, transform: newTransform, type: .clipBegin))
Expand Down

0 comments on commit ed64658

Please sign in to comment.